Release · v3.14.31
Stop building the site twice, and stop Netlify…
perf(ci): stop building the site twice, and stop Netlify building nothing
Details
Measured first, because the headline number was misleading. A pull request's GitHub checks finish in **1m29s** wall clock, not seven minutes: Build site + TypeScript 0.9 min Verify Python (uv) 0.3 min Links + analytics 1.4 min Verify Supabase REST 0.1 min The seven minutes is Netlify's deploy preview (~8 min on recent PRs). It is not a required check, so it never blocked a merge — but it is what the PR page shows pending, and it was doing a full build for changes that cannot alter the site. ## The site was being built twice `links-and-analytics` looked like parallelism and was not: it rebuilt the same tree the build job had just built — 41s in one job, 48s in the other, for one artifact — and because it started from scratch it finished last and set the wall-clock for the whole run. Its actual work is 24s (Chrome 14s, crawl 10s). Folded into the build job, which now builds once and then checks what it built. ## Netlify: skip builds that cannot change the output `ignore` now compares the commit range and skips when nothing outside `.github/`, `.claude/`, `backend/supabase_db/`, `test/` and `*.md` changed. A workflow or docs change was costing a full build: unshallowing 200 MB of history, `bundle install`, `pnpm install`, then generating 925 change-log pages to produce output identical to the last deploy. Across the last 39 merges to `main`, **7 would now skip**. It stays deliberately conservative — `package.json` and anything under `tools/` still trigger a build, because both can change what Jekyll emits — and any case it cannot judge (no cached ref) builds. ## Netlify: fetch the history without the blobs The change log is a pure function of the whole history, so the shallow clone has to be deepened — but `readHistory` only ever runs `git log` over commit metadata, never reading a file at an old revision. `--filter=blob:none` fetches the commits and trees and leaves ~200 MB of blobs behind, with the plain `--unshallow` kept as the next fallback. Flagged honestly: this one is NOT measured. The local `file://` transport ignores partial-clone filters, so the size was identical in a local test; GitHub supports them, and if the filter is ever refused the fallback makes it a no-op. Branch protection updated in the same breath: `Links + analytics` is no longer a check name, and a required check that never reports blocks a pull request forever. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DAJJ63FFnLoD5XLpu6yJaG
Files changed (2)
| .github/workflows/pr-validation.yml | +9 | −19 |
| netlify.toml | +21 | −2 |