diff --git a/scripts/linux-binaries-manifest.test.mjs b/scripts/linux-binaries-manifest.test.mjs index 010ed1ca2d..aa99d27cd6 100644 --- a/scripts/linux-binaries-manifest.test.mjs +++ b/scripts/linux-binaries-manifest.test.mjs @@ -166,10 +166,9 @@ test('openbitfun sync mirrors both products and their checksums', () => { assert.match(syncScript, /WEBSITE_RELEASE_DIR.*linux-binaries-v1\.json/); assert.match(syncScript, /mirror_dispatch_macos_cli_archives/); assert.match(syncScript, /x86_64-apple-darwin aarch64-apple-darwin/); - assert.match(syncScript, /WEBSITE_RELEASE_DIR.*relay-image\.json/); }); -test('release sync pins Relay and Linux metadata to the updater release during latest rotation', (t) => { +test('release sync pins Linux metadata to the updater release during latest rotation', (t) => { const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'openbitfun-release-metadata-')); t.after(() => fs.rmSync(temp, { recursive: true, force: true })); const urls = path.join(temp, 'urls'); @@ -178,7 +177,6 @@ test('release sync pins Relay and Linux metadata to the updater release during l source "$SYNC_SCRIPT" flock() { return 0; } curl() { printf '%s' "$TEST_LATEST_JSON"; } - mirror_relay_image_descriptor() { printf '%s\\n' "$GITHUB_RELAY_IMAGE_URL" >> "$TEST_URLS"; } mirror_linux_binaries() { printf '%s\\n' "$GITHUB_LINUX_BINARIES_URL" >> "$TEST_URLS"; } mirror_dispatch_macos_cli_archives() { :; } download_asset() { :; } @@ -202,56 +200,10 @@ test('release sync pins Relay and Linux metadata to the updater release during l }); assert.equal(result.status, 0, result.stderr); assert.deepEqual(fs.readFileSync(urls, 'utf8').trim().split('\n'), [ - `${releaseBase}/relay-image.json`, `${releaseBase}/linux-binaries-v1.json`, + `${releaseBase}/linux-binaries-v1.json`, ]); }); -test('the release mirror copies signed Relay descriptors from both official namespaces unchanged', (t) => { - const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'openbitfun-relay-mirror-')); - t.after(() => fs.rmSync(temp, { recursive: true, force: true })); - for (const [namespace, version, accepted] of [ - ['gcwing', '1.0.10', true], ['openbitfun', '1.0.10', true], - ['openbitfun', '1.0.9', false], ['unrelated-owner', '1.0.10', false], - ]) { - const mirror = fs.mkdtempSync(path.join(temp, `${namespace}-`)); - fs.mkdirSync(path.join(mirror, '1.0.10')); - const descriptor = JSON.stringify({ schema_version: 1, version, tag: `v${version}`, - image: `ghcr.io/${namespace}/openbitfun-relay-server`, digest: `sha256:${'a'.repeat(64)}` }); - const result = spawnSync('bash', ['-c', ` - source "$SYNC_SCRIPT" - VERSION=1.0.10 - VERSION_DIR="$WEBSITE_RELEASE_DIR/$VERSION" - curl() { - local output - while [ "$#" -gt 1 ]; do - if [ "$1" = -o ]; then output="$2"; shift; fi - shift - done - case "$1" in - *.sig) printf 'original signature' > "$output" ;; - *) printf '%s' "$DESCRIPTOR" > "$output"; printf 200 ;; - esac - } - mirror_relay_image_descriptor - `], { - cwd: repoRoot, encoding: 'utf8', windowsHide: true, - env: { ...process.env, SYNC_SCRIPT: path.join(repoRoot, 'scripts/openbitfun-release-sync.sh'), - OPENBITFUN_RELEASE_CHANNEL: 'stable', WEBSITE_RELEASE_DIR: mirror, DESCRIPTOR: descriptor }, - }); - assert.equal(result.status, accepted ? 0 : 1, `${namespace} ${version}: ${result.stderr}`); - for (const dir of [mirror, path.join(mirror, '1.0.10')]) { - const image = path.join(dir, 'relay-image.json'); - if (accepted) { - assert.equal(fs.readFileSync(image, 'utf8'), descriptor); - assert.equal(fs.readFileSync(`${image}.sig`, 'utf8'), 'original signature'); - } else { - assert.equal(fs.existsSync(image), false); - assert.equal(fs.existsSync(`${image}.sig`), false); - } - } - } -}); - test('openbitfun sync mirrors the website installer from the exact updater release', () => { const temp = fs.mkdtempSync(path.join(os.tmpdir(), 'openbitfun-windows-installer-mirror-')); const versionDir = path.join(temp, 'release', '1.2.3'); diff --git a/scripts/openbitfun-release-sync.sh b/scripts/openbitfun-release-sync.sh index c68572cd91..ee40ab01f8 100755 --- a/scripts/openbitfun-release-sync.sh +++ b/scripts/openbitfun-release-sync.sh @@ -4,8 +4,8 @@ # # Flow: # 1. Fetch the selected channel's latest-v1.json from GitHub -# 2. Mirror the signed Relay image descriptor and Linux binary manifest FIRST -# (small trust metadata must not queue behind ~700 MB of Desktop packages) +# 2. Mirror the Linux binary manifest FIRST (small trust metadata must not +# queue behind ~700 MB of Desktop packages) # 3. If that version is already published, prune to KEEP_VERSIONS and stop # 4. Otherwise download the new version, rewrite manifests, then prune # @@ -61,7 +61,6 @@ case "$RELEASE_CHANNEL" in esac GITHUB_LATEST_JSON_URL="${GITHUB_RELEASE_ROOT}/latest-v1.json" GITHUB_LINUX_BINARIES_URL="${GITHUB_RELEASE_ROOT}/linux-binaries-v1.json" -GITHUB_RELAY_IMAGE_URL="${GITHUB_RELEASE_ROOT}/relay-image.json" OPENBITFUN_BASE_URL="https://openbitfun.com/release${CHANNEL_PATH}" # The mirror deliberately lives outside the website checkout. It used to be # OpenBitFun-Website/dist/release, but `npm run build` empties dist/, so every @@ -381,9 +380,9 @@ PY rm -f "${WEBSITE_RELEASE_DIR}/linux-binaries-v1.json" log "Linux binaries manifest is not present in the latest release yet; Desktop mirror only." else - # Transient failure. Keep whatever is already published: CLI self-update and - # one-click Relay deploy both fall back to this file, so a single flaky run - # must not take it offline for the next 10 minutes. + # Transient failure. Keep whatever is already published: CLI self-update + # falls back to this file, so a single flaky run must not take it offline + # for the next 10 minutes. log "WARN: Linux binaries manifest unreachable this run; keeping the published mirror." fi } @@ -442,70 +441,6 @@ mirror_dispatch_macos_cli_archives() { fi } -# Mirror the signed, digest-pinned container descriptor before large Desktop -# assets. The mirror is not trusted: Desktop verifies relay-image.json.sig with -# its compiled-in minisign key before sending the digest to a customer server. -mirror_relay_image_descriptor() { - local descriptor_tmp signature_tmp status descriptor_version - descriptor_tmp="${VERSION_DIR}/relay-image.json.part" - signature_tmp="${VERSION_DIR}/relay-image.json.sig.part" - rm -f "$descriptor_tmp" "$signature_tmp" - - status="$(curl -sSL \ - --connect-timeout "$CONNECT_TIMEOUT" --max-time "$MAX_TIME" \ - -o "$descriptor_tmp" -w '%{http_code}' "$GITHUB_RELAY_IMAGE_URL" || echo 000)" - if [ "$status" = "404" ]; then - log "Relay image descriptor is not present in the latest release yet." - rm -f "$descriptor_tmp" - return 0 - fi - if [ "$status" != "200" ]; then - log "WARN: relay-image.json unreachable (HTTP $status); keeping any existing versioned copy." - rm -f "$descriptor_tmp" - return 0 - fi - if ! curl -fsSL --retry "$MAX_RETRIES" --retry-delay "$RETRY_DELAY" \ - --connect-timeout "$CONNECT_TIMEOUT" --max-time "$MAX_TIME" \ - -o "$signature_tmp" "${GITHUB_RELAY_IMAGE_URL}.sig"; then - log "WARN: relay-image.json.sig unreachable; refusing to publish an unsigned descriptor." - rm -f "$descriptor_tmp" "$signature_tmp" - return 0 - fi - - descriptor_version="$("$PYTHON" - "$descriptor_tmp" <<'PY' -import json, re, sys -with open(sys.argv[1], encoding="utf-8") as f: - data = json.load(f) -assert data.get("schema_version") == 1 -assert data.get("image") in { - "ghcr.io/openbitfun/openbitfun-relay-server", - "ghcr.io/gcwing/openbitfun-relay-server", -} -assert re.fullmatch(r"sha256:[0-9a-f]{64}", data.get("digest", "")) -print(data["version"]) -PY -)" || { - log "ERROR: relay-image.json failed its schema/repository/digest checks" - rm -f "$descriptor_tmp" "$signature_tmp" - return 1 - } - if [ "$descriptor_version" != "$VERSION" ]; then - log "ERROR: Relay image descriptor version $descriptor_version does not match Desktop version $VERSION" - rm -f "$descriptor_tmp" "$signature_tmp" - return 1 - fi - - mv "$descriptor_tmp" "${VERSION_DIR}/relay-image.json" - mv "$signature_tmp" "${VERSION_DIR}/relay-image.json.sig" - publish_file_atomically \ - "${VERSION_DIR}/relay-image.json" \ - "${WEBSITE_RELEASE_DIR}/relay-image.json" - publish_file_atomically \ - "${VERSION_DIR}/relay-image.json.sig" \ - "${WEBSITE_RELEASE_DIR}/relay-image.json.sig" - log "Published signed Relay image descriptor for $VERSION" -} - # ── Main ─────────────────────────────────────────────────────── main() { mkdir -p "$(dirname "$LOCK_FILE")" @@ -564,7 +499,6 @@ print(bases.pop()) exit 1 } - GITHUB_RELAY_IMAGE_URL="${RELEASE_ASSET_BASE_URL}/relay-image.json" GITHUB_LINUX_BINARIES_URL="${RELEASE_ASSET_BASE_URL}/linux-binaries-v1.json" INSTALLER_METADATA=$(printf '%s' "$LATEST_JSON" | "$PYTHON" -c " @@ -599,10 +533,9 @@ if entry: VERSION_DIR="${WEBSITE_RELEASE_DIR}/${VERSION}" mkdir -p "$VERSION_DIR" - # 4. Stable owns the CLI/Relay floating manifests. The first beta slice only + # 4. Stable owns the CLI floating manifests. The first beta slice only # mirrors Desktop updater and installer assets under /release/beta. if [ "$RELEASE_CHANNEL" = "stable" ]; then - mirror_relay_image_descriptor mirror_linux_binaries mirror_dispatch_macos_cli_archives else