Lintcheck summary #132
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 | |
# | |
# 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@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 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 }} |