Dashboard › cli › Distillation
58be756d-985a-4050-8527-4087b8796ac6["lore_tm_v1_KumF6RETOclT_DIW9S9Vffi_O7SzMv9a1irjn2UObZ0","lore_tm_v1_ndczshfRBOKJGQV4JjOhDvG2CgRFBtUNDs9-KH9RdI4","lore_tm_v1_aK2F1BUVBlS331nDaKx0fYzC2gaR2M9wBNo0SXemVbs","lore_tm_v1_654DH6bYwWWG2FOHgAwoDOsQp9vU_5GjDFmkQYT_7UM"]
π΄ (20:41) packages/cli/src/lib/upgrade.ts installation-method detection first returns getInstallInfo().method when present; otherwise it calls detectLegacyInstallationMethod() for pre-setup installs.
π΄ (20:41) When legacy installation detection returns anything other than "unknown", packages/cli/src/lib/upgrade.ts best-effort persists { method: legacyMethod, path: process.execPath, version: CLI_VERSION } via setInstallInfo(); read-only or broken DB errors are swallowed and logged as Failed to persist install info (DB may be read-only).
π΄ (20:41) fetchLatestFromGitHub(signal?) in packages/cli/src/lib/upgrade.ts calls fetchWithUpgradeError(${GITHUB_RELEASES_URL}/latest, { headers: getGitHubHeaders(), signal }, "GitHub"), maps a non-OK response to UpgradeError("network_error", \Failed to fetch from GitHub: ${response.status}`), and maps a missing JSON tag_nametoUpgradeError("network_error", "No version found in GitHub release")`.
π΄ (20:41) fetchLatestFromGitHub(signal?) returns data.tag_name.replace(VERSION_PREFIX_REGEX, ""), stripping the version prefix from the latest GitHub release tag.
π΄ (20:41) downloadFullBinary(version, downloadTag, destPath, setMessage?) in packages/cli/src/lib/upgrade.ts routes nightly versions to downloadNightlyToPath(destPath, version, setMessage) and stable versions to downloadStableToPath(downloadTag ?? version, destPath, setMessage).
π΄ (20:41) executeUpgradeHomebrew() runs brew upgrade getsentry/tools/sentry with inherited stdio and shell: process.platform === "win32"; it intentionally ignores a requested version because Homebrewβs tap formula controls the version and cannot pin an arbitrary release.
π΄ (20:41) executeUpgradeHomebrew() resolves only on exit code 0; other close codes produce UpgradeError("execution_failed", \brew upgrade failed with exit code ${code}`), while spawn errors produce UpgradeError("execution_failed", `brew failed: ${err.message}`)`.
π΄ (20:41) packages/cli/src/lib/delta-upgrade.ts delegates delta discovery/application to binpatch, importing applyPatchChainInMemory, extractStableChain, filterAndSortChainTags, validateChainStep, getPatchFromVersion, getPatchTargetSha256, ghcrSource, githubReleaseSource, MAX_NIGHTLY_CHAIN_DEPTH, makeCache, OciClient, PATCH_TAG_PREFIX, resolveAndApply, and SIZE_THRESHOLD_RATIO.
π΄ (20:41) packages/cli/src/lib/delta-upgrade.ts preserves its existing public API by re-exporting ExtractStableChainOpts, GitHubAsset, GitHubRelease, PatchChain, StableChainInfo, extractSha256, getPatchFromVersion, getPatchTargetSha256, getStableTargetSha256, and PATCH_TAG_PREFIX from binpatch.
π΄ (20:41) DeltaResult in packages/cli/src/lib/delta-upgrade.ts contains exact fields sha256: string, patchBytes: number, and chainLength: number.
π΄ (20:41) Delta GHCR configuration imports GHCR_REPO from ./ghcr.js as a single source of truth for ghcr.io/getsentry/cli, avoiding the silent HTTP 404 previously caused by a string literal.
π΄ (20:41) Delta HTTP instrumentation uses const instrument: InstrumentHook = (name, fn) => withTracing(name, "http.client", fn).
π΄ (20:41) Delta patch-cache keys use exact format patch-chain:${fromVersion}-${toVersion}.
π΄ (20:41) instrumentCache(base) wraps PatchCache.load() in a cache.get tracing span, records cache.key as [key], records cache.hit as whether the result is non-null, and records cache.item_size=result.totalSize on hits.
π΄ (20:41) instrumentCache(base) wraps nonempty PatchCache.save(chain, steps) calls in a cache.put tracing span keyed from the first stepβs fromVersion through the last stepβs toVersion; cache.item_size is the sum of every chain.patches[].size.
π΄ (20:41) If instrumentCache(base).save() has no first or last step, it directly calls base.save(chain, steps) without tracing; cleanup() and clear() directly delegate to the base cache.
π΄ (20:41) getPatchCache() creates an instrumented binpatch cache at join(getConfigDir(), "patch-cache").
π΄ (20:41) stableSource() configures githubReleaseSource() with releasesUrl: GITHUB_RELEASES_URL, binaryName: getPlatformBinaryName(), user agent sentry-cli/${CLI_VERSION}, fetch: customFetch, and the HTTP instrument hook.
π΄ (20:41) nightlySource() configures ghcrSource() with registry https://ghcr.io, repo: GHCR_REPO, binaryName: getPlatformBinaryName(), target tags formatted as nightly-${version}, compareVersions, user agent sentry-cli/${CLI_VERSION}, fetch: customFetch, and the HTTP instrument hook.
π΄ (20:41) canAttemptDelta(targetVersion) rejects delta upgrades when CLI_VERSION === "0.0.0-dev", when current and target versions differ in nightly/stable classification, or when isDowngrade(CLI_VERSION, targetVersion) is true; otherwise it permits the attempt.
π΄ (20:41) fetchRecentReleases(signal?) requests ${GITHUB_RELEASES_URL}?per_page=12 through customFetch, with Accept: application/vnd.github.v3+json, user agent sentry-cli/${CLI_VERSION}, and the optional abort signal.
π΄ (20:41) fetchRecentReleases(signal?) returns [] for non-OK responses, non-array JSON, or thrown errors; malformed JSON shape logs GitHub releases response is not an array with typeof data, while exceptions log Failed to fetch recent releases from GitHub.
π΄ (20:41) downloadStablePatch(url, signal?) uses customFetch with user agent sentry-cli/${CLI_VERSION} and the optional signal; an OK response becomes new Uint8Array(await response.arrayBuffer()), while non-OK responses or exceptions return null, with exceptions logged as Failed to download stable patch.
π΄ (20:41) Local extractStableChain(opts) adapts binpatchExtractStableChain(opts) to the existing nullable API, returning null when the binpatch result contains "failure" and otherwise returning the result.
π΄ (20:41) Local filterAndSortChainTags(allTags, currentVersion, targetVersion) delegates to binpatchFilterAndSortChainTags(allTags, currentVersion, targetVersion, compareVersions).
π΄ (20:41) Local ChainStepResult preserves three rich failure variants: { reason: "version-mismatch", expected, actual }, { reason: "missing-layer", layerName }, and { reason: "size-exceeded", layerSize, budget }; success is { ok: true, digest, size }.
π΄ (20:41) Local validateChainStep(manifest, opts) first compares getPatchFromVersion(manifest) with opts.expectedFrom; mismatch returns the exact expected version and actual version, where actual may be null.
π΄ (20:41) After the from-version check, local validateChainStep() delegates to binpatchValidateChainStep(manifest, opts); a binpatch failure is classified as "size-exceeded" if the requested patch layer exists, preserving its layer.size and opts.sizeLimit, or "missing-layer" if it does not.
π΄ (20:41) The local validateChainStep() wrapper is retained instead of using binpatchβs result directly so telemetry keeps the three reasons "version-mismatch", "missing-layer", and "size-exceeded" rather than binpatchβs coarser "malformed" and "over_budget" reasons.
π΄ (20:41) resolveStableChain(currentVersion, targetVersion, signal?) delegates to stableSource().resolveChain(currentVersion, targetVersion, signal).
π΄ (20:41) resolveNightlyChain(opts) accepts exact fields token, currentVersion, targetVersion, fullGzSize, optional preloadedTags, and optional signal, returning Promise<PatchChain | null>.
π΄ (20:41) resolveNightlyChain() creates OciClient with registry https://ghcr.io, repo: GHCR_REPO, user agent sentry-cli/${CLI_VERSION}, and fetch: customFetch.
π΄ (20:41) Nightly-chain tag discovery uses opts.preloadedTags when supplied; otherwise it calls client.listTags(opts.token, PATCH_TAG_PREFIX, opts.signal), then filters and sorts tags between opts.currentVersion and opts.targetVersion.
π΄ (20:41) resolveNightlyChain() returns null if the filtered chain has zero tags or exceeds MAX_NIGHTLY_CHAIN_DEPTH.
π΄ (20:41) Nightly patch manifests are fetched concurrently with Promise.all(chainTags.map((tag) => client.fetchManifest(opts.token, tag, opts.signal))); any manifest-fetch exception makes delta resolution return null.
π΄ (20:41) Nightly chain validation uses patch layer name ${getPlatformBinaryName()}.patch, tracks ordered digests and { fromVersion, toVersion } steps, starts previousVersion at opts.currentVersion, accumulates totalSize, and initializes expectedSha256 as an empty string.
π΄ (20:41) For each nightly manifest, resolveNightlyChain() rejects missing indexed manifest/tag values and validates the step against previousVersion with a remaining size budget of opts.fullGzSize * SIZE_THRESHOLD_RATIO - totalSize.
π΄ (20:41) A failed nightly chain step writes its precise result.failure.reason to the active Sentry span attribute telemetry_reason and returns null.
π΄ (20:41) A successful nightly chain step derives toVersion by slicing PATCH_TAG_PREFIX from the tag, appends the patch digest, adds its size to totalSize, appends { fromVersion: previousVersion, toVersion }, and advances previousVersion.
π΄ (20:41) On the final nightly manifest, resolveNightlyChain() reads the expected target hash using getPatchTargetSha256(manifest, binaryName) ?? "".
π΄ (20:41) After processing nightly manifests, resolveNightlyChain() rejects a final previousVersion unequal to opts.targetVersion or a missing expectedSha256; it records Sentry span attribute telemetry_reason="version-mismatch" before returning null.