diff --git a/.github/workflows/schema_diff.yml b/.github/workflows/schema_diff.yml index f0d148b071..eaa25b53fa 100644 --- a/.github/workflows/schema_diff.yml +++ b/.github/workflows/schema_diff.yml @@ -1,4 +1,4 @@ -name: Schema Diff +name: Schema Diff # If this is changed, need to update `schema_diff_comment.yml` as well on: pull_request: @@ -95,9 +95,34 @@ jobs: echo "⚠️ Schema diff generation failed. See job logs for details." \ > "${{ runner.temp }}/schema_diff.md" + # Post a comment. + # + # For same-repo PRs, we can do this directly and we are done. - name: Post sticky PR comment uses: marocchino/sticky-pull-request-comment@3d7b8546315c63df45a03981d50a43ec19237f80 # v3 - if: always() + # Only run for same-repo PRs + if: always() && github.event.pull_request.head.repo.full_name == github.repository with: header: schema-diff path: ${{ runner.temp }}/schema_diff.md + + # For PRs from forks, we only have a read-only `GITHUB_TOKEN` here and need to work around + # by triggering a `workflow_run` onto the main repo. + # + # We upload an artifact and the `schema_diff_comment.yml` workflow will be triggered + # on the main repo by this workflow exiting. + - name: Save PR number for forked PRs + # Only run for fork PRs + if: always() && github.event.pull_request.head.repo.full_name != github.repository + run: echo "${{ github.event.pull_request.number }}" > "${{ runner.temp }}/pr_number" + + - name: Upload schema diff artifact for forked PRs + # Only run for fork PRs + if: always() && github.event.pull_request.head.repo.full_name != github.repository + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: schema-diff + path: | + ${{ runner.temp }}/schema_diff.md + ${{ runner.temp }}/pr_number + retention-days: 1 diff --git a/.github/workflows/schema_diff_comment.yml b/.github/workflows/schema_diff_comment.yml new file mode 100644 index 0000000000..4aefc3a123 --- /dev/null +++ b/.github/workflows/schema_diff_comment.yml @@ -0,0 +1,71 @@ +name: Post schema diff comment for forked PRs + +# The "Schema Diff" workflow can't post a PR comment for PRs from forks, +# since GITHUB_TOKEN is downgraded to read-only in that context. Instead it +# uploads the diff (and PR number) as an artifact, which we pick up here and +# post as a comment. +# +# This workflow runs in the context of the main repository, so its +# `GITHUB_TOKEN` can have write access. +# The downloaded artifact comes from an untrusted fork though, so it must only +# ever be treated as inert comment text, never executed. +# The PR number it carries is likewise untrusted: we cross-check it against the trusted +# `workflow_run` event before posting, so a fork PR can't point the comment +# at some other PR or issue. +on: + workflow_run: + workflows: ["Schema Diff"] + types: [completed] + +# DANGER: Due to untrusted input and untrusted triggers, +# think very carefully before expanding the permissions +permissions: + # To read the artifacts from the `schema_diff.yml` run + actions: read + # To post a comment to the PR + pull-requests: write + +jobs: + comment: + name: Post schema diff comment + runs-on: ubuntu-latest + # Only run when triggered by a pull request and only on a PR from a fork + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.head_repository.full_name != github.repository && github.event.workflow_run.conclusion != 'cancelled' + steps: + - name: Download schema diff artifact + id: download + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: schema-diff + path: schema-diff + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + # The artifact (including the PR number) comes from an untrusted fork. + # Cross-check the number against the trusted workflow_run event before + # posting, so a fork PR cannot target another PR or issue. + - name: Verify PR matches the run that produced the artifact + id: verify + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # No checkout in this job, so tell gh which repo to talk to. + GH_REPO: ${{ github.repository }} + EXPECTED_BRANCH: ${{ github.event.workflow_run.head_branch }} + EXPECTED_HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }} + run: | + pr_number="$(cat schema-diff/pr_number)" + read -r pr_branch pr_head_repo < <( + gh api "repos/{owner}/{repo}/pulls/$pr_number" --jq '.head.ref + " " + .head.repo.full_name' + ) + if [ "$pr_branch" != "$EXPECTED_BRANCH" ] || [ "$pr_head_repo" != "$EXPECTED_HEAD_REPO" ]; then + echo "::error::Artifact PR $pr_number does not match the triggering run; refusing to post a comment" + exit 1 + fi + echo "number=$pr_number" >> "$GITHUB_OUTPUT" + + - name: Post sticky PR comment + uses: marocchino/sticky-pull-request-comment@3d7b8546315c63df45a03981d50a43ec19237f80 # v3 + with: + header: schema-diff + number_force: ${{ steps.verify.outputs.number }} + path: schema-diff/schema_diff.md diff --git a/changelog.d/20207.misc b/changelog.d/20207.misc new file mode 100644 index 0000000000..3709ad46f1 --- /dev/null +++ b/changelog.d/20207.misc @@ -0,0 +1 @@ +Fix the `Schema Diff` CI check failing to post a comment on pull requests from forks.