Lintcheck summary #5
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: 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 | |
# | |
# This action must not checkout/run code from the originating workflow_run | |
# or interpolate ${{}} untrusted fields into a shell context | |
# | |
# 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: | |
# actions: read | |
pull-requests: write | |
jobs: | |
download: | |
runs-on: ubuntu-latest | |
if: ${{ github.event.workflow_run.conclusion == 'success' }} | |
steps: | |
- name: Download artifact | |
uses: actions/download-artifact@v4 | |
with: | |
name: summary | |
path: untrusted | |
run-id: ${{ github.event.workflow_run.id }} | |
# github-token: ${{ github.token }} | |
- name: Format comment | |
uses: actions/github-script@v7 | |
with: | |
script: | | |
const fs = require("fs"); | |
const assert = require("assert/strict"); | |
function validateName(s) { | |
assert.match(s, /^[a-z0-9_:]+$/); | |
return s; | |
} | |
function validateURL(s) { | |
// e.g. https://github.com/rust-lang/rust-clippy/actions/runs/14983452753#user-content-needless-return | |
const start = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}`; | |
assert.ok(s.startsWith(start)); | |
assert.match(s.slice(start.length), /^#user-content-[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; | |
} | |
} | |
console.log(context.payload.workflow_run); | |
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 url = validateURL(untrustedRow.url); | |
const added = validateInt(untrustedRow.added); | |
const removed = validateInt(untrustedRow.removed); | |
const changed = validateInt(untrustedRow.changed); | |
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 | |
# If we already posted a comment and there are now no changes update it to say that | |
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 }} |