Files
CC-Switch/scripts/rewrite-updater-manifest.mjs
T
Jason 2b2f2cfad9 ci: mirror in-app updater to Cloudflare R2 with release-gated sync
- Move the R2 sync out of release.yml into a standalone sync-r2.yml
  triggered on release promotion (release: released) or manual dispatch,
  so the mirror follows the same gate as GitHub's /releases/latest
- Hard-fail on the official repo when R2 secrets are missing (a silently
  stale mirror strands updater users); forks may still skip
- Only the tag that is currently releases/latest may rewrite the root
  manifests or prune old versions; backfills of older tags restore
  versioned files only
- Resolve releases/latest with retries and fail instead of guessing on
  API errors; re-verify right before publishing the root manifests
- Add scripts/rewrite-updater-manifest.mjs to point latest.json download
  URLs at the mirror (minisign signatures cover file contents and stay
  valid); upload the macOS .tar.gz updater payload alongside installers
- Put https://dl.ccswitch.io/latest.json first in the updater endpoints
  with GitHub as connectivity fallback
2026-07-27 18:41:49 +08:00

46 lines
1.9 KiB
JavaScript

#!/usr/bin/env node
// Rewrites the Tauri updater manifest (latest.json) so its download URLs point
// at the R2 mirror instead of GitHub Releases. Minisign signatures cover file
// contents, not URLs, so they stay valid unchanged — the updater still verifies
// every downloaded artifact against the pubkey baked into the app.
//
// Usage: node scripts/rewrite-updater-manifest.mjs <latest-json> <tag> <base-url> [output]
import { readFileSync, writeFileSync } from 'node:fs';
const [input, tag, baseUrl, output = 'latest-r2.json'] = process.argv.slice(2);
if (!input || !tag || !baseUrl) {
console.error('Usage: node scripts/rewrite-updater-manifest.mjs <latest-json> <tag> <base-url> [output]');
process.exit(1);
}
const manifest = JSON.parse(readFileSync(input, 'utf8'));
const platforms = Object.entries(manifest.platforms ?? {});
if (platforms.length === 0) {
console.error(`No platforms found in ${input}`);
process.exit(1);
}
const normalizedBase = baseUrl.replace(/\/+$/, '');
// Mirrored artifacts live at <base>/<tag>/<file>; on GitHub they live at
// .../releases/download/<tag>/<file>. Rebuild from the filename rather than
// prefix-replacing so a layout change on either side fails loudly here.
const githubPrefix = `https://github.com/`;
for (const [key, entry] of platforms) {
if (typeof entry?.url !== 'string' || typeof entry?.signature !== 'string') {
console.error(`Platform ${key} is missing url or signature`);
process.exit(1);
}
if (!entry.url.startsWith(githubPrefix) || !entry.url.includes(`/releases/download/${tag}/`)) {
console.error(`Platform ${key} has unexpected url for ${tag}: ${entry.url}`);
process.exit(1);
}
const name = entry.url.split('/').pop();
entry.url = `${normalizedBase}/${tag}/${name}`;
}
writeFileSync(output, `${JSON.stringify(manifest, null, 2)}\n`);
console.log(`Wrote ${output} with ${platforms.length} platforms pointing at ${normalizedBase}/${tag}/`);