| name: Lintcheck summary | |
| # The workflow_run event runs in the context of the Clippy repo giving it write | |
| # access, needed here to create a PR comment when the PR originates from a fork | |
| # | |
| # The summary artifact is a JSON file that we verify in this action to prevent | |
| # the creation of arbitrary comments | |
| # | |
| # This action must not checkout/run code from the originating workflow_run | |
| # or directly interpolate ${{}} untrusted fields into code | |
| # | |
| # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run | |
| # https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections | |
| on: | |
| workflow_run: | |
| workflows: [Lintcheck] | |
| types: [completed] | |
| # Restrict the default permission scope https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes | |
| permissions: | |
| pull-requests: write | |
| jobs: | |
| download: | |
| runs-on: ubuntu-latest | |
| if: ${{ github.event.workflow_run.conclusion == 'success' }} | |
| steps: | |
| - name: Download artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: summary | |
| path: untrusted | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Format comment | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const fs = require("fs"); | |
| const assert = require("assert/strict"); | |
| function validateName(s) { | |
| assert.match(s, /^[a-z0-9_:]+$/); | |
| return s; | |
| } | |
| function validateInt(i) { | |
| assert.ok(Number.isInteger(i)); | |
| return i; | |
| } | |
| function tryReadSummary() { | |
| try { | |
| return JSON.parse(fs.readFileSync("untrusted/summary.json")); | |
| } catch { | |
| return null; | |
| } | |
| } | |
| const prNumber = parseInt(fs.readFileSync("untrusted/pr.txt"), 10); | |
| core.exportVariable("PR", prNumber.toString()); | |
| const untrustedSummary = tryReadSummary(); | |
| if (!Array.isArray(untrustedSummary)) { | |
| return; | |
| } | |
| let summary = `Lintcheck changes for ${context.payload.workflow_run.head_sha} | |
| | Lint | Added | Removed | Changed | | |
| | ---- | ----: | ------: | ------: | | |
| `; | |
| for (const untrustedRow of untrustedSummary) { | |
| const name = validateName(untrustedRow.name); | |
| const added = validateInt(untrustedRow.added); | |
| const removed = validateInt(untrustedRow.removed); | |
| const changed = validateInt(untrustedRow.changed); | |
| const id = name.replace("clippy::", "user-content-").replaceAll("_", "-"); | |
| const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}#${id}`; | |
| summary += `| [\`${name}\`](${url}) | ${added} | ${removed} | ${changed} |\n`; | |
| } | |
| summary += "\nThis comment will be updated if you push new changes"; | |
| fs.writeFileSync("summary.md", summary); | |
| - name: Create/update comment | |
| run: | | |
| if [[ -f summary.md ]]; then | |
| gh pr comment "$PR" --body-file summary.md --edit-last --create-if-none | |
| else | |
| # There were no changes detected by Lintcheck | |
| # - If a comment exists from a previous run that did detect changes, edit it (--edit-last) | |
| # - If no comment exists do not create one, the `gh` command exits with an error which | |
| # `|| true` ignores | |
| gh pr comment "$PR" --body "No changes for ${{ github.event.workflow_run.head_sha }}" --edit-last || true | |
| fi | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} |