Dashboard › cli › Distillation
3c01f7b4-b297-4774-bffd-b7a71019f8ab["lore_tm_v1_HgZQ6KfPxWjlAGEkesYjiKCR1ni6AyHymLR9JFPSbE8","lore_tm_v1__Ote-ouPCZQoiYT2j-7DI1svstSdl2KEquUDoqtB-_g"]
packages/cli/src/lib/upgrade.ts, detectInstallationMethod() must run isHomebrewInstall() before consulting getInstallInfo(), so switching from a previously recorded curl install to Homebrew returns "brew".isHomebrewInstall() resolves process.execPath with realpathSync() before checking for "/Cellar/", because process.execPath may point to /opt/homebrew/bin/sentry while the real binary is under /opt/homebrew/Cellar/sentry/<version>/bin/sentry; if realpath resolution fails, it checks the unresolved path.detectInstallationMethod() priority in packages/cli/src/lib/upgrade.ts is: 1. Homebrew realpath detection; 2. stored DB install info; 3. legacy detection; 4. best-effort persistence through setInstallInfo({method, path: process.execPath, version: CLI_VERSION}), with DB write failure logged via log.debug() rather than blocking detection.["npm", "pnpm", "bun", "yarn"], then detectPackageManagerFromPath(), and finally returns "unknown". detectPackageManagerFromPath() requires a node_modules path, identifies pnpm by a .pnpm segment, Bun by a .bun segment, and otherwise defaults npm/yarn-style layouts to "npm".runCommand() invokes package-manager commands with shell: process.platform === "win32" so Windows .cmd executables work, captures and trims stdout, drains discarded stderr to prevent blocking, and maps a null close code to exit code 1. isInstalledWith() uses yarn global list --depth=0 for Yarn and <pm> list -g sentry otherwise, requiring exit code 0 and stdout containing sentry@.streamDecompressToFile() in packages/cli/src/lib/upgrade.ts uses openSync() + writeSync() + closeSync() instead of createWriteStream(). Root cause: a writer’s 'finish' callback can fire before the fd is released, causing an immediate Linux spawn to fail with ETXTBSY; synchronous close removes the race and aligns gzip fallback behavior with raw writeFile() downloads.writeChunkSync(fd, chunk) loops until the entire Uint8Array is written, handling short writes and throwing if writeSync() returns 0 or a negative value. drainBodyToFd() pipes through new DecompressionStream("gzip"), separately captures streamError and writeError, reports bytes through onBytes, and leaves fd ownership to the caller.downloadStableToPath() first tries ${getBinaryDownloadUrl(version)}.gz (about 37 MB versus 99 MB, described as about 60% smaller), streams decompression to disk, and falls back on any gzip-path failure to the raw URL. The raw response is fully consumed with response.arrayBuffer() and written using writeFile(destPath, new Uint8Array(body)).downloadNightlyToPath() obtains an anonymous GHCR token, fetches either nightly-${version} or the rolling :nightly manifest, finds the ${getPlatformBinaryName()}.gz layer through findLayerByFilename(), downloads its blob, and streams gzip decompression to destPath; a missing response body throws UpgradeError("execution_failed", "GHCR blob response had no body").waitForBinaryVisible(path) accepts only a regular file with stats.size > 0; otherwise it polls exactly 6 times with 5 exponential sleeps of 100, 200, 400, 800, and 1600 ms, probing at cumulative times 0, 100, 300, 700, 1500, and 3100 ms.waitForBinaryVisible() polling addresses Windows + Bun 1.3.9 issue CLI-1D3, where Bun.file().writer().end() can return before the OS exposes the file and a subsequent Bun.spawn fails with Executable not found in $PATH. Exhaustion throws UpgradeError("execution_failed", "Downloaded binary is missing or empty at ${path}. This is usually transient — rerun \sentry cli upgrade` to retry.")`.downloadBinaryToTemp() acquires the curl-install lock, removes a leftover temp file, tries tryDeltaUpgrade() first, and falls back to downloadFullBinary() unless offline. After either path it calls waitForBinaryVisible(tempPath), logs the formatted verified size, clears the consumed patch cache best-effort, applies mode 0o755 outside Windows, and returns {tempBinaryPath, lockPath, patchBytes}; any thrown error releases the lock.downloadBinaryToTemp() distinguishes offline failures: explicit offline mode reports no pre-downloaded update and instructs running without --offline; network-fallback mode reports that the network is unavailable and no pre-downloaded update was found. The lock remains held on success through the download→spawn→install pipeline and must be released by the caller after the child exits.executeUpgrade() dispatches "curl" to downloadBinaryToTemp(), "brew" to brew upgrade getsentry/tools/sentry, and "npm", "pnpm", "bun", or "yarn" to global package installation. Yarn uses yarn global add sentry@<version>; the others use <pm> install -g sentry@<version>; an unsupported method throws UpgradeError("unknown_method").