Dashboard › cli › Distillation
e9e8f47c-8df8-44d6-9ec9-5ccf131de403["lore_tm_v1_tHVIa34W-MIK4GNuXpO5MU9OC16rxNDUsK-Dt79w8kQ"]
Date: Sep 9, 2026
/, so entries such as ~/.local/bin/ compare equal to ~/.local/bin.packages/cli/src/lib/binary.ts adds UpgradeSource with readonly githubRepo, ghcrRepo, and tagPrefix; ordered UPGRADE_SOURCES is exactly: 1. { githubRepo: "getsentry/toolkit", ghcrRepo: "getsentry/toolkit", tagPrefix: "cli@" }; 2. { githubRepo: "getsentry/cli", ghcrRepo: "getsentry/cli", tagPrefix: "" }. PRIMARY_UPGRADE_SOURCE is UPGRADE_SOURCES[0].packages/cli/src/lib/binary.ts defines LEGACY_INSTALL_SUBDIR = join(".sentry", "bin") and LEGACY_INSTALL_SUBDIRS = [LEGACY_INSTALL_SUBDIR]; migration is deliberately restricted to pre-XDG ~/.sentry/bin, excluding current valid targets ~/.local/bin and ~/bin. getLegacyInstallDirs(homeDir) converts the configured subdirectories to absolute paths.packages/cli/src/lib/binary.ts adds IS_CASE_INSENSITIVE_FS for win32 and darwin; samePath(a, b) tolerates one trailing separator and compares case-insensitively on Windows/macOS, allowing equivalents such as C:\Users\User and C:\Users\user.packages/cli/src/lib/binary.ts updates URL construction to be source-specific: getBinaryDownloadUrl(version, source = PRIMARY_UPGRADE_SOURCE) uses ${source.tagPrefix}${version} and ${source.githubRepo}; getGitHubReleasesUrl() builds the releases base; getGitHubReleaseByTagUrl() applies encodeURIComponent(tag); getGitHubLatestReleaseUrl() uses ?per_page=100 for prefixed sources and /latest otherwise; getGitHubRepositoryUrl() builds the repository probe URL. GITHUB_RELEASES_URL now derives from getGitHubReleasesUrl().packages/cli/src/lib/binary.ts adds ResolvedUpgradeSource, containing the selected UpgradeSource and successful Response, and ResolveUpgradeSourceOptions, containing getProbeUrl, optional fetch, optional signal, and optional ordered sources.fetchUpgradeProbe() in packages/cli/src/lib/binary.ts uses options.fetch ?? customFetch, getGitHubHeaders(), and the callerβs AbortSignal; it propagates AbortError, maps TLS certificate failures through buildTlsErrorDetail() to UpgradeError("network_error", ...), and maps other fetch failures to Failed to connect to GitHub: ${stringifyUnknown(error)}.resolveUpgradeSource() in packages/cli/src/lib/binary.ts probes options.sources ?? UPGRADE_SOURCES in exact order, returns { source, response } on success, falls through only for HTTP 404, fails every other HTTP status with Failed to fetch from GitHub: HTTP ${response.status}, and fails exhaustion with No CLI upgrade source was found: every source returned HTTP 404.packages/cli/src/lib/binary.ts documents that compareVersions(a, b) uses semver.compare, replacing the prior Bun.semver.order description, and correctly compares stable X.Y.Z and nightly X.Y.Z-dev.<unix-seconds> versions using numeric SemVer prerelease ordering.determineInstallDir(homeDir, env) in packages/cli/src/lib/binary.ts uses this exact priority: 1. SENTRY_INSTALL_DIR; 2. absolute XDG_BIN_HOME; 3. existing ~/.local/bin already in PATH; 4. existing ~/bin already in PATH; 5. ~/.local/bin. PATH membership now uses samePath() to handle trailing separators and filesystem casing.packages/cli/src/lib/ghcr.ts imports PRIMARY_UPGRADE_SOURCE and UpgradeSource; GHCR_REPO changes from hard-coded "getsentry/cli" to PRIMARY_UPGRADE_SOURCE.ghcrRepo.getAnonymousToken() in packages/cli/src/lib/ghcr.ts now accepts either UpgradeSource | AbortSignal as its first argument plus an optional signal, preserving signal-only compatibility; it requests https://ghcr.io/token?scope=repository:${source.ghcrRepo}:pull and defaults to PRIMARY_UPGRADE_SOURCE.PRIMARY_UPGRADE_SOURCE: fetchManifest(token, tag, signal?, source?); fetchNightlyManifest(token, signal?, source?); downloadNightlyBlob(token, digest, signal?, source?); internal fetchTagPage(token, lastTag?, signal?, source?); listTags(token, prefix?, signal?, source?); and downloadLayerBlob(token, digest, signal?, source?).fetchManifest() uses https://ghcr.io/v2/${source.ghcrRepo}/manifests/${tag}; downloadNightlyBlob() uses https://ghcr.io/v2/${source.ghcrRepo}/blobs/${digest}; fetchTagPage() uses https://ghcr.io/v2/${source.ghcrRepo}/tags/list?n=100; source propagation continues through fetchNightlyManifest(), listTags(), and downloadLayerBlob().downloadNightlyBlob(): the GHCR blob request uses Authorization: Bearer ${token} with redirect: "manual"; redirects 301, 302, 307, and 308 are followed in a separate customFetch() carrying only User-Agent, never the GHCR authorization header.GHCR_REQUEST_TIMEOUT = 10_000, GHCR_MAX_RETRIES = 1, GHCR_BLOB_TIMEOUT = 30_000, and TAGS_PAGE_SIZE = 100; external caller cancellation is propagated without retry, while timeout and retryable network failures may retry once.