Dashboard › cli › Distillation
ab5f75f6-d50f-44e0-8d34-6bb6ac9889e1["lore_tm_v1_AqGHoQPnaOuFXEmL52HPppaoDhh2qLvA_8qfdGb-mEQ","lore_tm_v1_PIXJPmh3TwtRfZNdgTD5I-pAOOtk6NQzpoea9r8-8KM","lore_tm_v1_B3JFx6UHTUQUfcFFqj9EOY0QnP7FGiOejpfazREWZfI"]
packages/cli/src/lib/version-check.ts, packages/cli/src/lib/delta-upgrade.ts, packages/cli/src/lib/upgrade.ts, packages/cli/src/lib/db/version-check.ts, and packages/cli/src/lib/binary.ts.packages/cli/src/lib/version-check.ts defines both CHECK_INTERVAL_MS and NOTIFICATION_INTERVAL_MS as 24 * 60 * 60 * 1000.packages/cli/src/lib/delta-upgrade.ts exposes DeltaResult, canAttemptDelta(), fetchRecentReleases(), downloadStablePatch(), extractStableChain(), filterAndSortChainTags(), validateChainStep(), resolveStableChain(), resolveNightlyChain(), applyPatchChain(), resolveStableDelta(), resolveNightlyDelta(), attemptDeltaUpgrade(), prefetchNightlyPatches(), and prefetchStablePatches().fetchLatestFromGitHub(signal?) in packages/cli/src/lib/upgrade.ts requests ${GITHUB_RELEASES_URL}/latest through fetchWithUpgradeError() with getGitHubHeaders() and the optional AbortSignal; non-OK responses produce UpgradeError("network_error", \Failed to fetch from GitHub: ${response.status}`), a missing tag_nameproduces"No version found in GitHub release", and the returned tag has VERSION_PREFIX_REGEX` removed.fetchLatestFromNpm() requests ${NPM_REGISTRY_URL}/latest with Accept: "application/json"; non-OK responses produce UpgradeError("network_error", \Failed to fetch from npm: ${response.status}`), a missing versionproduces"No version found in npm registry", and a valid response returns data.version`.fetchLatestNightlyVersion(signal?) performs two GHCR requests—anonymous token exchange via getAnonymousToken(), then fetchNightlyManifest(token)—and extracts the version with getNightlyVersion(manifest). It checks signal?.aborted before each network call and throws new AbortError() when aborted, because the signal is not threaded through the GHCR helpers.fetchLatestVersion(method, channel = "stable") chooses fetchLatestNightlyVersion() for the "nightly" channel; on stable it chooses fetchLatestFromGitHub() for "curl" or "brew" and fetchLatestFromNpm() for package-manager installations.nightlyVersionExists(version) checks GHCR tag nightly-${version} using getAnonymousToken() and fetchManifest(). It returns false only when an UpgradeError message contains HTTP 404 or HTTP 403; other errors propagate.versionExists(method, version) checks nightly-looking versions against GHCR through nightlyVersionExists(). Stable "curl"/"brew" versions use a HEAD request to ${GITHUB_RELEASES_URL}/tags/${version} with GitHub headers; package-manager versions use a HEAD request to ${NPM_REGISTRY_URL}/${version}.DownloadResult contains required tempBinaryPath: string and lockPath: string, plus optional patchBytes?: number; the caller must retain and later release the download lock after the child exits.streamDecompressToFile(body, destPath, setMessage?) pipes the response through new DecompressionStream("gzip") and writes with createWriteStream(destPath). The manual for await loop works around Bun issue https://github.com/oven-sh/bun/issues/13237, where streaming response bodies may be garbage-collected before completion.streamDecompressToFile() reports indeterminate decompressed-byte progress through makeByteProgress("Downloading", null, setMessage) because compressed Content-Length does not reveal decompressed size; progress is cosmetic and never aborts.streamDecompressToFile() installs an early writer "error" listener and stores the first error in writeError to prevent Node ERR_UNHANDLED_ERROR crashes from write failures such as ENOSPC or EIO.writer.write(chunk) returns false, streamDecompressToFile() races one-time "drain" and "error" listeners, removing the unused counterpart listener. This prevents a hang when an I/O failure such as ENOSPC occurs while the buffer is full and the writer never emits "drain", while also avoiding MaxListenersExceededWarning."drain".streamDecompressToFile() preserves the original streaming error in streamError, always calls progress.done(), and then awaits writer.end(). If writer.end() fails without a prior stream failure, that end error is thrown; if a stream failure already exists, the end error is demoted to log.debug(...) and the original stream error is rethrown so cleanup cannot mask the root cause.getNightlyGzFilename() returns ${getPlatformBinaryName()}.gz; nightly GHCR layers use names such as sentry-<os>-<arch>.gz, with Windows using sentry-windows-x64.exe.gz.downloadNightlyToPath(destPath, version?, setMessage?) gets an anonymous GHCR token, fetches nightly-${version} when a version is provided or the rolling nightly manifest otherwise, finds the platform layer by getNightlyGzFilename(), downloads its digest with downloadNightlyBlob(), and streams gzip decompression to destPath. A response without a body throws UpgradeError("execution_failed", "GHCR blob response had no body").downloadStableToPath(version, destPath, setMessage?) first attempts ${getBinaryDownloadUrl(version)}.gz, approximately 60% smaller (~37 MB versus ~99 MB), and streams decompression when the response is OK and has a body. Any compressed-path failure falls back to the raw binary URL.response.arrayBuffer() before calling writeFile(destPath, new Uint8Array(body)), avoiding the Bun event-loop bug where Bun.write(path, Response) with a large streaming body can let the process exit before download completion. A non-OK raw response throws UpgradeError("execution_failed", \Failed to download binary: HTTP ${response.status}`)`.VERIFY_MAX_ATTEMPTS = 6 and VERIFY_BASE_DELAY_MS = 100. probeBinaryFile(path) returns the size only when the path exists, is a regular file, and has size greater than zero; otherwise it returns null.waitForBinaryVisible(path) addresses Windows + Bun 1.3.9 issue CLI-1D3, where writer.end() can return before the OS exposes the file by path and a following Bun.spawn fails with Executable not found in $PATH.waitForBinaryVisible(path) performs six probes with exponential delays: 1. probe at 0 ms, sleep 100 ms; 2. probe at 100 ms, sleep 200 ms; 3. probe at 300 ms, sleep 400 ms; 4. probe at 700 ms, sleep 800 ms; 5. probe at 1500 ms, sleep 1600 ms; 6. final probe at 3100 ms, for a worst-case wall-clock budget of about 3.1s.waitForBinaryVisible(path) throws UpgradeError("execution_failed", \Downloaded binary is missing or empty at ${path}. This is usually transient — rerun `sentry cli upgrade` to retry.`); if visibility requires multiple probes, it logs Binary became visible after ${attempt} attempts`.downloadBinaryToTemp(version, downloadTag?, offline?, setMessage?) derives { tempPath, lockPath } from getCurlInstallPaths(), acquires lockPath, and removes a leftover tempPath before downloading.downloadBinaryToTemp() first calls tryDeltaUpgrade(version, tempPath, !!offline, setMessage). A successful delta records patchBytes; absent delta plus offline mode throws UpgradeError("offline_cache_miss", ...), with distinct messages for "explicit" offline mode versus "network-fallback"; otherwise it logs "Downloading full binary" and calls downloadFullBinary().sentry cli upgrade without --offline; network-fallback cache-miss guidance says the network is unavailable and instructs checking the internet connection and trying again.downloadBinaryToTemp() calls waitForBinaryVisible(tempPath), logs Binary verified (${formatBytes(verifiedSize)}), best-effort clears consumed patches with clearPatchCache().catch(...), sets mode 0o755 outside Windows, and returns { tempBinaryPath: tempPath, lockPath, patchBytes }.downloadBinaryToTemp() keeps the lock held on success across the download→spawn→install pipeline, but releases it on errors. The caller must release it after the child exits; when parent and child resolve to the same install path, acquireLock() transfers ownership through process.ppid recognition and the parent’s later release becomes a harmless no-op.tryDeltaUpgrade(version, destPath, offline?, setMessage?) delegates to attemptDeltaUpgrade(version, process.execPath, destPath, offline, setMessage); delta failures are designed to return null so callers can fall back to a full binary download.downloadFullBinary(version, downloadTag, destPath, setMessage?) uses downloadNightlyToPath(destPath, version, setMessage) when isNightlyVersion(version) is true; otherwise it uses downloadStableToPath(downloadTag ?? version, destPath, setMessage).executeUpgradeHomebrew() spawns 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 versioning and does not support arbitrary release pinning; nonzero exit and spawn errors become UpgradeError("execution_failed", ...).executeUpgradePackageManager(pm, version) uses ["global", "add", \sentry@${version}`]for Yarn and["install", "-g", `sentry@${version}`]for npm, pnpm, and Bun. It enables a shell on Windows because package-manager executables are.cmdbatch files that otherwise fail withENOENT`.executeUpgrade(method, version, downloadTag?, offline?, setMessage?) dispatches "curl" to downloadBinaryToTemp(), "brew" to executeUpgradeHomebrew(), and "npm", "pnpm", "bun", or "yarn" to executeUpgradePackageManager(). Brew and package-manager paths return null; unknown methods throw new UpgradeError("unknown_method").