Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,20 @@ jobs:
if: github.event_name != 'workflow_dispatch' || inputs.dep-suggests-matrix
uses: ./.github/workflows/dep-suggests-matrix

- id: repo-state
name: Determine repository state
uses: ./.github/workflows/repo-state

# Styling on foreign PRs works through the format-suggest workflow
- uses: ./.github/workflows/style
if: steps.repo-state.outputs.foreign != 'true' || steps.repo-state.outputs.is_pr != 'true'

# Snapshot tests can't work in a way similar to format-suggests
# because it requires running code which is a security risk on pull_request_target workflows
- uses: ./.github/workflows/update-snapshots
with:
base: ${{ inputs.ref || github.head_ref }}

- uses: ./.github/workflows/style

- uses: ./.github/workflows/roxygenize

- name: Remove config files from previous iteration
Expand Down Expand Up @@ -175,7 +183,7 @@ jobs:
echo -n "${{ steps.commit.outputs.sha }}" > rcc-smoke-sha.txt
shell: bash

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
with:
name: rcc-smoke-sha
path: rcc-smoke-sha.txt
Expand Down
100 changes: 100 additions & 0 deletions .github/workflows/commit-suggest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: commit-suggest.yaml

on:
workflow_run:
workflows: ["rcc"]
types:
- completed

permissions:
contents: write
pull-requests: write

jobs:
commit-suggest:
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request'

steps:
- name: Show event payload
run: |
echo '${{ toJson(github.event) }}' | jq .
shell: bash

- name: Checkout PR
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}

- name: Download artifact
uses: actions/download-artifact@v6
with:
name: changes-patch
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
continue-on-error: true

- name: Check if artifact exists
id: check-artifact
run: |
if [ -f changes.patch ]; then
echo "has_diff=true" >> $GITHUB_OUTPUT
else
echo "has_diff=false" >> $GITHUB_OUTPUT
echo "No changes-patch artifact found"
fi
shell: bash

- name: Find PR number for branch from correct head repository
id: find-pr
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
PR_NUMBER=$(gh pr list --head ${{ github.event.workflow_run.head_branch }} --state open --json number,headRepositoryOwner --jq '.[] | select(.headRepositoryOwner.login == "${{ github.event.workflow_run.head_repository.owner.login }}") | .number' || echo "")
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
shell: bash

- name: Generate comment body
if: steps.check-artifact.outputs.has_diff == 'true'
id: comment-body
run: |
cat << 'EOF' > comment.md
## Formatting suggestions available

A patch file with formatting suggestions has been generated. You can apply it using one of these methods:

### Method 1: Apply via gh CLI

```bash
# Download and apply the patch directly
gh run download ${{ github.event.workflow_run.id }} --repo ${{ github.repository }} --name changes-patch && patch -p1 < changes.patch && rm changes.patch
```

### Method 2: View the patch

<details>
<summary>Click to see the patch contents</summary>

```diff
EOF

cat changes.patch >> comment.md

cat << 'EOF' >> comment.md
```

</details>

---
*This comment was automatically generated by the commit-suggester workflow.*
EOF
shell: bash

- name: Post or update comment
if: steps.check-artifact.outputs.has_diff == 'true'
uses: thollander/actions-comment-pull-request@v3
with:
pr-number: ${{ steps.find-pr.outputs.pr_number }}
file-path: comment.md
comment-tag: formatting-suggestions
mode: recreate
158 changes: 107 additions & 51 deletions .github/workflows/commit/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,65 +22,121 @@ runs:
fi
shell: bash

- name: Commit if changed, create a PR if protected
id: commit
- name: Determine repository state
if: steps.check.outputs.has_changes == 'true'
id: repo-state
uses: ./.github/workflows/repo-state

- name: Commit and create PR on protected branch
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'false' && steps.repo-state.outputs.protected == 'true'
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
set -x
protected=${{ github.ref_protected }}
foreign=${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
is_pr=${{ github.event_name == 'pull_request' }}
if [ "${is_pr}" = "true" ] && [ "${foreign}" = "true" ]; then
# Running on a PR - will use reviewdog in next step
echo "Code changes detected on PR, will suggest changes via reviewdog"
echo "use_reviewdog=true" | tee -a $GITHUB_OUTPUT
git reset HEAD
git status
elif [ "${foreign}" = "true" ]; then
# https://github.com/krlmlr/actions-sync/issues/44
echo "Can't push to foreign branch"
elif [ "${protected}" = "true" ]; then
current_branch=$(git branch --show-current)
new_branch=gha-commit-$(git rev-parse --short HEAD)
git checkout -b ${new_branch}
git add .
git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
# Force-push, used in only one place
# Alternative: separate branch names for each usage
git push -u origin HEAD -f

existing_pr=$(gh pr list --state open --base main --head ${new_branch} --json number --jq '.[] | .number')
if [ -n "${existing_pr}" ]; then
echo "Existing PR: ${existing_pr}"
else
gh pr create --base main --head ${new_branch} --title "chore: Auto-update from GitHub Actions" --body "Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
fi

gh workflow run rcc -f ref=$(git rev-parse HEAD)
gh pr merge --merge --auto
current_branch=$(git branch --show-current)
new_branch=gha-commit-$(git rev-parse --short HEAD)
git checkout -b ${new_branch}
git add .
git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
# Force-push, used in only one place
# Alternative: separate branch names for each usage
git push -u origin HEAD -f

existing_pr=$(gh pr list --state open --base main --head ${new_branch} --json number --jq '.[] | .number')
if [ -n "${existing_pr}" ]; then
echo "Existing PR: ${existing_pr}"
else
git fetch
if [ -n "${GITHUB_HEAD_REF}" ]; then
git add .
git stash save
git switch ${GITHUB_HEAD_REF}
git merge origin/${GITHUB_BASE_REF} --no-edit
git stash pop
fi
git add .
git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
git push -u origin HEAD
gh pr create --base main --head ${new_branch} --title "chore: Auto-update from GitHub Actions" --body "Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
fi

gh workflow run rcc -f ref=$(git rev-parse HEAD)
gh pr merge --merge --auto
shell: bash

# Only set output if changed
echo sha=$(git rev-parse HEAD) >> $GITHUB_OUTPUT
- name: Commit and push on unprotected branch
id: commit
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'false' && steps.repo-state.outputs.protected == 'false'
env:
GITHUB_TOKEN: ${{ inputs.token }}
run: |
set -x
git fetch
if [ -n "${GITHUB_HEAD_REF}" ]; then
git add .
git stash save
git switch ${GITHUB_HEAD_REF}
git merge origin/${GITHUB_BASE_REF} --no-edit
git stash pop
fi
git add .
git commit -m "chore: Auto-update from GitHub Actions"$'\n'$'\n'"Run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
git push -u origin HEAD

# Only set output if changed
echo sha=$(git rev-parse HEAD) >> $GITHUB_OUTPUT
shell: bash

- name: Create patch file for foreign branch
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'true'
run: |
set -x
git diff > changes.patch
echo "Patch file created with uncommitted changes"
cat changes.patch
shell: bash

- name: Suggest changes via reviewdog
if: steps.commit.outputs.use_reviewdog == 'true'
uses: krlmlr/action-suggester@main
- name: Upload patch artifact
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'true'
uses: actions/upload-artifact@v5
with:
github_token: ${{ inputs.token }}
tool_name: "rcc"
name: changes-patch
path: changes.patch

- name: Add patch summary on foreign branch
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'true'
run: |
cat << 'EOF' >> $GITHUB_STEP_SUMMARY
## Formatting suggestions available

A patch file with formatting suggestions has been generated. Since this PR is from a forked repository, the changes cannot be pushed automatically.

You can apply the patch using one of these methods:

### Method 1: Apply via gh CLI

```bash
# Download and apply the patch directly
gh run download ${{ github.run_id }} --repo ${{ github.repository }} --name changes-patch && git apply changes.patch && rm changes.patch
```

### Method 2: Download from workflow artifacts

1. Download the `changes-patch` artifact from this workflow run
2. Extract and apply it:
```bash
git apply changes.patch
```

### Method 3: View the patch

<details>
<summary>Click to see the patch contents</summary>

```diff
EOF

cat changes.patch >> $GITHUB_STEP_SUMMARY

cat << 'EOF' >> $GITHUB_STEP_SUMMARY
```

</details>
EOF
shell: bash

- name: Fail on foreign branch
if: steps.check.outputs.has_changes == 'true' && steps.repo-state.outputs.foreign == 'true'
run: |
echo "Exiting with failure due to foreign branch. Please apply the patch suggested in the action or PR comment."
exit 1
shell: bash
2 changes: 1 addition & 1 deletion .github/workflows/covr/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ runs:

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: coverage-test-failures
path: ${{ runner.temp }}/package
45 changes: 45 additions & 0 deletions .github/workflows/format-suggest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Workflow derived from https://github.com/posit-dev/setup-air/tree/main/examples

on:
# Using `pull_request_target` over `pull_request` for elevated `GITHUB_TOKEN`
# privileges, otherwise we can't set `pull-requests: write` when the pull
# request comes from a fork, which is our main use case (external contributors).
#
# `pull_request_target` runs in the context of the target branch (`main`, usually),
# rather than in the context of the pull request like `pull_request` does. Due
# to this, we must explicitly checkout `ref: ${{ github.event.pull_request.head.sha }}`.
# This is typically frowned upon by GitHub, as it exposes you to potentially running
# untrusted code in a context where you have elevated privileges, but they explicitly
# call out the use case of reformatting and committing back / commenting on the PR
# as a situation that should be safe (because we aren't actually running the untrusted
# code, we are just treating it as passive data).
# https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/
pull_request_target:

name: format-suggest.yaml

jobs:
format-suggest:
name: format-suggest
runs-on: ubuntu-latest
# Only run this job if changes come from a fork.
# We commit changes directly on the main repository.
if: github.event.pull_request.head.repo.full_name != github.repository

permissions:
# Required to push suggestion comments to the PR
pull-requests: write

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}

- uses: ./.github/workflows/style

- name: Suggest
uses: reviewdog/action-suggester@v1
with:
level: error
fail_level: error
tool_name: air-and-clang-format
28 changes: 28 additions & 0 deletions .github/workflows/repo-state/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "Determine repository state"
description: "Expose protected, foreign, and is_pr context variables"
outputs:
protected:
description: "Whether the current branch is protected"
value: ${{ steps.state.outputs.protected }}
foreign:
description: "Whether the PR is from a foreign repository"
value: ${{ steps.state.outputs.foreign }}
is_pr:
description: "Whether the event is a pull request"
value: ${{ steps.state.outputs.is_pr }}

runs:
using: "composite"
steps:
- name: Determine repository state
id: state
run: |
set -x
protected=${{ github.ref_protected }}
foreign=${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }}
is_pr=${{ github.event_name == 'pull_request' }}

echo "protected=${protected}" | tee -a $GITHUB_OUTPUT
echo "foreign=${foreign}" | tee -a $GITHUB_OUTPUT
echo "is_pr=${is_pr}" | tee -a $GITHUB_OUTPUT
shell: bash
5 changes: 5 additions & 0 deletions .github/workflows/update-snapshots/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,8 @@ runs:
run: |
false
shell: bash

- name: Reset Git changes
run: |
git reset -- tests/testthat/_snaps
shell: bash