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.
This commit is contained in:
SatoshiQaziMuhammed authored and GitHub committed 2026-11-01 11:40:55 +04:00
1 parent c84494c22f
commit 07aa8b3dd0
1 file changed
+25
+25
View File
@@ -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