From 365f6eefe3b2e36809274c765bf615bad0e53d3b Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Tue, 5 May 2026 11:20:27 +0200 Subject: [PATCH] Check JSON response for knip to avoid false positives --- .github/workflows/tests-pr.yml | 2 +- bin/run-knip-ci.js | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 bin/run-knip-ci.js diff --git a/.github/workflows/tests-pr.yml b/.github/workflows/tests-pr.yml index 372cfd5549f..1f4ca53f9c0 100644 --- a/.github/workflows/tests-pr.yml +++ b/.github/workflows/tests-pr.yml @@ -87,7 +87,7 @@ jobs: with: node-version: ${{ env.DEFAULT_NODE_VERSION }} - name: Run knip - run: pnpm knip + run: node bin/run-knip-ci.js graphql-schema: if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }} diff --git a/bin/run-knip-ci.js b/bin/run-knip-ci.js new file mode 100644 index 00000000000..c59a4629850 --- /dev/null +++ b/bin/run-knip-ci.js @@ -0,0 +1,41 @@ +import {spawnSync} from 'node:child_process' + +// Run knip with the JSON reporter so a successful run still emits structured +// output (e.g. {"files":[],"issues":[]}). An empty stdout therefore indicates +// knip did not actually analyze the codebase — a silent-pass mode previously +// observed — and must be treated as a failure to avoid false-green builds. +const knip = spawnSync('pnpm', ['--silent', 'knip', '--reporter', 'json'], { + encoding: 'utf8', +}) + +if (knip.error) { + console.error(`::error::failed to spawn knip: ${knip.error.message}`) + process.exit(1) +} + +let report +try { + report = JSON.parse(knip.stdout) +} catch { + console.error( + `::error::knip exited ${knip.status} but produced no parseable JSON output. Failing CI to avoid a false green.`, + ) + console.error('Raw stdout:') + console.error(knip.stdout) + console.error('Raw stderr:') + console.error(knip.stderr) + process.exit(1) +} + +if (!Array.isArray(report.files) || !Array.isArray(report.issues)) { + console.error('::error::knip output had unexpected shape') + console.error(JSON.stringify(report)) + process.exit(1) +} + +if (knip.status !== 0) { + // Findings: re-run with the default reporter so the log is human-readable. + spawnSync('pnpm', ['knip'], {stdio: 'inherit'}) +} + +process.exit(knip.status ?? 1)