Skip to content

[static] Previous-artifact cache silently expires after 1 day, forcing full 16-semester rebuilds #18

Description

@tiankaima

Problem

The curriculum/Young incremental cache relies on downloading the previous run's github-pages artifact (.github/workflows/build.yml:33-62). But actions/upload-pages-artifact@v4 defaults retention-days to 1 (verified in the action's action.yml), so each artifact expires exactly 24h after upload. The daily cron (.github/workflows/build.yml:5, 0 0 * * *) actually starts with ±40 min scheduler jitter — observed starts range 01:06–01:46 UTC. Whenever run N+1 starts later than run N's upload time + 24h, the restore step finds the artifact expired and silently rebuilds everything: all 16 selected semesters, ~280 JW schedule-table/datum chunk POSTs, all catalog lesson/exam fetches.

Evidence this already happened

  • Run 29463669719 (2026-07-16 01:15:33) logged at 01:15:50: Previous build artifact 29380812432 is unavailable. The 2026-07-15 artifact was created 01:12:49 and expired 01:12:43 — 3 minutes before the restore step ran.
  • The currently published snapshot proves the full rebuild: every row in upstream_fetches has fetched_at >= 2026-07-16T01:17:45 (checked https://static.life-ustc.tiankaima.dev/life-ustc-static.sqlite, SELECT MIN(fetched_at), MAX(fetched_at) FROM upstream_fetches2026-07-16T01:17:45 … 2026-07-18T01:17:11). With a working cache, ended semesters keep their original fetch rows.
  • gh api repos/Life-USTC/static/actions/artifacts shows every github-pages artifact with expires_at = created_at + 1 day.

A full rebuild is ~10× the normal upstream load against jw.ustc.edu.cn / catalog.ustc.edu.cn and makes a mid-run failure (no publish at all) much more likely. It also recurs unpredictably since GitHub does not guarantee scheduled-run start times.

Suggested fix

One line in the upload step (.github/workflows/build.yml:80-84):

      - name: Upload static files as artifact
        id: deployment
        uses: actions/upload-pages-artifact@v4
        with:
          path: build/
          retention-days: 7   # survive a failed/delayed day

Optionally also make the restore step fall back to older successful runs when the newest artifact is expired (currently it only tries .[0].databaseId and gives up), and log a loud warning (::warning::) when the cache is lost so the full rebuild is visible instead of silent.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions