Dashboard › cli › Distillation
f969327c-73a0-4bd0-b7da-8f72cfb5b014["lore_tm_v1_z4_enLwCPV6Bz1JRoG30ljEsDfg6UHgM5lfWj1ePknM","lore_tm_v1_ZvuFy3Y4zoJRpgQC6KSEyoULnK-Zvv1Ih6zgLfk_pVw","lore_tm_v1_Ypxi8pXabURiYriQtrzYvZmsTDAYVNQkitxGxrw9Fvs","lore_tm_v1_3tO-MQeWDG1lnYoejRl6zDlOHNs9YTJt_rTA2x9NBVc","lore_tm_v1_phY9BIeG0quLDg-xKfddda8S8wDnPAiKzJuX_VSkSzQ"]
ENOSPC while the write buffer is full may never emit "drain", which can otherwise cause a hang./home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/binary.ts lines 1β180 of 529.src/lib/binary.ts defines InstallationMethod as "curl" | "brew" | "npm" | "pnpm" | "bun" | "yarn" | "unknown"; parseInstallationMethod() lowercases input and accepts all except "unknown" through VALID_METHODS.src/lib/binary.ts function isMusl() caches its result, returns false outside Linux, first probes /lib/ld-musl-${muslArch}.so.1, then checks combined stdout/stderr from spawnSync("ldd", ["--version"]) for "musl"; its architecture mapping is x64 β x86_64, with every other architecture mapped to aarch64.src/lib/binary.ts function getPlatformBinaryName() maps platforms to darwin, windows, or otherwise linux; maps arm64 to arm64 and every other architecture to x64; adds -musl when isMusl() is true and .exe on Windows, producing sentry-<os>-<arch>[-musl][.exe].src/lib/binary.ts function getBinaryDownloadUrl(version) returns https://github.com/getsentry/cli/releases/download/${version}/${getPlatformBinaryName()}; GITHUB_RELEASES_URL is https://api.github.com/repos/getsentry/cli/releases.src/lib/binary.ts function isNightlyVersion(version) classifies any version containing "-dev." as nightly.src/lib/binary.ts still says it uses Bun.semver.order, although the file imports compare as semverCompare from semver, indicating stale Bun-era documentation after the Node.js migration./home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/upgrade.ts lines 340β1095 of 1096.src/lib/upgrade.ts first returns getInstallInfo().method; otherwise it calls detectLegacyInstallationMethod() and best-effort persists { method: legacyMethod, path: process.execPath, version: CLI_VERSION }, logging "Failed to persist install info (DB may be read-only)" on failure.src/lib/upgrade.ts uses GitHub ${GITHUB_RELEASES_URL}/latest for curl/brew and ${NPM_REGISTRY_URL}/latest for package-manager installs; GitHubβs tag_name has VERSION_PREFIX_REGEX removed, while npm returns version.fetchLatestNightlyVersion(signal?) performs two GHCR operations: getAnonymousToken() and fetchNightlyManifest(token), checking signal?.aborted before each call but not passing the signal into either helper; its comment explicitly says the AbortSignal is not threaded through GHCR helpers.fetchLatestVersion(method, channel = "stable") routes nightly to fetchLatestNightlyVersion(), stable curl/brew to fetchLatestFromGitHub(), and other stable methods to fetchLatestFromNpm().nightlyVersionExists(version) checks GHCR tag nightly-${version} and returns false only when an UpgradeError message contains "HTTP 404" or "HTTP 403"; other errors propagate.versionExists(method, version) routes isNightlyVersion(version) to GHCR, uses a HEAD request to ${GITHUB_RELEASES_URL}/tags/${version} for curl/brew, and a HEAD request to ${NPM_REGISTRY_URL}/${version} otherwise.DownloadResult contains tempBinaryPath, lockPath, and optional patchBytes; the caller must release lockPath after the spawned child exits.streamDecompressToFile(body, destPath, setMessage?) in src/lib/upgrade.ts pipes the response through new DecompressionStream("gzip"), creates a Node createWriteStream(destPath), and reports indeterminate decompressed bytes through makeByteProgress("Downloading", null, setMessage).streamDecompressToFile() installs a persistent writer "error" listener that stores the first writeError; its for await loop stops if that error is set, calls writer.write(chunk), records chunk.byteLength, and on backpressure races one-shot "drain" and "error" listeners while removing the unused counterpart.streamDecompressToFile() captures the original streaming exception in streamError, always invokes progress.done() in finally, and then always calls writer.end(callback) to flush/close the writer.streamDecompressToFile() rejects writer.end() with err ?? writeError; if an earlier stream error exists, an end error is demoted to log.debug("writer.end failed after a stream error: ..."), and the original streamError is rethrown preferentially so cleanup cannot mask the root cause.streamDecompressToFile() still describe a manual loop using Bun.file().writer() and a Bun GC/event-loop workaround, while the implementation now uses Node createWriteStream; this is stale implementation documentation.downloadNightlyToPath() gets an anonymous GHCR token; fetches nightly-${version} when pinned or rolling nightly otherwise; computes ${getPlatformBinaryName()}.gz; finds the matching OCI layer by filename; downloads its digest; rejects a missing response body with UpgradeError("execution_failed", "GHCR blob response had no body"); then calls streamDecompressToFile().downloadStableToPath() first requests ${getBinaryDownloadUrl(version)}.gz and streams/decompresses it if the response is OK with a body; any thrown error in this entire compressed attempt is swallowed and triggers fallback to the raw URL.await response.arrayBuffer() and writes new Uint8Array(body) with writeFile(destPath, ...); comments still attribute this approach to a Bun.write(path, Response) event-loop bug.VERIFY_MAX_ATTEMPTS = 6 and VERIFY_BASE_DELAY_MS = 100; probeBinaryFile(path) accepts only an existing regular file with stats.size > 0.waitForBinaryVisible(path) probes 6 times using exponential delays of 100, 200, 400, 800, and 1600 ms, at cumulative times 0, 100, 300, 700, 1500, and 3100 ms; it logs delayed visibility and returns the verified byte size.waitForBinaryVisible() throws UpgradeError("execution_failed", "Downloaded binary is missing or empty at ${path}. This is usually transient β rerun \sentry cli upgrade` to retry.")`.waitForBinaryVisible() and its call site still describes Windows + Bun 1.3.9 issue CLI-1D3, where Bun.file().writer() returned before path visibility and Bun.spawn failed with Executable not found in $PATH; the active implementation is now Node-based, so this rationale may no longer match the code path.downloadBinaryToTemp(version, downloadTag?, offline?, setMessage?) acquires the curl-install lock, removes a leftover temporary binary, tries tryDeltaUpgrade() first, records patchBytes on success, and otherwise either reports offline_cache_miss or downloads the full binary.sentry cli upgrade without --offline; implicit network-unavailable cache miss tells the user to check the internet connection and retry.downloadBinaryToTemp() calls waitForBinaryVisible(tempPath), logs the formatted verified size, clears the consumed patch cache best-effort, applies mode 0o755 outside Windows, and returns { tempBinaryPath: tempPath, lockPath, patchBytes }; on any error it releases the lock and rethrows.downloadBinaryToTemp() path intentionally retains the parent lock across the downloadβspawnβinstall pipeline; if the child resolves to the same install path, acquireLock recognizes process.ppid and takes over, making the parentβs later release a no-op.tryDeltaUpgrade() calls attemptDeltaUpgrade(version, process.execPath, destPath, offline, setMessage); delta failure returns null so the caller can fall back to the full binary.downloadFullBinary() routes nightly versions to downloadNightlyToPath(destPath, version, setMessage) and stable versions to downloadStableToPath(downloadTag ?? version, destPath, setMessage).executeUpgradeHomebrew() runs brew upgrade getsentry/tools/sentry; Homebrew intentionally ignores the requested version because the tap formula controls versioning.executeUpgradePackageManager(pm, version) uses ["global", "add", \sentry@${version}`]for Yarn and["install", "-g", `sentry@${version}`]for npm/pnpm/Bun; spawning usesshell: process.platform === "win32"because package-manager executables are.cmd` files on Windows.executeUpgrade() routes "curl" to downloadBinaryToTemp(), "brew" to Homebrew, "npm" | "pnpm" | "bun" | "yarn" to global package-manager installation, and unknown methods to new UpgradeError("unknown_method")./home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/glowing-cactus/packages/cli/src/lib/ghcr.ts lines 120β520 of 520.GHCR_REPO = "getsentry/cli", GHCR_TAG = "nightly", registry https://ghcr.io, OCI manifest type application/vnd.oci.image.manifest.v1+json, and tag page size TAGS_PAGE_SIZE = 100.getAnonymousToken(signal?), fetchManifest(token, tag, signal?), and fetchTagPage(token, lastTag?, signal?) already accept and pass an AbortSignal, despite fetchLatestNightlyVersion() not threading its supplied signal into them.fetchNightlyManifest(token) does not accept an AbortSignal; it calls fetchManifest(token, GHCR_TAG) without one.manifest.annotations.version; getNightlyVersion() throws UpgradeError("network_error", "Nightly manifest has no version annotation") if absent.findLayerByFilename() matches layer.annotations["org.opencontainers.image.title"] exactly and throws UpgradeError("version_not_found", \No nightly build found for ${filename}`)` when absent.downloadNightlyBlob(token, digest, signal?) first requests the GHCR blob with bearer authorization, redirect: "manual", and buildSignal(GHCR_BLOB_TIMEOUT, signal); GHCR may return the blob directly with HTTP 200 or redirect with 301, 302, 307, or 308.Authorization header because Azure Blob Storage returns 404 when that header accompanies its signed query-string credentials; the redirect request carries only User-Agent plus the callerβs signal.AbortSignal.timeout: comments state a full nightly binary is approximately 30 MB and a 30-second timeout would require sustained throughput of approximately 8 Mbps.listTags() paginates GHCR tags with n=100, adds &last=${encodeURIComponent(lastTag)}, optionally filters by prefix, stops on an empty or short page, and advances with tags.at(-1).downloadLayerBlob() reuses downloadNightlyBlob() and fully buffers response.arrayBuffer(); it is documented for small patch payloads of 50β500 KB.