77 lines
2.6 KiB
JavaScript
77 lines
2.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* assert-no-pnpm-version-pin.mjs — CI regression guard (followup: fix pnpm version conflict)
|
|
*
|
|
* Ensures no .github/workflows/*.yml file pins pnpm via a `version:` key
|
|
* under a `pnpm/action-setup` step. The canonical version source is
|
|
* `package.json#packageManager`, which carries an exact version + integrity
|
|
* hash. Duplicating the version in the workflow creates a conflict that
|
|
* pnpm/action-setup@v4 treats as an error.
|
|
*
|
|
* Usage:
|
|
* node scripts/lib/assert-no-pnpm-version-pin.mjs
|
|
* pnpm run verify:ci
|
|
*
|
|
* Exit codes:
|
|
* 0 — no version pin found
|
|
* 1 — one or more violations found (details on stderr)
|
|
*/
|
|
|
|
import { readFileSync, readdirSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const REPO_ROOT = path.resolve(__dirname, "../..");
|
|
const WORKFLOWS_DIR = path.join(REPO_ROOT, ".github", "workflows");
|
|
|
|
let violations = 0;
|
|
|
|
// Read workflow files — silently pass if directory doesn't exist
|
|
let files;
|
|
try {
|
|
files = readdirSync(WORKFLOWS_DIR).filter(
|
|
(f) => f.endsWith(".yml") || f.endsWith(".yaml")
|
|
);
|
|
} catch {
|
|
process.stdout.write("OK: no .github/workflows directory found; nothing to check.\n");
|
|
process.exit(0);
|
|
}
|
|
|
|
for (const file of files) {
|
|
const fullPath = path.join(WORKFLOWS_DIR, file);
|
|
const content = readFileSync(fullPath, "utf8");
|
|
const lines = content.split("\n");
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
// Locate a step that uses pnpm/action-setup
|
|
if (!lines[i].includes("pnpm/action-setup")) continue;
|
|
|
|
// Look ahead up to 10 lines for a `version:` key in the same step
|
|
const end = Math.min(i + 10, lines.length);
|
|
for (let j = i + 1; j < end; j++) {
|
|
const ahead = lines[j];
|
|
// A new step begins at a `- name:` or `- uses:` list item → stop
|
|
if (/^\s*-\s+(name|uses)\s*:/.test(ahead)) break;
|
|
// `version:` key found inside this step → violation
|
|
if (/^\s+version\s*:/.test(ahead)) {
|
|
process.stderr.write(
|
|
`ERROR: ${file}:${j + 1}: 'version:' key found under pnpm/action-setup step.\n` +
|
|
` Remove 'with.version'; let package.json#packageManager be the single\n` +
|
|
` source of truth for the pnpm version (exact version + integrity hash).\n\n`
|
|
);
|
|
violations++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (violations > 0) {
|
|
process.stderr.write(`${violations} violation(s) found.\n`);
|
|
process.exit(1);
|
|
}
|
|
|
|
process.stdout.write("OK: no pnpm version pins found in workflow files.\n");
|
|
process.exit(0);
|