Dashboard › cli › Distillation
75de5e74-478c-409c-a9cc-74d6a6a7284c["lore_tm_v1_qFYIGgRlrMA_gR59wOVkey_RRs5M5U-hSjJcqATTmWA","lore_tm_v1_e7ARNDFzJ7BKJvJSdaRL6utv1ikL7syoQLRFB0Kv94Q","lore_tm_v1_IjzC09ENpcVP0ayjcgXGQ94m24TqqMwL9ikb6af0T0U","lore_tm_v1_3tKE5AbzlCSltdI9t5GNY_7dOLtyHSqpxQL_WHOAmks","lore_tm_v1_USiv8QEAVotSwWgGUxXiCJqALciotSj9VyYMOv5Ibh0","lore_tm_v1_H0etraJs0mu-yuJvM5Q4PBjBJWzGrLSmYiO-tXuJAmk"]
packages/cli/src/lib/ghcr.ts is a 520-line GHCR/OCI client for fetching nightly CLI binaries from public package ghcr.io/getsentry/cli; exported constants are GHCR_REPO = "getsentry/cli" and GHCR_TAG = "nightly", while private constants are GHCR_REGISTRY = "https://ghcr.io", OCI_MANIFEST_TYPE = "application/vnd.oci.image.manifest.v1+json", GHCR_REQUEST_TIMEOUT = 10_000, GHCR_MAX_RETRIES = 1, GHCR_BLOB_TIMEOUT = 30_000, and TAGS_PAGE_SIZE = 100.packages/cli/src/lib/ghcr.ts: public nightlies use an anonymous bearer-token exchange; the nightly version comes from OCI manifest annotations.version, making version discovery 2 HTTP requests (token exchange plus manifest fetch); each binary is a separate OCI layer identified by org.opencontainers.image.title; GHCR blob requests may redirect to Azure Blob Storage and must be followed manually without forwarding the Authorization header because Azure otherwise returns HTTP 404.OciLayer in packages/cli/src/lib/ghcr.ts has required digest: string, mediaType: string, and size: number, plus optional annotations?: Record<string, string>; OciManifest has required schemaVersion: number and layers: OciLayer[], plus optional mediaType, config, and manifest-level annotations.packages/cli/src/lib/ghcr.ts: isRetryableError() retries TimeoutError, AbortError, and messages containing timeout, econnreset, econnrefused, network, or fetch failed; buildSignal() combines AbortSignal.timeout(timeout) with an external signal through AbortSignal.any(); fetchWithRetry() makes at most 2 attempts because its loop is attempt <= GHCR_MAX_RETRIES, immediately stops retrying caller-triggered external AbortError, defaults to a 10,000 ms timeout, and wraps exhausted failures as UpgradeError("network_error", \${context}: ${message}`)`.getAnonymousToken(signal?) requests https://ghcr.io/token?scope=repository:getsentry/cli:pull with User-Agent: getUserAgent(), uses fetchWithRetry(), returns the JSON token, maps a non-OK response to UpgradeError("network_error", "GHCR token exchange failed: HTTP <status>"), and maps a missing token field to UpgradeError("network_error", "GHCR token exchange returned no token").fetchManifest(token, tag, signal?) requests https://ghcr.io/v2/getsentry/cli/manifests/${tag} through fetchWithRetry() with Authorization: Bearer ${token}, Accept: application/vnd.oci.image.manifest.v1+json, and User-Agent; non-OK responses become UpgradeError("network_error", "Failed to fetch manifest for tag \"${tag}\": HTTP ${status}"). fetchNightlyManifest(token) is a convenience wrapper calling fetchManifest(token, GHCR_TAG) and does not expose a signal parameter.getNightlyVersion(manifest) returns manifest.annotations?.version; absence produces UpgradeError("network_error", "Nightly manifest has no version annotation"). findLayerByFilename(manifest, filename) matches layer.annotations?.["org.opencontainers.image.title"]; absence produces UpgradeError("version_not_found", \No nightly build found for ${filename}`)`.downloadNightlyBlob(token, digest, signal?) first calls customFetch() on https://ghcr.io/v2/getsentry/cli/blobs/${digest} with bearer authorization, user agent, redirect: "manual", and a combined 30,000 ms timeout/external signal. It returns HTTP 200 directly; manually handles HTTP 301, 302, 307, and 308; rejects a redirect lacking Location; follows the signed redirect using only User-Agent and the external signal; and intentionally applies no timeout to the redirected body stream because a full nightly is approximately 30 MB and a 30-second limit would require sustained approximately 8 Mbps.downloadNightlyBlob() failures are: first-request exception β Failed to connect to GHCR: ${msg}; redirect without Location β GHCR blob redirect (${status}) had no Location header; redirected-fetch exception β Failed to download from blob storage: ${msg}; non-OK redirected response β Blob storage download failed: HTTP ${status}; and any initial status outside 200/301/302/307/308 β Unexpected GHCR blob response: HTTP ${status}. All are UpgradeError with code "network_error".fetchTagPage(token, lastTag?, signal?), requesting /v2/getsentry/cli/tags/list?n=100 and appending &last=${encodeURIComponent(lastTag)} after a full page. listTags(token, prefix?, signal?) accumulates tags, optionally keeps only tag.startsWith(prefix), stops on an empty or shorter-than-100 page, and otherwise uses tags.at(-1) as the next cursor. downloadLayerBlob(token, digest, signal?) calls downloadNightlyBlob() and returns its fully buffered response.arrayBuffer(); it is intended for patch files sized approximately 50β500 KB.packages/cli/test/lib/ghcr.test.ts is a 558-line Vitest suite that stores/restores globalThis.fetch around each test and uses a fixture OCI manifest with schemaVersion: 2, version 0.0.0-dev.1740000000, layers sentry-linux-x64.gz (sha256:abc123, 1000 bytes) and sentry-darwin-arm64.gz (sha256:def456, 1200 bytes), plus source annotation https://github.com/getsentry/cli.getAnonymousToken() tests in packages/cli/test/lib/ghcr.test.ts cover token "test-token-abc" from a successful response, HTTP 401 yielding GHCR token exchange failed: HTTP 401, TypeError("fetch failed") yielding Failed to connect to GHCR: fetch failed, and HTTP 200 JSON {} yielding GHCR token exchange returned no token.packages/cli/test/lib/ghcr.test.ts verify: nightly requests target /manifests/nightly with Authorization: Bearer my-token and OCI manifest Accept; arbitrary tag patch-0.13.0 works; HTTP 404 and network failures include the requested tag in their errors; getNightlyVersion() extracts 0.0.0-dev.1740000000 and rejects missing annotations/version; findLayerByFilename() resolves both Linux and Darwin fixture layers and rejects sentry-freebsd-x64.gz with No nightly build found for sentry-freebsd-x64.gz.packages/cli/test/lib/ghcr.test.ts verify direct HTTP 200 bytes, manual HTTP 307 and 302 redirects, exactly 2 requests for a redirected download, omission of Authorization from the second request, missing-Location failure GHCR blob redirect (307) had no Location header, redirected HTTP 403 failure, unexpected initial HTTP 500 failure, initial network failure, redirect-follow network failure, and downloadLayerBlob() returning bytes [1, 2, 3, 4, 5] as an ArrayBuffer.listTags() tests in packages/cli/test/lib/ghcr.test.ts cover unfiltered tags ["nightly", "patch-0.13.0", "patch-0.14.0"], "patch-" filtering, no matches, missing tags field, HTTP 500, and network failure. Pagination coverage creates exactly 100 first-page tags tag-000 through tag-099, then tag-100 and tag-101, expecting 102 results and exactly 2 page calls; prefix filtering over 80 mixed tags expects exactly 40 patch- results.packages/cli/src/lib/upgrade.ts owns stable/nightly version lookup and full-binary download integration: fetchLatestFromGitHub() near line 375, fetchLatestNightlyVersion() near line 443, nightlyVersionExists() near line 498, versionExists() near line 527, downloadNightlyToPath() near line 683, downloadStableToPath() near line 716, downloadBinaryToTemp() near line 856, and downloadFullBinary() near line 959; stable downloads use getBinaryDownloadUrl(), while nightlies use GHCR.packages/cli/src/lib/delta-upgrade.ts imports GITHUB_RELEASES_URL and GHCR_REPO, uses GITHUB_RELEASES_URL for release requests including ?per_page=12, and uses the named GHCR_REPO import as the single source of truth for ghcr.io/getsentry/cli.packages/ directory contains exactly one entry: cli/.