From 07aa8b3dd0fdbc0bb1e96bfa5fe17c497940254b Mon Sep 17 00:00:00 2001 From: SatoshiQaziMuhammed Date: Sun, 1 Nov 2026 11:40:55 +0400 Subject: [PATCH] Purge changed files from jsDelivr after promoting to master (#77) * feat(config): sign the served config files signed/targets.json gives the sha256 of every JSON file the wallet may fetch, signed with an Ed25519 targets key. signed/root.json names that key and three sr25519 root keys (wallet accounts), two of which can replace it; the wallet will ship this first root. scripts/signed_config.py writes the root, signs the targets list and checks it. Code Quality now refuses a tree whose served files do not match the signed list, and runs weekly so an expiring signature shows a month ahead. * fix(config): sign the test fixtures and follow published roots The wallet refuses any JSON file from this repository that the signed list does not give, and its instrumented tests read tests/, so those files are signed too. A root after the first is written to signed/root.N.json, where the wallet looks for it; sign and check use the newest published root. * Purge changed files from jsDelivr after promoting to master When raw.githubusercontent.com is unreachable the wallet reads the same paths from jsDelivr, which caches a branch file for up to 12 hours and expires each file on its own. A stale chains.json next to a fresh signed/targets.json fails the wallet's hash check, so the wallet keeps its old config until both expire. After master moves, purge every JSON file that changed, and fail the job if any CDN behind jsDelivr did not purge, or throttled, a path. --- .github/workflows/promote-to-live.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/promote-to-live.yml b/.github/workflows/promote-to-live.yml index 20bfc89..e8726b4 100644 --- a/.github/workflows/promote-to-live.yml +++ b/.github/workflows/promote-to-live.yml @@ -53,6 +53,8 @@ jobs: - name: Promote run: | SHA="${{ github.event.workflow_run.head_sha }}" + git fetch origin master + git rev-parse origin/master > "$RUNNER_TEMP/previous-master" # --force-with-lease, not --force: if master has moved since this job read it, # the push is refused rather than silently discarding whatever moved it. @@ -63,3 +65,26 @@ jobs: echo "\`master\` → \`$SHA\`" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "Apps read this branch directly; the change is live as soon as their config refreshes." >> "$GITHUB_STEP_SUMMARY" + + - name: Purge the changed files from jsDelivr + # When raw.githubusercontent.com is unreachable the wallet reads the same paths from + # jsDelivr, which keeps a branch file for up to 12 hours and expires each file on its + # own clock. A cached chains.json next to a fresh signed/targets.json (or the reverse) + # fails the wallet's hash check, and the wallet keeps its old config until both age + # out. Purging every changed file together makes the mirror serve the new set at once. + run: | + SHA="${{ github.event.workflow_run.head_sha }}" + PREVIOUS=$(cat "$RUNNER_TEMP/previous-master") + failed=0 + for path in $(git diff --name-only "$PREVIOUS" "$SHA" -- '*.json'); do + url="https://purge.jsdelivr.net/gh/${{ github.repository }}@master/$path" + # Each CDN behind jsDelivr reports on its own; a throttled or failed one still + # serves the old copy. + if curl -sSf -m 30 --retry 3 "$url" | jq -e '[.paths[] | (.throttled | not) and ([.providers[]] | all)] | all' > /dev/null; then + echo "purged $path" + else + echo "::error::jsDelivr purge failed for $path" + failed=1 + fi + done + exit $failed