diff --git a/.github/workflows/portability-report.yml b/.github/workflows/portability-report.yml index 0a56e6078..f5204865f 100644 --- a/.github/workflows/portability-report.yml +++ b/.github/workflows/portability-report.yml @@ -3,7 +3,9 @@ name: Portability report # Lists lines a pull request adds that contain values tied to one machine, network or account # (credentials, home paths, temporary paths, network addresses, email addresses). Findings never # fail the job; the author reads the report and decides what to change. An operational error (for -# example a git failure) does fail it. See scripts/portability-report.mjs. +# example a git failure) does fail it. The repository is public, so this summary lists file and +# line only; authors see the matched text when they run the report locally or on push. +# See scripts/portability-report.mjs. on: pull_request: @@ -27,4 +29,5 @@ jobs: node scripts/portability-report.mjs --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} + --locations-only --out "$GITHUB_STEP_SUMMARY" diff --git a/scripts/portability-report.mjs b/scripts/portability-report.mjs index 3e3d632e4..2ef7c0847 100644 --- a/scripts/portability-report.mjs +++ b/scripts/portability-report.mjs @@ -16,6 +16,7 @@ // node scripts/portability-report.mjs --from A --to B # a commit range // node scripts/portability-report.mjs --staged # what is staged for the next commit // add --out report.md to also write the report to a file +// add --locations-only to list file and line without quoting the matched text (for public CI output) import { execFileSync } from "node:child_process"; import { writeFileSync } from "node:fs"; @@ -79,7 +80,7 @@ export function findPortabilityIssues(lines, checks = CHECKS) { return findings; } -export function renderReport(findings, label) { +export function renderReport(findings, label, { locationsOnly = false } = {}) { const out = [`# Portability report: ${label}`, ""]; if (findings.length === 0) { out.push("No matching machine-specific values detected."); @@ -105,6 +106,10 @@ export function renderReport(findings, label) { if (group.length === 0) continue; out.push(`## ${check.category} (${group.length})`, "", `_${check.why}_`, ""); for (const { file, line, match, text } of group) { + if (locationsOnly) { + out.push(`- \`${file}:${line}\``); + continue; + } if (check.category === "Credential") { out.push(`- \`${file}:${line}\` matched \`${match.slice(0, 6)}…\` (value withheld)`); continue; @@ -120,10 +125,11 @@ export function renderReport(findings, label) { } function parseArguments(argv) { - const options = { staged: false }; + const options = { staged: false, locationsOnly: false }; for (let index = 0; index < argv.length; index += 1) { const key = argv[index]; if (key === "--staged") options.staged = true; + else if (key === "--locations-only") options.locationsOnly = true; else if (["--from", "--to", "--out", "--repo"].includes(key)) options[key.slice(2)] = argv[++index]; else throw new Error(`Unknown argument: ${key}`); } @@ -149,7 +155,9 @@ export function main(argv = process.argv.slice(2)) { label = `${from.slice(0, 8)}..${to.length > 12 ? to.slice(0, 8) : to}`; } const diff = git(repo, ["diff", "-U0", "--no-color", "--diff-filter=ACMR", ...range]); - const report = renderReport(findPortabilityIssues(addedLines(diff)), label); + const report = renderReport(findPortabilityIssues(addedLines(diff)), label, { + locationsOnly: options.locationsOnly, + }); process.stdout.write(report); if (options.out) writeFileSync(options.out, report); return 0; diff --git a/scripts/portability-report.test.mjs b/scripts/portability-report.test.mjs index 296dbd81e..cf55a86e5 100644 --- a/scripts/portability-report.test.mjs +++ b/scripts/portability-report.test.mjs @@ -82,6 +82,14 @@ test("a credential on the same line as another finding is withheld from every ca assert.match(report, /100\.95\.1\.2/); }); +test("locations-only output names file and line without quoting the matched text", () => { + const findings = findPortabilityIssues(addedLines(diff)); + const report = renderReport(findings, "x..y", { locationsOnly: true }); + assert.match(report, /## Home path \(1\)/); + assert.match(report, /- `docs\/a\.md:10`/); + assert.ok(!report.includes("/Users/alice/"), report); +}); + test("the report says so when nothing is found and groups findings when something is", () => { assert.match(renderReport([], "x..y"), /No matching machine-specific values detected/); const report = renderReport(findPortabilityIssues(addedLines(diff)), "x..y");