From a1096a891539491e5e1d48f0fc30493801f6b97a Mon Sep 17 00:00:00 2001 From: Oto Macenauer Date: Wed, 23 Sep 2026 11:30:26 +0200 Subject: [PATCH] feat(build): run the contract checker on every installed artifact The publish-docs action checks a docs repo's output before release; the build now runs the same checker (actions/lib/check.js) again on what the knowledge base actually received, because not every artifact went through a current action: one published before a rule existed, one packed by hand, one whose HTML the old pattern-matching checks missed. - scripts/check-artifact.js checks each app as installed into apps/, before inline scripts are hoisted, on every source (prebuilt and local included). Findings are logged grouped by rule, one line per rule with a count and the first example, plus a per-artifact summary. - A strict build (KB_STRICT, the deployment workflow's default) refuses an artifact with an error finding, naming the rule and file. Warnings never fail a build. - This replaces the build's entry-point-only headless marker warning. check.js resolves htmlparser2/postcss from actions/node_modules when that tree is installed and from the root otherwise (CI's E2E job installs only the root), so the root package.json now pins both at the versions actions/package.json pins, and a test holds the two in step. Co-Authored-By: Claude Opus 5.5 (1M context) --- CLAUDE.md | 4 +- contract/DEPLOYMENT.md | 5 ++ contract/RULES.md | 4 ++ package-lock.json | 58 ++++++++++++----- package.json | 4 +- scripts/build-vite.js | 40 +++--------- scripts/check-artifact.js | 72 ++++++++++++++++++++++ tests/artifact-checks.spec.js | 113 ++++++++++++++++++++++++++++++++++ 8 files changed, 250 insertions(+), 50 deletions(-) create mode 100644 scripts/check-artifact.js create mode 100644 tests/artifact-checks.spec.js diff --git a/CLAUDE.md b/CLAUDE.md index e408596..e876a27 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -83,6 +83,7 @@ Orchestrator: `scripts/build-vite.js`. Flags: `--local`, `--headless`. - `scripts/build-vite.js` — Build orchestrator (4 steps: prepare, hoist, copy assets, astro build) - `scripts/fetch-apps.js` — GitHub Release artifact downloader. Only *obtains* an artifact; installing it is one shared path in `build-vite.js` - `scripts/artifacts.js` — Safe tarball extraction + tree copy, shared by both fetch paths. Validates archive members (no traversal, no absolute paths, no symlinks) before anything is written, and replaces the old `cp -r`/`tar` shell-outs so the build runs on Windows +- `scripts/check-artifact.js` — Runs the publish action's contract checker (`actions/lib/check.js`) on every installed artifact, before hoisting; logs findings grouped by rule, and fails a strict build on an error. `check.js` resolves its parsers from `actions/node_modules` or the root, which pins the same versions - `scripts/hoist-inline-scripts.js` — Moves inline `'), + 'guide/index.html': page(undefined, ''), + }); + try { + const lines = summarise(checkApp(dir, ENTRY)); + expect(lines).toHaveLength(1); + expect(lines[0].id).toBe('KB-HTML-004'); + expect(lines[0].line).toMatch(/^KB-HTML-004 ×2, e\.g\. demo\/\S+\.html: 1 inline ') }); + const clean = app({ 'index.html': page() }); + try { + const warned = []; + expect(reportFindings('owner/docs', checkApp(noisy, ENTRY), { strict: true, warn: (m) => warned.push(m) })) + .toEqual({ errors: 0, warnings: 1 }); + expect(warned.length).toBeGreaterThan(0); + + const silent = []; + expect(reportFindings('owner/docs', checkApp(clean, ENTRY), { strict: true, warn: (m) => silent.push(m) })) + .toEqual({ errors: 0, warnings: 0 }); + expect(silent).toEqual([]); + } finally { + rmSync(noisy, { recursive: true, force: true }); + rmSync(clean, { recursive: true, force: true }); + } + }); +});