PR comment (by ecosystem round-trip) #17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR comment (by ecosystem round-trip) | |
| # In-repo equivalent of the external bot upstream uses: react to the | |
| # `by ecosystem round-trip` run, download its `comment.md` artifact, and post | |
| # it as a sticky PR comment. Runs as a privileged `workflow_run` job so it can | |
| # comment on fork PRs (where the `pull_request` token is read-only). This | |
| # mirrors ruff's own (now bot-replaced) `ty-ecosystem-analyzer_comment.yaml`. | |
| # | |
| # astral plans to open source astral-sh-bot | |
| # (https://astral.sh/blog/open-source-security-at-astral). once it's available | |
| # and installed on this repo, this workflow can be removed in favour of the bot | |
| # consuming the `comment.md` artifact directly, same as upstream. | |
| permissions: {} | |
| on: # zizmor: ignore[dangerous-triggers] | |
| workflow_run: | |
| workflows: ["by ecosystem round-trip"] | |
| types: [completed] | |
| workflow_dispatch: | |
| inputs: | |
| workflow_run_id: | |
| description: The by ecosystem round-trip run whose comment to post | |
| required: true | |
| jobs: | |
| comment: | |
| name: Post round-trip comment | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.event == 'pull_request' | |
| permissions: | |
| pull-requests: write | |
| steps: | |
| - name: Download comment artifact | |
| id: download | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: comment.md | |
| run-id: ${{ github.event.workflow_run.id || github.event.inputs.workflow_run_id }} | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Post or update PR comment | |
| if: steps.download.outcome == 'success' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require("fs"); | |
| // the artifact comes from a (possibly fork) PR, so treat it as | |
| // untrusted: a symlinked comment.md could exfiltrate a runner | |
| // secret into a public comment. refuse anything but a plain file. | |
| for (const f of ["comment.md", "pr-number.txt"]) { | |
| if (fs.existsSync(f) && fs.lstatSync(f).isSymbolicLink()) { | |
| core.setFailed(`${f} must not be a symlink`); | |
| return; | |
| } | |
| } | |
| if (!fs.existsSync("comment.md") || !fs.existsSync("pr-number.txt")) { | |
| core.info("no comment artifact; nothing to post"); | |
| return; | |
| } | |
| const body = fs.readFileSync("comment.md", "utf8"); | |
| const prNumber = parseInt(fs.readFileSync("pr-number.txt", "utf8").trim(), 10); | |
| if (!Number.isInteger(prNumber)) { | |
| core.info("no PR number; nothing to post"); | |
| return; | |
| } | |
| const marker = "<!-- by-ecosystem-roundtrip -->"; | |
| const { owner, repo } = context.repo; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| per_page: 100, | |
| }); | |
| const existing = comments.find( | |
| (c) => c.body && c.body.includes(marker), | |
| ); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner, | |
| repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner, | |
| repo, | |
| issue_number: prNumber, | |
| body, | |
| }); | |
| } |