Skip to content

perf: build hygiene (conditional vendoring, CI GOGC, determinism gate, docs)#2034

Merged
markdumay merged 4 commits into
mainfrom
perf/build-hygiene
Jul 16, 2026
Merged

perf: build hygiene (conditional vendoring, CI GOGC, determinism gate, docs)#2034
markdumay merged 4 commits into
mainfrom
perf/build-hygiene

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Part of the build-time optimization program (Chunk C). Four independent hygiene items, no change
to the generated site (byte-diff verified, see below).

1. Conditional module vendoring

prebuild/prestart previously ran rimraf _vendor && hugo mod vendor on every build and
server start. mod:vendor now delegates to scripts/mod-vendor.cjs, which skips vendoring when
_vendor/modules.txt is newer than both go.mod and go.sum, and otherwise runs the original
rimraf _vendor && hugo mod vendor (kept verbatim as mod:vendor:force).

  • CI is byte-for-byte unaffected: a clean checkout has no _vendor/, so the guard always misses
    and vendoring runs exactly as before. Same for Netlify (the resources plugin caches
    resources/, not _vendor/).
  • mod:update still re-vendors (it removes _vendor/ first, and hugo mod get -u touches
    go.mod).
  • npm run mod:vendor:force re-vendors unconditionally.

Vendor-skip evidence (second consecutive npm run build:example):

> node scripts/mod-vendor.cjs
mod-vendor: _vendor is up to date, skipping (use "mod:vendor:force" to re-vendor)

2. GOGC=400 for CI builds

Measured locally on the exampleSite build, GOGC=800 saved 6-7 CPU-seconds; 400 is the
conservative pick as CI runners have less RAM. Local defaults are intentionally unchanged.

  • Netlify: added GOGC = "400" to [build.environment] in data/netlify.toml — the SOURCE
    the header generator reads (build:headers:prod renders mod-csp's index.netlify.toml,
    which remarshals site.Data.netlify). The auto-generated root netlify.toml was regenerated
    and committed alongside; running the generator twice produces no further diff.
  • GitHub Actions: .github/workflows/lint-build.yml only calls the reusable workflow
    gethinode/.github/.github/workflows/lint-build.yml@v1, and an env: block at the caller does
    not propagate into it. GOGC=400 is therefore prepended to each command in the build-command
    input. The reusable workflow runs the command with shell: bash on all three OSes, so the
    env-prefix syntax is portable.

3. Determinism gate

scripts/check-build-determinism.sh builds the exampleSite twice from scratch
(hugo --gc --minify -s exampleSite, rm -rf exampleSite/public before each), verifies zero
ERROR lines and the expected page counts (98 EN / 26 FR / 24 NL) per build, then diff -rqs the
two trees. The only tolerated diffs are the pre-existing intermittent .Content race in
RSS-embedded HTML (accepted 2026-07-15): en/index.xml, en/docs/blocks/index.xml,
en/tags/block/index.xml. Anything else exits non-zero. Future perf PRs cite one command.

4. CI caching documentation

Recorded in CLAUDE.md (Build Performance and CI Caching):

  • resources/_gen caching is worth ~4x on cold runners (68.7 s → ~17 s measured);
  • the shared root resources/ dir serves both the theme and the exampleSite build;
  • the getresource 5-minute TTL adds network noise to isolated runs.

Verification

  • npm run build:example output on this branch vs. stock (origin/main) is byte-identical
    except the intended GOGC = '400' line in the generated netlify.toml artifact (item 2) and
    the RSS allowlist files above. Page counts 98/26/24, zero ERROR lines, on both.
  • scripts/check-build-determinism.sh runs green on this branch.
  • pnpm test (full lint suite) passes.
  • pnpm build:headers:prod round-trip is stable: a second run after regeneration produces no
    further diff in netlify.toml.

🤖 Generated with Claude Code

markdumay and others added 4 commits July 15, 2026 21:43
- prebuild/prestart re-vendored all Hugo modules on every build; mod:vendor
  now skips when _vendor/modules.txt is newer than go.mod and go.sum
- the original unconditional behavior is preserved verbatim as
  mod:vendor:force and is used whenever the guard misses
- CI is unaffected: clean checkouts have no _vendor, so the guard always
  misses there and vendoring runs exactly as before

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Netlify: GOGC added to [build.environment] in data/netlify.toml (the
  source read by the header generator) and the auto-generated netlify.toml
  regenerated via build:headers:prod; a second generator run is a no-op
- GitHub Actions: GOGC=400 prepended to the build-command input of the
  reusable workflow, as a caller-level env block does not propagate into it
- measured locally on the exampleSite build, GOGC=800 saved 6-7 CPU-seconds;
  400 is the conservative pick as CI runners have less RAM
- local defaults are intentionally unchanged

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- scripts/check-build-determinism.sh builds the exampleSite twice from
  scratch, verifies zero ERROR lines and the expected page counts
  (98 EN / 26 FR / 24 NL), then compares both trees with diff -rq
- the only tolerated diffs are the pre-existing intermittent .Content race
  in RSS-embedded HTML (en/index.xml, en/docs/blocks/index.xml,
  en/tags/block/index.xml), accepted by the maintainer on 2026-07-15
- future performance PRs cite a green run of this one command as evidence
  that they do not change the generated site

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- caching resources/_gen is worth about 4x on cold runners (68.7 s to ~17 s
  measured on the exampleSite build)
- the shared root resources/ directory serves both the theme build and the
  exampleSite build
- the getresource 5-minute TTL adds network noise to isolated runs
- document the conditional mod:vendor guard, mod:vendor:force, the CI GOGC
  setting, and the determinism gate for performance PRs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit 719c7b0
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a585ec38a840400087c1797
😎 Deploy Preview https://deploy-preview-2034--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay

Copy link
Copy Markdown
Collaborator Author

Program summary: this PR is Chunk C of the build-time optimization program and is intentionally HELD for the maintainer. All gates passed (byte-diff vs stock, determinism harness green incl. an independently re-run pass, lint green, netlify.toml round-trip stable). Live register: https://github.com/gethinode/gethinode.com/blob/program/build-time-optimization/docs/superpowers/specs/2026-07-15-build-time-optimization-register.md (private repo — link for the maintainer).

@markdumay markdumay merged commit f161b7e into main Jul 16, 2026
16 checks passed
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.2.6 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant