#!/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);