From 3d7dda4cf42176d587b459345c56236a26030324 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 8 Aug 2026 05:51:51 -0700 Subject: [PATCH] fix(docs): retain prior builds' hashed assets across Pages deploys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pages serves exactly the newest artifact, so every push-triggered deploy deleted the previous build's content-hashed JS/CSS while edge caches (max-age=300, stale-while-revalidate=3600) kept serving HTML that referenced them. With deploys landing every ~15-30 min, docs pages spent most of the day pointing at 404'd bundles — search (pure client JS) was the loudest casualty. Fix: keep a rolling 14-day pool of hashed assets (en + zh-Hans) in the Actions cache and union-merge it into each deploy artifact, current build authoritative on collision (cp --update=none). Stale HTML and already-open tabs now keep resolving across any number of deploys. --- .github/workflows/deploy-site.yml | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index 588d7707ea..26e11ee9fb 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -189,6 +189,61 @@ jobs: cp website/build/llms-full.txt _site/llms-full.txt fi + # Pages serves exactly the newest artifact, so each deploy used to delete + # the previous build's content-hashed JS/CSS while edge caches (Vercel → + # Fastly, max-age=300 + stale-while-revalidate=3600) kept serving HTML + # that referenced it — every asset request 404'd for up to ~65 minutes + # after each deploy. With push-triggered deploys landing every ~15-30 + # minutes, the docs were in that broken window most of the day (search, + # being pure client JS, died first). Fix: keep a rolling pool of prior + # builds' hashed assets and union-merge it into every artifact so stale + # HTML keeps resolving. Hashed filenames are content-addressed, so a + # collision is by definition the identical file — the merge never + # overwrites current-build output (cp --update=none). + - name: Restore asset retention pool + uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: _asset_retention + key: docs-asset-retention-${{ github.run_id }} + restore-keys: | + docs-asset-retention- + + - name: Merge retained assets from previous deploys + run: | + set -euo pipefail + ASSET_DIRS="assets zh-Hans/assets" + mkdir -p _asset_retention + # 1) Add this build's hashed assets to the pool (fresh mtimes, so + # assets still shipped by current builds never age out). + for d in $ASSET_DIRS; do + if [ -d "_site/docs/$d" ]; then + mkdir -p "_asset_retention/$d" + cp -a "_site/docs/$d/." "_asset_retention/$d/" + fi + done + # 2) Drop pool entries no build has produced for 14 days — far + # beyond any edge-cache or open-tab horizon. + find _asset_retention -type f -mtime +14 -delete + find _asset_retention -type d -empty -delete + # 3) Union-merge the pool into the artifact; --update=none keeps the + # current build authoritative for any path it produced. + for d in $ASSET_DIRS; do + if [ -d "_asset_retention/$d" ]; then + mkdir -p "_site/docs/$d" + cp -R --update=none "_asset_retention/$d/." "_site/docs/$d/" + fi + done + echo "retention pool:" && du -sh _asset_retention + echo "deployed assets:" && du -sh _site/docs/assets + + - name: Save asset retention pool + # Always save under a fresh key (caches are immutable); restore-keys + # prefix matching picks the newest on the next run. + uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: _asset_retention + key: docs-asset-retention-${{ github.run_id }} + - name: Upload artifact uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 with: