Skip to content

🚧 fix: skip Jekyll pages build and prune superseded patch bundles on release - #1955

Draft
jeswr wants to merge 1 commit into
mainfrom
fix/pages-release-pruning
Draft

🚧 fix: skip Jekyll pages build and prune superseded patch bundles on release#1955
jeswr wants to merge 1 commit into
mainfrom
fix/pages-release-pruning

Conversation

@jeswr

@jeswr jeswr commented Jul 4, 2026

Copy link
Copy Markdown
Member

🚧 Release-flow fix for the failing pages deployment

Note: This PR was generated by an agent on @jeswr's behalf, following up on the triage in #1845. Please review accordingly.

References #1845 (intentionally not "Closes" — the pages branch is still ~7.4GB until the separate one-off prune below is approved and executed, so the deployment failure can recur in the meantime).

This PR performs no pages-branch surgery. It only changes the release flow going forward.

What this changes

1. Skip the Jekyll build (.nojekyll) — immediate mitigation

The failing step is GitHub's implicit pages-build-deployment Jekyll build, which copies the whole ~7.4GB site on the runner and dies with No space left on device. scripts/post-webpack.ts now writes an empty .nojekyll marker at the bundle root, and the @qiwi/semantic-release-gh-pages-plugin config gains "dotfiles": true — without it the plugin's gh-pages globbing (**/* with dot: false by default) silently drops the file. Once the next release publishes it, Jekyll is skipped entirely. (The 1GB GitHub Pages site limit still applies, hence 2. and the one-off prune.)

2. Stop the regrowth — bound per-minor storage

Each release adds ~32MB (patch dir + latest + vMajor/latest + vMajor/vMinor/latest, each holding index.js + dynamic-import.js at ~4MB) and "add": true means nothing is ever removed. Now, after semantic-release publishes x.y.z, a new workflow step prunes the superseded patch dirs of minor x.y from the pages branch:

  • Never touched: latest/, vMajor/latest, vMajor/vMinor/latest (all documented URL shapes in the README keep working), example/, other minors/majors, and dev/bench (written by the benchmark workflow — the prune only ever deletes numeric dirs directly under <major>/<minor>/, so it cannot reach it by construction).
  • Removed: numeric patch dirs of the released minor other than the one just published — e.g. publishing 4.10.3 removes 4/10/1 and 4/10/2. Per-minor storage is thus bounded at one patch dir + the latest copies. (Pinning an exact older patch remains possible via the npm package; README updated to say so.)
  • Mechanics: blobless sparse clone (--filter=blob:none + cone sparse-checkout of <major>/<minor> only), so the runner never materialises the large branch; ts-node scripts/prune-pages decides what to delete; push uses the same GH_TOKEN as the release (a github.token push would not trigger the Pages deployment) with a fetch+rebase retry loop in case the benchmark writer races.

The prune logic lives in scripts/prune-pages.ts (exported + CLI with --dry-run) and is covered by a new jest suite.

Dry-run evidence (local, no publish, no pages push)

Fixture tree mirroring the pages branch (latest/, 4/latest, 4/10/{1,2,3,latest}, 4/9/{9,latest}, 3/22/{0,latest}, 3/latest, example/, dev/bench/, .nojekyll):

$ npm run pages:prune -- --name=v4.10.3 --root=<fixture> --dry-run
Would remove 2 superseded patch dir(s) for v4.10.3:
  - 4/10/1
  - 4/10/2
Kept within this minor: 4/10/3, 4/10/latest
# tree verified byte-identical after dry run

Real run against the fixture removes exactly 4/10/1 and 4/10/2; everything else (including dev/bench and all latest shapes) verified untouched.

End-to-end rehearsal of the exact workflow step against a local bare repo standing in for origin (blobless sparse clone → prune → commit → push): resulting pages tree keeps .nojekyll, 3/22/{0,latest}, 4/9/{9,latest}, 4/10/{3,latest}, all latest shapes, example/, dev/bench — only 4/10/1 and 4/10/2 dropped. A second rehearsal with a competing dev/bench commit pushed mid-flight confirmed the retry loop recovers (rejected push → fetch+rebase → push OK on attempt 2) with both the bench update and the prune preserved.

.nojekyll path: post-webpack.ts smoke-tested in an isolated fake repo — bundle/.nojekyll (0 bytes) created alongside the usual dynamic-import.js and the three latest copies.

Validation: eslint on touched files exit 0; tsc --noEmit on the new/changed scripts + test exit 0; new jest suite 6/6 passing; workflow YAML parses.

One-off prune (separate sign-off)

Not part of this PR — awaiting explicit approval from @jeswr before anything is done to the pages branch. This PR only bounds future growth; the branch is already ~7.4GB (2,974 files, 188 minor dirs), ~7x the 1GB Pages limit. The proposed one-off action is:

  • Rewrite the pages branch as a single orphan commit (shrinking clone/checkout size, not just the deployed tree) containing only: dev/bench (benchmark history), example/, latest/, every vMajor/latest, every vMajor/vMinor/latest, and the current patch dir of the newest minor, plus .nojekyll.
  • Consequences: historical per-patch URLs (/x/y/z/index.js for superseded patches) stop resolving — anyone pinning them should move to vMajor/vMinor/latest or the npm package, where every published bundle remains available forever (dist/ ships in the eyereasoner package). All documented URL shapes in the README keep working.
  • Force-push of the rewritten branch is the only step that needs the sign-off; it is independent of (but complementary to) this PR and can happen before or after merge.

Happy to adjust retention (e.g. keep the last N patch dirs per minor instead of one) if preferred — the prune script takes the decision in one place.

🤖 Generated with Claude Code

…lease

The pages branch has grown to ~7.4GB because every release adds ~32MB
of webpack bundles (patch dir + three latest copies) with the gh-pages
plugin's "add": true, and GitHub's implicit Jekyll build now runs out
of disk on the runner (see #1845).

- publish a .nojekyll marker with the bundle (and enable the gh-pages
  plugin's dotfiles option so it is actually picked up) to skip the
  failing Jekyll build entirely
- after semantic-release publishes x.y.z, prune the superseded patch
  dirs of minor x.y from the pages branch via a blobless sparse clone,
  bounding per-minor storage at one patch dir plus the latest copies;
  latest/, vMajor/latest and vMajor/vMinor/latest are never touched,
  nor are example/ and dev/bench (benchmark workflow)
- document the retention policy in the README

See #1845.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant