Dashboard › cli › Distillation
264b28af-7ef4-48e7-9c0d-bbd6cec62ab3["lore_tm_v1_rrCux1kVojuFtKc_NRwZwn6hSm3ORFjlE-mg3o3X_7A","lore_tm_v1_ulvniQa951w_TC8oWs7FeZZ6QANuTkuDRv0clyz9h1Q","lore_tm_v1_c5Q-YPBCeJuilUlao1AhRhHPXHZdO5Nn4_ToDSUSIQU","lore_tm_v1_b07JUz6IuCRSJYNeR7szcvsaqjRP3pHlj27l17ojsXM"]
packages/cli/src/lib/ghcr.ts implements GHCR/OCI helpers for the public ghcr.io/getsentry/cli nightly package; anonymous access requires no user-provided token beyond the registry token exchange.packages/cli/src/lib/ghcr.ts defines GHCR_BLOB_TIMEOUT = 30_000; its comments cite observed GHCR cold-start latency spikes from 126ms to 30s for identical requests.packages/cli/src/lib/ghcr.ts uses AbortSignal.timeout(timeout) and distinguishes external cancellation with Boolean(externalSignal?.aborted && error.name === "AbortError"), relying on abort state and error type rather than runtime-dependent error-message text.packages/cli/src/lib/ghcr.ts contains fetchWithRetry(), which loops from attempt 0 through GHCR_MAX_RETRIES, and reports exhausted failures as ${context}: ${lastError?.message ?? "unknown error"}.packages/cli/src/lib/ghcr.ts defines GHCR_REGISTRY = "https://ghcr.io" and imports getUserAgent from ./constants.js.Authorization header because forwarding it to signed blob-storage URLs can invalidate signed query-string credentials and produce HTTP 404.packages/cli/src/lib/ghcr.ts exposes GHCR operations including getAnonymousToken(), fetchManifest(), fetchNightlyManifest(), getNightlyVersion(), findLayerByFilename(), downloadNightlyBlob(), listTags(), and downloadLayerBlob().packages/cli/test/lib/ghcr.test.ts is a 558-line Vitest suite for GHCR/OCI download helpers; it mocks globalThis.fetch through mockFetch() and restores the original fetch in afterEach(), avoiding real network access.makeManifest() creates an OCI manifest with schemaVersion: 2, media type application/vnd.oci.image.manifest.v1+json, config digest sha256:config, config media type application/vnd.oci.empty.v1+json, and config size 2.makeManifest() defines two layers in order: 1. digest sha256:abc123, size 1000, title sentry-linux-x64.gz; 2. digest sha256:def456, size 1200, title sentry-darwin-arm64.gz. Both use media type application/octet-stream.makeManifest() annotations include version: "0.0.0-dev.1740000000" and "org.opencontainers.image.source": "https://github.com/getsentry/cli".getAnonymousToken() tests verify the URL contains https://ghcr.io/token?scope=repository:${GHCR_REPO}:pull, a successful response returns "test-token-abc", HTTP 401 becomes UpgradeError("GHCR token exchange failed: HTTP 401"), network TypeError("fetch failed") becomes UpgradeError("Failed to connect to GHCR: fetch failed"), and a successful JSON response lacking token becomes UpgradeError("GHCR token exchange returned no token").fetchNightlyManifest("my-token") is tested to request /v2/${GHCR_REPO}/manifests/${GHCR_TAG} with Authorization: Bearer my-token and Accept: application/vnd.oci.image.manifest.v1+json.fetchNightlyManifest() error tests map HTTP 404 to Failed to fetch manifest for tag "nightly": HTTP 404 and TypeError("fetch failed") to Failed to fetch manifest for tag "nightly": fetch failed, both as UpgradeError.getNightlyVersion() extracts "0.0.0-dev.1740000000" from manifest annotations; missing or absent annotations produce UpgradeError, with the missing-version case asserting "Nightly manifest has no version annotation".findLayerByFilename() resolves sentry-linux-x64.gz → sha256:abc123 and sentry-darwin-arm64.gz → sha256:def456; requesting sentry-freebsd-x64.gz produces UpgradeError("No nightly build found for sentry-freebsd-x64.gz"), and a layer without annotations also produces UpgradeError.downloadNightlyBlob() tests cover a direct HTTP 200 response and HTTP 307/302 redirects. For a 307 redirect to https://blob.storage.azure.com/signed?token=xyz, exactly 2 requests are expected and the second request must omit the authorization header.downloadNightlyBlob() error behavior tested is: redirect without Location → GHCR blob redirect (307) had no Location header; redirected storage HTTP 403 → Blob storage download failed: HTTP 403; initial HTTP 500 → Unexpected GHCR blob response: HTTP 500; initial network failure → Failed to connect to GHCR: fetch failed; redirect-follow network failure → Failed to download from blob storage: fetch failed. Each is an UpgradeError.fetchManifest() tests use arbitrary tags such as "patch-0.13.0" and "some-tag"; HTTP 404 for "patch-0.13.0" becomes Failed to fetch manifest for tag "patch-0.13.0": HTTP 404, while a network failure for "some-tag" becomes Failed to fetch manifest for tag "some-tag": fetch failed.listTags() tests verify an unfiltered result ["nightly", "patch-0.13.0", "patch-0.14.0"], prefix "patch-" filtering to ["patch-0.13.0", "patch-0.14.0"], an empty array when no tags match, and an empty array when the response has no tags field.listTags() maps HTTP 500 to UpgradeError("Failed to list GHCR tags: HTTP 500") and TypeError("fetch failed") to UpgradeError("Failed to list GHCR tags: fetch failed").listTags() pagination is tested with a full first page of exactly 100 tags (tag-000 through tag-099) and a second page containing tag-100 and tag-101; the result has exactly 102 tags and exactly 2 page calls.listTags("token", "patch-") prefix filtering is tested against 80 mixed tags: the first 40 are patch-0 through patch-39, the remaining 40 are nightly-40 through nightly-79, and exactly 40 matching tags are returned.downloadLayerBlob("token", "sha256:abc123") is tested to request /v2/${GHCR_REPO}/blobs/sha256:abc123, follow a 307 redirect to https://blob.storage.azure.com/file, and return an ArrayBuffer; a network failure must reject with UpgradeError.packages/cli/src/lib/version-check.ts imports fetchLatestFromGitHub and fetchLatestNightlyVersion from ./upgrade.js; its version-check path selects fetchLatestNightlyVersion(signal) for nightly builds and fetchLatestFromGitHub(signal) otherwise.packages/cli/src/commands/cli/upgrade.ts imports/uses versionExists, calling versionExists(lookupMethod, target) around line 320; a comment around line 458 references CLI-1D3 and the visibility-race retry loop in downloadBinaryToTemp.packages/cli/src/lib/upgrade.ts defines fetchLatestFromGitHub() around line 375, fetchLatestNightlyVersion() around line 443, nightlyVersionExists() around line 498, versionExists() around line 527, streamDecompressToFile() around line 576, downloadNightlyToPath() around line 683, downloadStableToPath() around line 716, waitForBinaryVisible() around line 805, downloadBinaryToTemp() around line 856, and downloadFullBinary() around line 959.versionExists() routes versions recognized by isNightlyVersion(version) to nightlyVersionExists(version); stable-version lookup uses GitHub release URLs including ${GITHUB_RELEASES_URL}/tags/${version}.packages/cli/src/lib/upgrade.ts derives the compressed nightly artifact filename as ${getPlatformBinaryName()}.gz.downloadFullBinary() selects downloadNightlyToPath(destPath, version, setMessage) for nightly versions and downloadStableToPath(downloadTag ?? version, destPath, setMessage) otherwise.packages/cli/src/lib/upgrade.ts use GHCR’s OCI blob protocol via downloadNightlyToPath(), while stable full-binary downloads use downloadStableToPath() and the GitHub URL from getBinaryDownloadUrl(version).downloadBinaryToTemp() returns both a binary path and lock path, calls downloadFullBinary(version, downloadTag, tempPath, setMessage), and then validates visibility/size with waitForBinaryVisible(tempPath).packages/cli/src/lib/delta-upgrade.ts prevents delta upgrades across release channels by comparing isNightlyVersion(CLI_VERSION) with isNightlyVersion(targetVersion); it labels the target channel "nightly" or "stable" from the same predicate.packages/cli/src/lib/delta-upgrade.ts requests GitHub releases with ${GITHUB_RELEASES_URL}?per_page=12 and uses getPlatformBinaryName() when identifying delta-upgrade binaries.packages/cli/src/lib/release-notes.ts fetches release data from ${GITHUB_RELEASES_URL}?per_page=${CHANGELOG_MAX_RELEASES}.