Dashboard › cli › Distillation
e9174dce-9c5e-435c-ab8c-3a38dacdacca["lore_tm_v1_Qs1o5rRK8R_zUDM3ezUE-LIAUnMkyKPZwhNdmU1qfxI"]
Date: Sep 9, 2026
packages/cli/src/lib/upgrade.ts:701-714 defines versionExists(method, version, source?): nightly, curl, and brew installations use standaloneVersionExists(version, source); npm-style installations send HEAD to ${NPM_REGISTRY_URL}/${version} through fetchWithUpgradeError() and return response.ok.packages/cli/src/lib/upgrade.ts:719-727 defines DownloadResult with required tempBinaryPath and lockPath, plus optional delta-upgrade size patchBytes.packages/cli/src/lib/upgrade.ts:737-748 defines writeChunkSync(fd, chunk), which loops over short writeSync() writes and throws if a write returns n <= 0 to avoid an infinite no-progress loop.packages/cli/src/lib/upgrade.ts:756-781 defines drainBodyToFd(): streams through new DecompressionStream("gzip"), writes each chunk synchronously, reports decompressed byte counts through onBytes, and separately returns streamError and writeError.packages/cli/src/lib/upgrade.ts:784-839 implements streamDecompressToFile() with openSync(destPath, "w", 0o644), writeSync, and closeSync rather than fs.createWriteStream. Root cause addressed: Node’s writer.end() callback fires on 'finish' before the fd is released, creating a reproducible Linux ETXTBSY race when immediately spawning the output; synchronous close makes compressed full-download fallback behavior consistent with the race-free raw writeFile path. Progress uses makeByteProgress("Downloading", null, setMessage) and is cosmetic/non-aborting.packages/cli/src/lib/upgrade.ts:850-893 defines nightly downloads: getNightlyGzFilename() returns ${getPlatformBinaryName()}.gz; downloadNightlyToPath() obtains a source-specific anonymous GHCR token, fetches either nightly-${version} or rolling nightly, finds the matching OCI layer by filename, downloads its blob from the same UpgradeSource, rejects a missing response body with UpgradeError("execution_failed", "GHCR blob response had no body"), and streams gzip decompression to disk.packages/cli/src/lib/upgrade.ts:907-948 implements source-affine stable download in downloadStableToPath(): first tries ${getBinaryDownloadUrl(version, source)}.gz and stream-decompresses it, but silently falls back to the raw URL on any compressed-path failure. The raw response must be ok; otherwise it throws UpgradeError("execution_failed", "Failed to download binary: HTTP …"). It fully consumes response.arrayBuffer() before writeFile() to avoid Bun issue oven-sh/bun#13237, where a process can exit before a large streaming Bun.write(path, Response) finishes.packages/cli/src/lib/upgrade.ts:950-1021 defines visibility verification with VERIFY_MAX_ATTEMPTS = 6 and VERIFY_BASE_DELAY_MS = 100. probeBinaryFile() accepts only an existing regular file with stats.size > 0. waitForBinaryVisible() probes at 0, 100, 300, 700, 1500, and 3100 ms, sleeping 100, 200, 400, 800, and 1600 ms; it returns the file size or throws UpgradeError("execution_failed", "Downloaded binary is missing or empty at … This is usually transient — rerun sentry cli upgrade to retry.").packages/cli/src/lib/upgrade.ts:974-1020 addresses Windows + Bun 1.3.9 issue CLI-1D3: Bun.file().writer().end() can return before the OS exposes the file by path, causing immediate Bun.spawn to fail with Executable not found in $PATH; exponential backoff allows the race to self-heal.packages/cli/src/lib/upgrade.ts:1050-1129 implements downloadBinaryToTemp(version, downloadTag?, offline?, setMessage?, source = PRIMARY_UPGRADE_SOURCE): acquires the curl install lock, best-effort removes a stale temp file, attempts source-affine delta upgrade first, handles offline cache misses with distinct explicit-offline versus network-unavailable messages, otherwise performs a source-affine full download, verifies a visible non-empty file, best-effort clears the consumed patch cache, applies mode 0o755 outside Windows, and returns { tempBinaryPath, lockPath, patchBytes }. Any thrown error releases the lock before propagation.packages/cli/src/lib/upgrade.ts:1033-1040 documents lock ownership across download→spawn→install: the caller must release the lock after the child exits; if the child resolves to the same install path, acquireLock recognizes process.ppid and transfers lock ownership, making the parent’s later release a harmless no-op.packages/cli/src/lib/upgrade.ts:1142-1157 defines tryDeltaUpgrade() as a source-affine wrapper around attemptDeltaUpgrade(version, process.execPath, destPath, offline, setMessage, source); delta failure returns null so callers can fall back to a full binary.packages/cli/src/lib/upgrade.ts:1167-1184 defines downloadFullBinary(): nightly versions call downloadNightlyToPath(destPath, version, setMessage, source), while stable versions call downloadStableToPath(downloadTag ?? version, destPath, setMessage, source).packages/cli/src/lib/upgrade.ts:1196-1221 defines Homebrew execution as brew upgrade getsentry/tools/sentry with inherited stdio and shell: process.platform === "win32"; nonzero exit and spawn errors become UpgradeError("execution_failed", …). The requested version is intentionally ignored because Homebrew’s formula controls the version and does not support arbitrary release pinning.packages/cli/src/lib/upgrade.ts:1231-1267 defines package-manager global installation: Yarn uses ["global", "add", "sentry@${version}"]; npm, pnpm, and Bun use ["install", "-g", "sentry@${version}"]. Windows enables shell: true because package-manager executables are .cmd files that otherwise fail with ENOENT; nonzero exits and spawn failures become UpgradeError("execution_failed", …).packages/cli/src/lib/upgrade.ts:1288-1317 defines executeUpgrade(method, version, downloadTag?, offline?, setMessage?, source = PRIMARY_UPGRADE_SOURCE): curl forwards all arguments, including the selected source, to downloadBinaryToTemp(); brew invokes Homebrew and returns null; npm, pnpm, bun, and yarn invoke the global package-manager installer and return null; unknown methods throw UpgradeError("unknown_method").