Dashboard › cli › Distillation
841c2c76-ca0f-4218-a48e-e472f730385d["lore_tm_v1_Jpj1lJ8IQwyIWs5ZheGrqJG-yKhsn8XE1RTRYCX1Ijs","lore_tm_v1_maZBzaMlG9SxMPfLAItWFSPskIV6zACRHMY6d7SGpwQ","lore_tm_v1_Oq_07893R8MJzPtxaCV-0KIJOIHPAU87NyalasWeR-4","lore_tm_v1_z851bpOtKgpxvVOXi-pvj8cm1cGxRGGs2_TE_oeBXL8","lore_tm_v1_hQ4XZ-XIUN2xFsK5fM4DE4_y0E2sDEQph0igGeKE-V0","lore_tm_v1_NereQraFCUtPeruXTA2EOdgrHbcRThWrFQCB8tnOmag","lore_tm_v1_it3P0VehiiJHIeFaipWBtmjKsYPbqWyifYA-C-JdT_k","lore_tm_v1_v_jrrTVklbwxYhSi8KhTikwiuyL0qGVpbS3spFwunIc","lore_tm_v1_P63bwkmZ3iBYXsgnC4sb5K0XHb2NG-pI3RQrRAiOrMI","lore_tm_v1_K2aq_8xK6h92t0L3jhyJhfPFFQqJYh5rTB3ANmCuX0Y"]
packages/cli/src/lib/ghcr.ts importing PRIMARY_UPGRADE_SOURCE and type UpgradeSource from ./binary.js, changing GHCR_REPO from hard-coded "getsentry/cli" to PRIMARY_UPGRADE_SOURCE.ghcrRepo, and making GHCR token, manifest, blob, tag-listing, and layer-download helpers source-aware.getAnonymousToken(sourceOrSignal, signal) in packages/cli/src/lib/ghcr.ts accepting either UpgradeSource or a backward-compatible AbortSignal, selecting PRIMARY_UPGRADE_SOURCE when passed a signal, and constructing the scope as repository:${source.ghcrRepo}:pull.packages/cli/src/lib/ghcr.ts: fetchManifest(token, tag, signal?, source = PRIMARY_UPGRADE_SOURCE), fetchNightlyManifest(token, signal?, source = PRIMARY_UPGRADE_SOURCE), downloadNightlyBlob(token, digest, signal?, source = PRIMARY_UPGRADE_SOURCE), fetchTagPage(token, lastTag?, signal?, source = PRIMARY_UPGRADE_SOURCE), listTags(token, prefix?, signal?, source = PRIMARY_UPGRADE_SOURCE), and downloadLayerBlob(token, digest, signal?, source = PRIMARY_UPGRADE_SOURCE).packages/cli/test/lib/ghcr.test.ts coverage using UPGRADE_SOURCES[0]: getAnonymousToken() must request a scope containing scope=repository:getsentry/toolkit:pull and return "toolkit-token"; fetchNightlyManifest("token", undefined, UPGRADE_SOURCES[0]) must request /v2/getsentry/toolkit/manifests/nightly.packages/cli/test/lib/install-script.test.ts test that parses UPGRADE_SOURCES=(...) from the install script and requires its whitespace-separated repository order to exactly equal UPGRADE_SOURCES.map((source) => source.githubRepo).packages/cli/src/lib/upgrade.ts nightly download behavior: getNightlyGzFilename() returns ${getPlatformBinaryName()}.gz; downloadNightlyToPath() gets an anonymous GHCR token, fetches nightly-${version} when a version is supplied or the rolling nightly manifest otherwise, finds the platform .gz layer, downloads its blob, requires a response body, and streams decompression to destPath.downloadStableToPath() in packages/cli/src/lib/upgrade.ts: first try ${url}.gz because it is approximately 60% smaller (~37 MB versus ~99 MB in the documentation), stream-decompress it when successful, silently fall through on failure, then download the raw binary; raw non-OK responses throw UpgradeError("execution_failed", \Failed to download binary: HTTP ${response.status}`); the raw body is fully consumed with response.arrayBuffer()beforewriteFile()to avoid Bun issuehttps://github.com/oven-sh/bun/issues/13237`.packages/cli/src/lib/upgrade.ts: probeBinaryFile(path) returns a size only for a present regular file with stats.size > 0, otherwise null; waitForBinaryVisible(path) uses VERIFY_MAX_ATTEMPTS = 6 and VERIFY_BASE_DELAY_MS = 100, probing at 0, 100, 300, 700, 1500, and 3100 ms with sleeps of 100, 200, 400, 800, and 1600 ms.waitForBinaryVisible(path) must throw UpgradeError when the downloaded file never becomes visible or stays empty.waitForBinaryVisible(): on Windows with Bun 1.3.9 (CLI-1D3), streaming writes through Bun.file().writer() can return from writer.end() before the OS exposes the file by path, causing a subsequent Bun.spawn failure Executable not found in $PATH; exponential-backoff polling allows the transient race to self-heal.downloadBinaryToTemp(version, downloadTag?, offline?, setMessage?) in packages/cli/src/lib/upgrade.ts: acquire the curl-install lock, remove a leftover temp file, attempt delta upgrade first, retain deltaResult.patchBytes on success, and otherwise either raise an offline cache-miss error or log "Downloading full binary" and call downloadFullBinary().downloadBinaryToTemp(): explicit offline mode says Cannot upgrade to ${version} in offline mode β no pre-downloaded update is available. Run \sentry cli upgrade` without `--offline` to download the update directly.; implicit network-unavailable mode says Cannot upgrade to ${version} β the network is unavailable and no pre-downloaded update was found. Check your internet connection and try again.Both useUpgradeError("offline_cache_miss", ...)`.downloadBinaryToTemp() calls waitForBinaryVisible(tempPath), logs Binary verified (${formatBytes(verifiedSize)}), clears the consumed patch cache on a best-effort basis, applies Unix mode 0o755 outside Windows, and returns { tempBinaryPath: tempPath, lockPath, patchBytes }; failures release the lock and rethrow.downloadFullBinary() dispatching nightly versions to downloadNightlyToPath(destPath, version, setMessage) and stable versions to downloadStableToPath(downloadTag ?? version, destPath, setMessage).packages/cli/src/lib/upgrade.ts: Homebrew runs brew upgrade getsentry/tools/sentry; Yarn runs yarn global add sentry@${version}; npm, pnpm, and Bun run <pm> install -g sentry@${version}. Spawn uses stdio: "inherit" and shell: process.platform === "win32" because .cmd files otherwise fail with ENOENT.executeUpgrade(method, version, downloadTag?, offline?, setMessage?): "curl" returns downloadBinaryToTemp(...); "brew" runs Homebrew and returns null; "npm", "pnpm", "bun", and "yarn" run the corresponding global package-manager install and return null; unknown methods throw UpgradeError("unknown_method").packages/cli/test/lib/delta-upgrade.mocked.test.ts isolates a vi.mock("../../src/lib/constants.js") setting CLI_VERSION: "0.13.0" so canAttemptDelta() passes its "0.0.0-dev" guard; it remains separate from delta-upgrade.test.ts because bun test --isolate gives each file a fresh module graph.0.13.0 β 0.14.0 chain with a synthetic empty TRDIFF10 patch resolves and downloads but rejects with "SHA-256 mismatch" after producing empty output; an HTTP 500 while resolving releases returns null.resolveNightlyDelta("0.0.0-dev.200", ...) returns null when the target manifest has no ${BINARY_NAME}.gz layer, and also returns null when a .gz layer of size 30_000_000 exists but /tags/list returns { tags: [] }; tag listing runs in parallel with target-manifest fetching and therefore must not receive a mock 404.attemptDeltaUpgrade() tests requiring null for a stable-to-nightly cross-channel target (CLI_VERSION "0.13.0" to "0.0.0-dev.100"), null when all stable-resolution fetches return HTTP 500, and null when patch application receives garbage bytes [0, 1, 2, 3].packages/cli/test/fixtures/patches/small-old.bin, small.trdiff10, and small-new.bin: expected SHA-256 is 54d0dcd74478bc154b5b24393fdc6129518271baa36f446384d60e84021bb724; the expected DeltaResult has patchBytes: 89 and chainLength: 1; patched output must byte-equal small-new.bin.BYK/binpatch tag ref v0.4.2 failing with 404, while the repositoryβs actual tags are unprefixed: 0.4.2 at commit 9ba6bbb8227fcbd2521852d2311c82afac5e9573, 0.4.1 at d0593cdd89eb720edddf9644e0753613e4a8e622, and 0.4.0 at 2446f4cbecca92eba775e8ef52dff01fd20a9326.binpatch package metadata: version 0.4.2, ESM ("type": "module"), MIT license, author BYK, homepage https://github.com/BYK/binpatch#readme, source git+https://github.com/BYK/binpatch.git, issue tracker https://github.com/BYK/binpatch/issues, Node engine >=22.15.0, entry point ./dist/index.js, types ./dist/index.d.ts, and sideEffects: false.binpatch package scripts: "build": "tsup", "clean": "rm -rf dist", "typecheck": "tsc --noEmit", "test": "vitest run", and "prepublishOnly": "npm run clean && npm run build"; dev dependencies are @types/node 24.13.1, tsup ^8.5.0, TypeScript ^5.7.3, and Vitest ^4.1.7.binpatch documentation describing it as a pure-Node, zero-product-coupling, MIT-licensed binary delta-update engine for TRDIFF10/bsdiff+zstd patch chains, with runtime apply/discovery and CI-side generation/publishing through a composite GitHub Action in action/; documentation is at https://binpatch.p.byk.im/ and source at https://github.com/BYK/binpatch.binpatch two-part scope: (1) apply and discovery via the npm package using only node:* built-ins; (2) patch generation and publication to GHCR/GitHub Releases via a composite GitHub Action. The runtime package provides TRDIFF10 parsing, chain application, offline caching, pluggable SourceStrategy discovery, and cache-first resolveAndApply.binpatch usage: applyPatchChainInMemory(oldPath, patches, destPath, { onBytes }) keeps intermediate hops in memory, writes and hashes only the final hop, and returns its SHA-256; makeCache("/path/to/cache-dir") supports save(chain, steps), load(currentVersion, targetVersion), cleanup() for entries past a 7-day TTL, and clear().ghcrSource configuration fields registry, repo, userAgent, binaryName, targetTag, and compareVersions; the example uses registry https://ghcr.io, repo owner/project, user agent my-cli/1.2.3, binary my-cli-linux-x64, and targetTag: (v) => \nightly-${v}`. githubReleaseSourceusesreleasesUrl, binaryName, and userAgent, with example URL https://api.github.com/repos/owner/project/releases`.resolveAndApply({ offline: true }) is cache-only and must never touch the network.resolveAndApply() orchestration inputs: source, currentVersion, targetVersion, oldPath, destPath, optional cache, optional offline, optional onProgress, and optional telemetry callbacks onResolved({ source }), onOfflineMiss(), and onUnavailable(reason); its result is { sha256, patchBytes, chainLength } | null, where null means fall back to a full download.binpatch: "no_patches", "malformed_chain", "too_long", "over_budget", and "network"; "malformed_chain" denotes a published-but-broken or poisoned patch and is the reason recommended for alerting, unlike benign "no_patches".binpatch progress is events, never rendering: the library never draws progress, while resolveAndApply() emits { type: "phase" | "bytes" | "done"; phase; ... } through onProgress; a missing handler is silent.onProgress handler must never abort the operation; safeProgress isolates progress callbacks so consumers may independently render a stderr bar, spinner message, or log line.SourceStrategy contract as resolveChain(current, target, signal?, report?) => PatchChain | null; its optional report(reason) classifies a null result for telemetry and never changes control flow.binpatch exports grouped as: TRDIFF10 parsing/diff primitives parsePatchHeader, offtin, PatchHeader, MAX_OUTPUT_SIZE, addDiffChunk; patch application applyPatch, applyPatchToMemory, applyPatchChainInMemory; discovery/orchestration resolveAndApply, SourceStrategy, ghcrSource, githubReleaseSource, OciClient; progress ProgressEvent, ProgressHandler, safeProgress; contracts/types PatchChain, PatchLink, ChainStep, DeltaResult, BinpatchError, MAX_STABLE_CHAIN_DEPTH, MAX_NIGHTLY_CHAIN_DEPTH, SIZE_THRESHOLD_RATIO, PATCH_TAG_PREFIX; cache makeCache, PatchCache, patchFileName, chainFileName, ChainMeta, and PatchStepMeta.binpatch wire contract: TRDIFF10 consists of "TRDIFF10" magic plus offtin sign-magnitude i64 values controlLen, diffLen, and newSize, followed by zstd(control) | zstd(diff) | zstd(extra); parsing bounds newSize by MAX_OUTPUT_SIZE of 2 GiB; after applying a full chain, the output is SHA-256 checked against the expected hash, and a mismatch discards it so the consumer can fall back to a full download.