Files
software-mansion__argent/scripts/check-workspace-versions.mjs
Ignacy Łątka dc0930d50c ci: prevent package-lock drift + add repo-hygiene checks (#362)
## Why

We hit a mismatch between a locally-generated `package-lock.json` and
the one on `main`. `npm ci` validates the lockfile loosely and accepts a
stale lock that a fresh `npm install` would rewrite, so the drift
slipped through. This adds CI to catch that, plus a few related
common-mistake guards.

## What

**`Lockfile` workflow** (Node 20) — runs `npm install
--package-lock-only --ignore-scripts` and fails if `package-lock.json`
changes. This is the gap `npm ci` leaves: it asserts the lock is exactly
what npm produces, not merely "consistent enough".

**`Repo Hygiene` workflow** — three fast static guards:
- **Merge-conflict markers** — fail on any `<<<<<<<` / `=======` /
`>>>>>>>` left in a tracked file.
- **Focused tests** — fail on `describe/it/test/bench.only` in
`*.test.ts(x)` / `*.spec.ts(x)`. Uses a POSIX-ERE word-boundary pattern
so it actually fires under `git grep -E` (a `\b` pattern silently
matches nothing there).
- **Workspace version sync** — fail when `packages/*` versions drift,
via `scripts/check-workspace-versions.mjs` (also `npm run
check:versions`).

To make versions consistent, `@argent/ui` is bumped `0.0.1 → 0.12.0` (it
is `private`, so no publish impact).

## Verification (Node 20 / npm 10.8.2, matching CI)

- Lockfile check: no false positive on `main`; idempotent; catches a
tampered lock.
- Merge-marker & `.only` greps: proven to catch real violations and
reject lookalikes (`await.only`, `audit.only`, `foo.it.only`); clean
across the repo.
- `check-workspace-versions.mjs`: reports all packages at `0.12.0`;
passes `eslint` and `typecheck:scripts`.
- All new/changed files pass `prettier --check`; both workflow YAMLs
parse.

## Note: npm version coupling

The check runs on Node 20 (npm 10.8.2), matching the committed lock's
format. npm 11 (Node 24) regenerates the same lock minus cosmetic
`"peer": true` markers — no resolution change, but a contributor who
regenerates on npm 11 would trip the strict diff. The repo pins no
toolchain today (CI mixes Node 20 and 24). Pinning npm/Node repo-wide
would fully close this; left out of scope here — happy to add it as a
follow-up.
2026-06-19 15:19:19 +02:00

40 lines
1.4 KiB
JavaScript

#!/usr/bin/env node
// Fails if the workspace packages under packages/* don't all share the same
// version. Keeps the monorepo's lockstep versioning from silently drifting when
// a release bump misses a package.
import { readFileSync, readdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const packagesDir = join(dirname(fileURLToPath(import.meta.url)), "..", "packages");
const byVersion = new Map(); // version -> package names
for (const entry of readdirSync(packagesDir, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
let manifest;
try {
manifest = JSON.parse(readFileSync(join(packagesDir, entry.name, "package.json"), "utf8"));
} catch {
continue; // directory without a package.json
}
if (!manifest.version) continue;
const names = byVersion.get(manifest.version) ?? [];
names.push(manifest.name ?? entry.name);
byVersion.set(manifest.version, names);
}
if (byVersion.size <= 1) {
const [version] = byVersion.keys();
console.log(`All workspace packages are at ${version ?? "(no versioned packages found)"}.`);
process.exit(0);
}
console.error("Workspace package versions are out of sync:");
for (const [version, names] of [...byVersion].sort()) {
console.error(` ${version}: ${names.sort().join(", ")}`);
}
console.error(
"\nEvery package under packages/* must share one version. Bump the outliers to match."
);
process.exit(1);