Dashboard › cli › Distillation
9dc21796-50ae-41da-b8fb-205cdb794634["lore_tm_v1_cZ4Qvmx2U3CXtSZr7Fpu_GtOpWS12EbCW5nwF2PBlUo","lore_tm_v1__OvVRNg2gpttEbKC7Exz1pPyhkg6Ldjs_i67XRXzGrE","lore_tm_v1_1T7QbCPdzvZ9abw_XwTz2x0b7tMruBFi9J-D-hE09ko","lore_tm_v1_jjgEwGcGc_3Tq72f7mVpAtf6KdzBh7UxyBjK8mlAWyY","lore_tm_v1_vIxA0aeiJ9lxoQB_W89iRPIYsdRiPOOpk5FVtAJUQb0","lore_tm_v1_EvdRXli6HEHf_cg9rhRBxrHaCouq2iNmLstW-BffJrU","lore_tm_v1_IMuB__HRKYK9Ls-gGeHk3XyyFS5x2BBapVqj8GaAPvM"]
packages/cli/src/lib/ghcr.ts defines GHCR networking with GHCR_REQUEST_TIMEOUT = 10_000, GHCR_MAX_RETRIES = 1 (up to 2 attempts), and GHCR_BLOB_TIMEOUT = 30_000. fetchWithRetry() retries only timeout/abort and network-style failures (timeout, econnreset, econnrefused, network, fetch failed); caller-triggered external aborts propagate without retry.packages/cli/src/lib/ghcr.ts uses GHCR_REPO = "getsentry/cli", GHCR_TAG = "nightly", registry https://ghcr.io, OCI manifest type application/vnd.oci.image.manifest.v1+json, and tag pagination with TAGS_PAGE_SIZE = 100.downloadNightlyBlob() in packages/cli/src/lib/ghcr.ts manually handles GHCR blob responses: direct HTTP 200 is returned; 301/302/307/308 redirects require a Location header and are followed without Authorization because forwarding the bearer token to signed Azure Blob Storage URLs produces HTTP 404. The GHCR request has a 30-second timeout; the Azure streaming request has only the caller’s optional signal so slow approximately 30 MB downloads are not forced to sustain approximately 8 Mbps.packages/cli/src/lib/ghcr.ts exposes getAnonymousToken(), fetchManifest(), fetchNightlyManifest(), getNightlyVersion(), findLayerByFilename(), downloadNightlyBlob(), listTags(), and downloadLayerBlob(). OCI layer lookup uses annotation org.opencontainers.image.title; nightly version discovery uses manifest annotation version.packages/cli/src/lib/delta-upgrade.ts now bridges CLI delta upgrades to binpatch, importing applyPatchChainInMemory, chain extraction/filtering/validation helpers, ghcrSource, githubReleaseSource, makeCache, OciClient, resolveAndApply, MAX_NIGHTLY_CHAIN_DEPTH, PATCH_TAG_PREFIX, and SIZE_THRESHOLD_RATIO, while re-exporting the existing public types/helpers to preserve the CLI API.packages/cli/src/lib/delta-upgrade.ts, using named import GHCR_REPO from ./ghcr.js maintains a single source of truth and avoids a prior silent HTTP 404 caused by a string-literal repository mismatch.makeCache(join(getConfigDir(), "patch-cache")); cache keys are patch-chain:${fromVersion}-${toVersion}. instrumentCache() emits cache.get/cache.put spans with cache.key, cache.hit, and cache.item_size.packages/cli/src/lib/delta-upgrade.ts are: stable via githubReleaseSource() using GITHUB_RELEASES_URL; nightly via ghcrSource() using https://ghcr.io, GHCR_REPO, and target tags nightly-${version}. Both use getPlatformBinaryName(), customFetch, compareVersions where applicable, tracing instrumentation, and user agent sentry-cli/${CLI_VERSION}.canAttemptDelta(targetVersion) returns false when CLI_VERSION === "0.0.0-dev", when current and target versions belong to different stable/nightly channels, or when the target is a downgrade.fetchRecentReleases() in packages/cli/src/lib/delta-upgrade.ts requests ${GITHUB_RELEASES_URL}?per_page=12; network failures, non-2xx responses, or non-array JSON yield [], with debug logging for malformed/failing responses.resolveNightlyChain() retains a CLI-local validateChainStep() adapter instead of relying solely on binpatchValidateChainStep() so telemetry preserves 3 distinct failure reasons: version-mismatch, missing-layer, and size-exceeded; binpatch’s native result is coarser (malformed or over_budget).resolveNightlyChain() lists PATCH_TAG_PREFIX tags through OciClient, rejects empty chains and chains longer than MAX_NIGHTLY_CHAIN_DEPTH, fetches manifests concurrently, looks for ${getPlatformBinaryName()}.patch, enforces remaining budget as fullGzSize * SIZE_THRESHOLD_RATIO - totalSize, validates every fromVersion, and takes the final expected digest from getPatchTargetSha256().applyPatchChain() calls applyPatchChainInMemory(oldBinaryPath, chain.patches.map((patch) => patch.data), destPath, onBytes) and verifies its returned SHA-256 against chain.expectedSha256; mismatch throws SHA-256 mismatch after patching: got ${sha256}, expected ${chain.expectedSha256}.makeProgressHandler() uses byte formatting for pre-apply phases such as download/read, but percent-only formatting for apply. Root issue: multi-hop apply totals sum every hop’s newSize, which could display 930 MB for a 310 MB installation and mislead users.telemetry() captures the resolved source and emits delta.source; values include the actual source and offline_miss. This captured value is intended to preserve network/cache/offline attribution when patch application throws after successful chain resolution.attemptDeltaUpgrade() records delta.from_version, delta.to_version, channel, patch bytes, and chain length; emits Sentry distributions upgrade.delta.patch_bytes and upgrade.delta.chain_length; captures failures at warning level; logs Delta upgrade failed (...), falling back to full download; marks the span error; and returns null to trigger full-download fallback.prefetchNightlyPatches() and prefetchStablePatches() resolve a chain only when delta is eligible and the signal is not aborted, then save chains with steps into the patch cache.packages/cli/src/lib/release-notes.ts sets CHANGELOG_MAX_RELEASES = 30, versus fetchRecentReleases()’s 12-release delta cap. The 30-release request is intended to cover approximately 6+ months of weekly stable releases and larger version jumps while staying below GitHub’s per_page=100 maximum.prefetchedReleases; otherwise fetchReleasesForChangelog() requests ${GITHUB_RELEASES_URL}?per_page=30. Network errors, non-2xx responses, invalid JSON, and non-array responses produce no changelog rather than failing the upgrade.packages/cli/src/lib/release-notes.ts derive Unix timestamps from versions shaped like X.Y.Z-dev.<unix-seconds> and call https://api.github.com/repos/getsentry/cli/commits?sha=main&since=${sinceDate}&until=${untilDate}&per_page=100. Because GitHub since is inclusive and until is exclusive, both boundaries use timestamp +1s to exclude the installed version’s commit and include the target version’s commit.fetchChangelog() dispatches by channel: "stable" | "nightly", accepts fromVersion, toVersion, optional maxItems, and optional stable-only prefetchedReleases, and is best-effort: all failures are debug-logged and return null. It is designed to run in parallel with binary download so changelog retrieval adds zero upgrade latency.