Dashboard › cli › Distillation
0534536a-f2ed-434f-a0c4-9801726bce10["lore_tm_v1_DUl0dtC7t4IqWgnP58uIaY-9PgpbPCkLrplspL67ErY","lore_tm_v1_SChsB-mpePAPZuAM7r12T5pHRdEOriM8sZSqT2BQwBA","lore_tm_v1_QmYXSfHw86BsR28pg95CcHIPtiJnlDCMqMsMUM7vC3I","lore_tm_v1_HyLaxNvq9CJmjWeJ83tsVxWjAN2n7yrtXkXAsibI5NE","lore_tm_v1_mXNf4UsiSgVEOYyN7pSAP5dqWe0gklld4mZSmDYnGbU","lore_tm_v1_KHbij4ETeqaNRtGmK3NlzqRYCkk_v8FX3Gce3lqMyT0","lore_tm_v1_vgvRATe7yPTJeHVEItkCO4IDxMdd0CfuYwuCFdWcSpk","lore_tm_v1_gv95lksN8RChcVdEOsWD98I6BesdNjizXHXnKEn_5Wg"]
packages/cli/src/lib/ghcr.ts is a GHCR OCI client for nightly CLI binaries. Nightly artifacts are public, use anonymous token exchange, and store the nightly version in the manifest-level annotations.version; latest-version discovery requires 2 HTTP requests: token exchange and manifest fetch.packages/cli/src/lib/ghcr.ts documents a GHCR redirect quirk: blob requests return HTTP 307 to Azure Blob Storage, and automatically following the redirect would forward the Authorization header, causing Azure to return 404; redirects must therefore be followed manually without authorization.GHCR_REQUEST_TIMEOUT = 10_000, GHCR_MAX_RETRIES = 1, and GHCR_BLOB_TIMEOUT = 30_000.isRetryableError(error) treats errors named "TimeoutError" or "AbortError" as retryable and also matches lowercase messages containing "timeout", "econnreset", "econnrefused", "network", or "fetch failed". HTTP errors are handled after a Response and are not retry conditions in this helper.buildSignal(timeout, externalSignal?) creates AbortSignal.timeout(timeout) and combines it with an external signal using AbortSignal.any([timeoutSignal, externalSignal]); isExternalAbort(error, externalSignal?) requires both externalSignal.aborted and error.name === "AbortError".fetchWithRetry(url, init, context, options?) defaults to GHCR_REQUEST_TIMEOUT, performs at most 2 attempts because its loop runs from 0 through GHCR_MAX_RETRIES = 1, overrides init.signal with the combined signal, immediately stops on caller cancellation, retries only retryable timeout/network errors, and ultimately throws UpgradeError("network_error", \${context}: ${lastError?.message ?? "unknown error"}`)`.126ms to 30s for identical requests; a short timeout plus retry is intended to cap the worst case around 20s and recover when the first request reaches a cold instance.GHCR_REPO = "getsentry/cli", GHCR_TAG = "nightly", GHCR_REGISTRY = "https://ghcr.io", and OCI_MANIFEST_TYPE = "application/vnd.oci.image.manifest.v1+json".OciLayer contains digest: string, mediaType: string, size: number, and optional annotations?: Record<string, string>. OciManifest contains schemaVersion: number, optional mediaType, optional config?: OciLayer, layers: OciLayer[], and optional manifest-level annotations?: Record<string, string>.getAnonymousToken(signal?) requests ${GHCR_REGISTRY}/token?scope=repository:${GHCR_REPO}:pull with "User-Agent": getUserAgent() through fetchWithRetry(). Non-OK responses throw GHCR token exchange failed: HTTP ${response.status}, missing tokens throw GHCR token exchange returned no token, and success returns the JSON token.fetchManifest(token, tag, signal?) requests ${GHCR_REGISTRY}/v2/${GHCR_REPO}/manifests/${tag} with Authorization: Bearer ${token}, Accept: OCI_MANIFEST_TYPE, and the user agent. A non-OK response throws Failed to fetch manifest for tag "${tag}": HTTP ${response.status}; success parses and returns OciManifest.fetchNightlyManifest(token) delegates to fetchManifest(token, GHCR_TAG).getNightlyVersion(manifest) reads manifest.annotations?.version; if absent, it throws UpgradeError("network_error", "Nightly manifest has no version annotation").findLayerByFilename(manifest, filename) matches layer.annotations?.["org.opencontainers.image.title"] === filename; if no layer matches, it throws UpgradeError("version_not_found", \No nightly build found for ${filename}`)`.downloadNightlyBlob(token, digest, signal?) first requests ${GHCR_REGISTRY}/v2/${GHCR_REPO}/blobs/${digest} with bearer authorization, user agent, redirect: "manual", and buildSignal(GHCR_BLOB_TIMEOUT, signal). Connection errors become UpgradeError("network_error", \Failed to connect to GHCR: ${msg}`)`.downloadNightlyBlob() returns the first response directly for HTTP 200; it recognizes redirects 301, 302, 307, and 308, requires a Location header, and otherwise throws either GHCR blob redirect (${status}) had no Location header or Unexpected GHCR blob response: HTTP ${status}.downloadNightlyBlob() requests the redirect URL with only "User-Agent": getUserAgent() and the callerβs raw signal, intentionally omitting both Authorization and AbortSignal.timeout(). The rationale is that a timeout would cover body streaming too, and a full nightly binary of approximately 30 MB under a 30s timeout would require sustained throughput around 8 Mbps; the first GHCR request retains its timeout while Azure is considered latency-reliable.Failed to download from blob storage: ${msg}; non-OK Azure responses become Blob storage download failed: HTTP ${redirectResponse.status}.TAGS_PAGE_SIZE = 100. fetchTagPage(token, lastTag?, signal?) requests ${GHCR_REGISTRY}/v2/${GHCR_REPO}/tags/list?n=100, appending &last=${encodeURIComponent(lastTag)} when present, and returns data.tags ?? []; a non-OK response throws Failed to list GHCR tags: HTTP ${response.status}.listTags(token, prefix?, signal?) repeatedly calls fetchTagPage(), adds only tags beginning with prefix when supplied, stops for an empty page or a page shorter than 100, and otherwise paginates using tags.at(-1).downloadLayerBlob(token, digest, signal?) delegates to downloadNightlyBlob() and returns response.arrayBuffer(); it is intended for fully buffered patch payloads around 50β500 KB.packages/cli/src/lib/binary.ts, packages/cli/test/lib/binary.test.ts, packages/cli/test/lib/ghcr.test.ts, and packages/cli/test/lib/install-script.test.ts.0e661a5f5 fix(org): log region URL parse failures in org list (#1539); 2. 9d0901b10 feat(local): Add agent debugging stream (#1538); 3. 72c307f47 fix(cli): log UID resolution failures in sentry cli fix (#1541); 4. e710ce905 fix(telemetry): log process-tree walk failures in agent detection (#1542); 5. 6e3e7e13a feat(config): follow XDG Base Directory spec for config location (#1503).Response, so it never repeats the request; only HTTP 404 advances to the next source, while every other HTTP or network failure aborts immediately.packages/cli/test/lib/binary.test.ts coverage verifies exact UPGRADE_SOURCES ordering, first-source success, legacy fallback only after HTTP 404, no fallback for HTTP 401, 403, 429, or 500, no fallback after TypeError("fetch failed"), and failure after both sources return 404. Expected probe URLs are Toolkit first (https://api.github.com/repos/getsentry/toolkit/releases/latest) and legacy CLI second (https://api.github.com/repos/getsentry/cli/releases/latest).packages/cli/test/lib/ghcr.test.ts cases expect GHCR functions to accept a selected source: getAnonymousToken(UPGRADE_SOURCES[0]) must request a scope containing scope=repository:getsentry/toolkit:pull, and fetchNightlyManifest("token", undefined, UPGRADE_SOURCES[0]) must request /v2/getsentry/toolkit/manifests/nightly.packages/cli/src/lib/ghcr.ts implementation still has hardcoded GHCR_REPO = "getsentry/cli" and signatures getAnonymousToken(signal?) and fetchNightlyManifest(token), while the modified tests expect source-aware signatures and getsentry/toolkit; this indicates an in-progress implementation/test mismatch.packages/cli/test/lib/install-script.test.ts coverage reads the install script, extracts UPGRADE_SOURCES=(...), splits it on whitespace, and requires exact equality with UPGRADE_SOURCES.map((source) => source.githubRepo).packages/cli/test/lib/upgrade.test.ts, packages/cli/test/commands/cli/upgrade.test.ts, packages/cli/test/lib/delta-upgrade.test.ts, packages/cli/test/lib/delta-upgrade.mocked.test.ts, packages/cli/test/e2e/delta-upgrade.test.ts, packages/cli/test/lib/version-check.test.ts, packages/cli/test/lib/release-notes.test.ts, and packages/cli/test/lib/release-notes.property.test.ts.packages/cli/test/lib/upgrade.test.ts coverage references downloadBinaryToTemp, executeUpgrade, fetchLatestFromGitHub, fetchLatestVersion, and versionExists; it includes stable GitHub behavior, nightly GHCR manifest discovery, nightly-version existence checks, curl nightly download/decompression, offline errors, download-integrity verification, and installation methods including curl, npm, pnpm, bun, yarn, brew, and unknown.