Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 2 additions & 50 deletions scripts/linux-binaries-manifest.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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() { :; }
Expand All @@ -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');
Expand Down
79 changes: 6 additions & 73 deletions scripts/openbitfun-release-sync.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
#
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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")"
Expand Down Expand Up @@ -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 "
Expand Down Expand Up @@ -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
Expand Down
Loading