feat: site-build-gate as external pkg

Merged
WI
1d ago
+35-1299

Summary

Learn runs the published @netdata/site-build-gate package instead of a copy of the gate checked in under scripts/site-build-gate/. A Dependabot security update to one of the gate's dependencies is then an ordinary yarn.lock change. It no longer fails the Netlify build on a checksum mismatch.

 build:netlify
   verify-git-history
   verify-indexnow-vendor
-  npm ci --prefix scripts/site-build-gate      # separate npm lock, bound by manifest.json
   docusaurus build
   run-post-build-gates
-    scripts/site-build-gate/site_build_gate.mjs       # refuses to run if any file's hash differs
+    node_modules/@netdata/site-build-gate/site_build_gate.mjs
 package.json
+  devDependencies: "@netdata/site-build-gate": "^10.1.0"
 yarn.lock
+  @netdata/site-build-gate@^10.1.0             # css-tree, parse5, saxes already in the graph
 scripts/
-└── site-build-gate/
-    ├── manifest.json        # SHA-256 of package.json, package-lock.json, site_build_gate.mjs
-    ├── package.json
-    ├── package-lock.json
-    └── site_build_gate.mjs

The gate's rules, baseline (config/site-build-gate-baseline.json) and command-line arguments are unchanged.

  • Source and versioning: the package is published from netdata-labs/seo (vendor/site-build-gate/). Its major version is the ruleset version, so 10.x means ruleset 10.
  • Tests: they no longer pin checksums. Instead they check that the installed gate's major version matches the baseline's ruleset_version.
  • Docs: AGENTS.md (static build gate contract) and README.md (dependency authority, and the local setup step that mentioned the nested gate install) describe the package instead of the copy.

Evidence

  • Before: a lockfile-only update to a gate dependency, the shape of a Dependabot security PR, fails after the full Docusaurus build:

    site build gate contract error: vendor checksum mismatch for package-lock.json: expected a3cac544…, got 3c71e70c…
    exit=2
    

    #3121 (source-map-js 1.2.1 → 1.2.2) shows this in practice: all GitHub checks were green, netlify/netdata-docusaurus/deploy-preview was red, and the PR was closed. The fix needed coordinated changes in seo, website and Learn (#3136).

    After: the same kind of change, only yarn.lock (entities 8.1.0 → 8.0.0, within ^8.0.0), installed with --frozen-lockfile:

    node scripts/run-post-build-gates.mjs
    "checked_pages": 2030,
    "regressions": [],
    exit=0
    
  • Full Netlify build (npm run build:netlify, Node 22.23.2): passes. The site build gate scanned 2,030 pages with 0 regressions, the Cloudflare analytics gate passed, and there are 297 broken-anchor warnings, the same as master.

  • Tests: yarn test:run passes: Vitest 494 passed and 1 skipped, plus the first-touch (26), IndexNow (64), Swagger UI assets (3), dependency-authority (3) and site-build-gate (4) suites.

  • Pull request checks on b1eebcd0f (^10.1.0): the Netlify deploy preview, header and redirect rules, the required same-site links job, the advisory cross-site and new third-party link jobs, verify and the dependency-impact check all pass.

  • Published package (10.1.0 on registry.npmjs.org, released by the seo publish workflow through npm trusted publishing): all four files are byte-identical to vendor/site-build-gate/ at seo main @ 7b3e6d83. Installed from the registry, it gives site build gate v10: 2030 pages, 0 regression(s), 0 accepted finding(s) on the same build.

Merge Danger

Door: two-way

Reverting this PR restores the vendored copy and its npm ci step exactly. The published package stays on npm either way; nothing else depends on it yet.

Blast Radius: deploys

  • Every Netlify build runs the gate. If the package were missing or broken, every deploy would fail (exit 2), including ingest. It fails loudly; it can't silently let pages through.
  • Version range: ^10.1.0 lets Dependabot propose later 10.x releases as their own PRs; .github/dependabot.yml needs no change. A new major is a ruleset change: its Dependabot PR fails the Netlify build by design (baseline ruleset 10 does not match 11) until the PR also updates config/site-build-gate-baseline.json and any new findings are reviewed.
  • Changes an earlier decision: "Learn commits the checksum-bound gate" becomes "Learn installs a published version". Costa agreed to this experiment.
  • Trade-off: the gate's parser dependencies now float within ^ ranges in Learn's yarn.lock instead of matching seo byte for byte.
    • Caught by the build: a parser update that crashes the gate or reports defects that aren't there.
    • Not caught here: a parser update that makes the gate miss a defect. Only seo's fixture tests can catch that.
  • Left unchanged on purpose: config/seo-remediation-contract.json still names scripts/site-build-gate, because it's a frozen historical acceptance record.

Summary by CodeRabbit

  • Build and Dependency Management

    • The site-build gate is now installed as a published package and runs locally after the Docusaurus build.
    • The gate’s major version is checked against the configured baseline version.
    • The Netlify build no longer performs a separate dependency installation for the gate.
  • Documentation

    • Updated setup and dependency guidance to reflect the package-based gate and the project’s Node.js and Yarn versions.