chore(docs linter): fix external prs workflow (#35785)

The external PRs workflow is still broken: commenting on the PR with the
lint results doesn't work because pull_request events triggered from
forks do not have write permissions.

Following GitHub recommendations, I broke it down into two workflows:

1. First workflow writes the lint results (if any) to an uploaded
artifact.
2. Second workflow (triggered by workflow_run and thus has write
permissions) downloads the artifact and posts the results as a PR
comment.
This commit is contained in:
Charis authored and GitHub committed 2025-05-27 10:10:04 -04:00
1 parent 22ae9a2caf
commit 5dc929a584
2 files changed
+94 -4

No files matched your search

@@ -0,0 +1,67 @@
name: Comment on external PRs with linter results
# This is a continuation of ./docs-lint-v2.yml, to write comments on external
# PRs.
#
# SECURITY:
# This workflow runs with write permissions, in the context of code from an
# external PR. This is safe because no external code is executed. The
# stringified Markdown output from the linter (downloaded as an artifact) is
# directly written as the body of a PR comment.
on:
workflow_run:
workflows: ['[Docs] Lint v2']
types:
- completed
permissions:
pull-requests: write
jobs:
comment_on_pr:
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
steps:
- id: download_artifact
name: 'Download artifact'
uses: actions/github-script@v7
with:
script: |
const artifacts = await github.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }}
});
const matchingArtifact = artifacts?.data?.artifacts?.find(
(artifact) => artifact.name == 'lint_results'
);
if (matchingArtifact) {
core.setOutput('contains_results', 'true')
const download = await github.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchingArtifact.id,
archive_format: 'zip',
});
const fs = require('fs');
fs.writeFileSync('${{ github.workspace }}/lint_results.zip', Buffer.from(download.data));
}
- id: unzip_results
name: Unzip results file
if: steps.download_artifact.outputs.contains_results == 'true'
run: unzip lint_results.zip
- name: 'Comment on PR'
if: steps.download_artifact.outputs.contains_results == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const prNumber = Number(fs.readFileSync('./pr_number.txt'));
const lintResults = fs.readFileSync('./lint_results.txt', 'utf8');
await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: prNumber,
body: lintResults
});
+27 -4
View File
@@ -1,4 +1,19 @@
name: '[Docs] Lint v2'
# Runs the docs linter on PRs that edit docs content.
# There are two branches of this workflow for internal and external PRs, due
# to the security design of GitHub Actions.
#
# Internal PRs:
# Have write permissions, so comments are written directly by reviewdog.
#
# External PRs:
# Have read-only permissions, so lint results are uploaded as an artifact, to
# be written to the PR in a subsequent workflow_run action that has write
# permissions. See ./docs/lint-v2-comment.yml.
#
# See https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/
on:
pull_request:
@@ -58,11 +73,11 @@ jobs:
| { grep -E "^apps/docs/content/" || test $? = 1; } \
| xargs -r supa-mdx-lint --format rdf \
| reviewdog -f=rdjsonl -reporter=github-pr-review -tee
- name: run linter (external)
- id: external_lint
name: run linter (external)
if: steps.filter.outputs.docs == 'true' && github.event.pull_request.head.repo.full_name != github.repository
env:
BASE_REF: ${{ github.base_ref }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -o pipefail
@@ -75,7 +90,15 @@ jobs:
LINT_RESULTS=$(run_lints)
LINT_EXIT_CODE=$?
set -e
echo "LINT_EXIT_CODE=$LINT_EXIT_CODE" >> $GITHUB_OUTPUT
if [[ $LINT_EXIT_CODE -ne 0 ]]; then
gh pr comment "$PR_NUMBER" --body "$LINT_RESULTS"
exit 1
mkdir -p ./__github_actions__pr
echo "${{ github.event.number }}" > ./__github_actions__pr/pr_number.txt
echo "$LINT_RESULTS" > ./__github_actions__pr/lint_results.txt
fi
- name: save results as artifact (external)
if: steps.filter.outputs.docs == 'true' && github.event.pull_request.head.repo.full_name != github.repository && steps.external_lint.outputs.LINT_EXIT_CODE != 0
uses: actions/upload-artifact@v4
with:
name: lint_results
path: __github_actions__pr/