Dashboard › cli › Session 12DoSgIBVC2c
12DoSgIBVC2cxJr4qREAD-ONLY final security/supply-chain gate getsentry/cli PR #1569 exact base ec83887a16f780f32fba4b7d710bad262dba3a22 head 878459c490576dbb7abb76d3a3dc1b5d9fc62c1f. Immutable Git objects only; never worktree. Repo /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Inspect all changed source/tests. Verify prior blockers, especially transport-only cache fallback typed UpgradeTransportError; HTTP/metadata always fail closed even cache populated; package manager stable latest/pins reject malformed/dev/beta/rc; abort reason identity every stage; strict branded prefetch provenance; stable/nightly target-driven channel; 404-only/source affinity/trusted pagination/GHCR scope+redirect/SHA/delta/cache/offline/installer unchanged; CodeQL mock and assertion exact parsed origin. Return substantive findings-first PASS/CONCERN/MUST-FIX/BLOCKED exact head file:line and residual gaps. Final exactly MERGE or DO-NOT-MERGE. Never modify; empty forbidden.
BASE ec83887a16f780f32fba4b7d710bad262dba3a22 110d6530e57ca4fbfb2a3f98e52013d2668b5b5e HEAD ec83887a16f780f32fba4b7d710bad262dba3a22 110d6530e57ca4fbfb2a3f98e52013d2668b5b5e 878459c490576dbb7abb76d3a3dc1b5d9fc62c1f 8e36adccb73967ef42f9dc3b0015e5032ed41942 e22f0eba66298a7228a988a4adcf1ec5be85227b M packages/cli/src/commands/cli/upgrade.ts M packages/cli/src/lib/binary.ts M packages/cli/src/lib/delta-upgrade.ts M packages/cli/src/lib/errors.ts M packages/cli/src/lib/ghcr.ts M packages/cli/src/lib/release-notes.ts M packages/cli/src/lib/upgrade.ts M packages/cli/src/lib/version-check.ts M packages/cli/test/commands/cli.test.ts M packages/cli/test/commands/cli/upgrade.test.ts M packages/cli/test/lib/binary.test.ts M packages/cli/test/lib/delta-upgrade.mocked.test.ts M packages/cli/test/lib/delta-upgrade.test.ts M packages/cli/test/lib/ghcr.test.ts M packages/cli/test/lib/release-notes.test.ts M packages/cli/test/lib/upgrade.test.ts M packages/cli/test/lib/version-check.test.ts 741bd72559d1...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0898bc0e2001fCxbWAEmNUKj1e
rrors.js";
import { formatBytes } from "./formatters/numbers.js";
import {
downloadNightlyBlob,
fetchManifest,
fetchNightlyManifest,
findLayerByFilename,
+ GhcrManifestHttpError,
getAnonymousToken,
getNightlyVersion,
+ type OciManifest,
} from "./ghcr.js";
import { logger } from "./logger.js";
import { clearPatchCache } from "./patch-cache.js";
import { makeByteProgress, type SetMessage } from "./progress.js";
/** Scoped logger for upgrade operations */
const log = logger.withTag("upgrade");
// Re-export for backward compatibility — consumers that import
// InstallationMethod from upgrade.ts continue to work.
export type { InstallationMethod } from "./binary.js";
// biome-ignore lint/performance/noBarrelFile: backward-compat re-export, not a barrel
export { parseInstallationMethod } from "./binary.js";
/** Package managers ...
diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts index 8d8282368..b3039d86b 100644 --- a/packages/cli/src/commands/cli/upgrade.ts +++ b/packages/cli/src/commands/cli/upgrade.ts @@ -1,96 +1,100 @@ /** * sentry cli upgrade * * Self-update the Sentry CLI to the latest or a specific version. * After upgrading, spawns the NEW binary with `cli setup` to update * completions, agent skills, and record installation metadata. * * Supports two release channels: * - stable (default): tracks the latest GitHub release * - nightly: tracks the rolling nightly prerelease built from main * * The channel can be set via --channel or by passing "nightly"/"stable" * as the version argument. The choice is persisted in the local database * so that subsequent bare `sentry cli upgrade` calls use the same channel. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0898c0f65001evqxmmn3nEQ09w
+}
+
+/** Build the GitHub API base URL for a release source. */
+export function getGitHubReleasesUrl(
+ source: UpgradeSource = PRIMARY_UPGRADE_SOURCE
+): string {
+ return `https://api.github.com/repos/${source.githubRepo}/releases`;
+}
+
+/** Build the GitHub API URL for one source-specific release tag. */
+export function getGitHubReleaseByTagUrl(
+ version: string,
+ source: UpgradeSource = PRIMARY_UPGRADE_SOURCE
+): string {
+ const tag = `${source.tagPrefix}${version}`;
+ return `${getGitHubReleasesUrl(source)}/tags/${encodeURIComponent(tag)}`;
+}
+
+/** Build the GitHub API URL used to discover a source's latest CLI release. */
+export function getGitHubLatestReleaseUrl(
+ source: UpgradeSource = PRIMARY_UPGRADE_SOURCE
+): string {
+ return source.tagPrefix
+ ? `${getGitHubReleasesUrl(source)}?per_page=100`
+ : ...
diff --git a/packages/cli/src/lib/release-notes.ts b/packages/cli/src/lib/release-notes.ts index ddf617535..903ca51f6 100644 --- a/packages/cli/src/lib/release-notes.ts +++ b/packages/cli/src/lib/release-notes.ts @@ -1,72 +1,79 @@ /** * Release Notes Parser & Aggregation * * Extracts user-facing changelog entries from GitHub Release bodies (stable) * or conventional commit messages (nightly). Uses `marked.lexer()` for * AST-based section extraction and produces structured data that can be * re-serialized as filtered markdown for rendering via `renderMarkdown()`. ...
diff --git a/packages/cli/src/lib/binary.ts b/packages/cli/src/lib/binary.ts
index 75582612a..994626cfd 100644
--- a/packages/cli/src/lib/binary.ts
+++ b/packages/cli/src/lib/binary.ts
@@ -1,51 +1,55 @@
/**
* Binary Management
*
* Shared utilities for installing, replacing, and managing the CLI binary.
* Used by both `setup --install` (fresh installs) and `upgrade` (self-updates).
*/
import { spawnSync } from "node:child_process";
import {
existsSync,
mkdirSync,
readFileSync,
renameSync,
unlinkSync,
writeFileSync,
} from "node:fs";
import { chmod, copyFile, mkdir, realpath, unlink } from "node:fs/promises";
import { delimiter, dirname, isAbsolute, join, resolve, sep } from "node:path";
import { compare as semverCompare } from "semver";
import { getUserAgent } from "./constants.js";
import {
buildTlsErrorDetail,
customFetch,
isTlsCertError,
} from "./custom-ca.js";
-import { stringifyUnknown, UpgradeError } from "./errors.js";
+import ...
diff --git a/packages/cli/test/lib/binary.test.ts b/packages/cli/test/lib/binary.test.ts
index 6fcfb3b1a..be462848c 100644
--- a/packages/cli/test/lib/binary.test.ts
+++ b/packages/cli/test/lib/binary.test.ts
@@ -20,34 +20,38 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
acquireLock,
compareVersions,
determineInstallDir,
fetchWithUpgradeError,
getBinaryDownloadUrl,
getBinaryFilename,
getBinaryPaths,
+ getGitHubReleaseByTagUrl,
getLegacyInstallDirs,
getPlatformBinaryName,
installBinary,
isDowngrade,
isMusl,
releaseLock,
replaceBinarySync,
+ resolveUpgradeSource,
samePath,
+ UPGRADE_SOURCES,
+ UpgradeSourceNotFoundError,
} from "../../src/lib/binary.js";
import { UpgradeError } from "../../src/lib/errors.js";
describe("getBinaryDownloadUrl", () => {
test("builds correct URL for current platform", () => {
const url = getBinaryDownloadUrl("1.0.0");
- ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0898cb8be001J1tReyfy9BvZCi
ginalFetch: typeof globalThis.fetch;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/resolve-effective-org.test.ts:166: originalFetch = globalThis.fetch;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/resolve-effective-org.test.ts:171: globalThis.fetch = originalFetch;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/resolve-effective-org.test.ts:217: test("resolves DSN org after API refresh when cache is cold", async () => {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/resolve-effective-org.test.ts:224: test("resolves slug after API refresh populates cache", async () => {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/resolve-effective-org.test.ts:246: // then falls back to the original slug. ...
diff --git a/packages/cli/test/commands/cli.test.ts b/packages/cli/test/commands/cli.test.ts
index cd1514eb0..4eec11e9d 100644
--- a/packages/cli/test/commands/cli.test.ts
+++ b/packages/cli/test/commands/cli.test.ts
@@ -117,120 +117,114 @@ describe("upgradeCommand.func", () => {
globalThis.fetch = originalFetch;
});
// Note: We skip testing "unknown installation method" case because
// detectInstallationMethod() runs actual shell commands (npm list, etc.)
// which can be slow/flaky in CI. The unknown method handling is tested
// indirectly through the upgrade.ts unit tests in lib/upgrade.test.ts.
test("shows installation info with specified method", async () => {
globalThis.fetch = (async () =>
- new Response(JSON.stringify({ tag_name: "v0.0.0-dev" }), {
+ new Response(JSON.stringify([{ tag_name: "cli@1.0.0" }]), {
status: 200,
headers: { "Content-Type": "application/json" },
})) as typeof fetch;
const func = ...
diff --git a/packages/cli/test/lib/delta-upgrade.mocked.test.ts b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
index cd0a2fddc..f6186ab59 100644
--- a/packages/cli/test/lib/delta-upgrade.mocked.test.ts
+++ b/packages/cli/test/lib/delta-upgrade.mocked.test.ts
@@ -94,20 +94,20 @@ describe("resolveStableDelta", () => {
// Create a "current binary" to patch from
const oldBinaryPath = tempFile("old-binary.bin");
const destPath = tempFile("patched-binary.bin");
writeFileSync(oldBinaryPath, Buffer.from("old binary content for testing"));
// Set up fetch mocks — releases API + patch download
// Since applyPatch will fail (we don't have a real TRDIFF10 matching this binary),
// we expect resolveStableDelta to throw, but the chain resolution should succeed
- const patchUrl = `https://github.com/getsentry/cli/releases/download/0.14.0/${BINARY_NAME}.patch`;
+ const patchUrl = ...
diff --git a/packages/cli/test/lib/release-notes.test.ts b/packages/cli/test/lib/release-notes.test.ts
index 78963b471..ffccf4901 100644
--- a/packages/cli/test/lib/release-notes.test.ts
+++ b/packages/cli/test/lib/release-notes.test.ts
@@ -4,26 +4,32 @@
* Tests core invariants (section extraction, version filtering, truncation)
* that are hard to express as property-based tests due to format specifics.
*
* Core random-input invariants (category validity, filtering, commit parsing)
* are tested via property-based tests in release-notes.property.test.ts.
*/
import { marked } from "marked";
-import { describe, expect, test } from "vitest";
-import type { GitHubRelease } from "../../src/lib/delta-upgrade.js";
+import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
+import {
+ fetchRecentReleases,
+ type GitHubRelease,
+} from "../../src/lib/delta-upgrade.js";
import {
buildChangelogSummary,
...
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:178: }).catch((reason: unknown) => reason);
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:440: test("re-throws AbortError as-is", async () => {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:442: const err = new Error("Aborted");
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:443: err.name = "AbortError";
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:452: expect((error as Error).name).toBe("AbortError");
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:457: test("preserves an arbitrary external abort reason", async () => {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:458: const controller = new AbortController();
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/test/lib/binary.test.ts:459: ...
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/binary.ts:319: signal: options.signal,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/binary.ts:323: throw options.signal.reason;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/binary.ts:325: if (error instanceof Error && error.name === "AbortError") {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/binary.ts:496:export async function fetchWithUpgradeError(
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/binary.ts:505: if (error instanceof Error && error.name === "AbortError") {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/upgrade.ts:523: response = await fetchWithUpgradeError(
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/upgrade.ts:557: const response = await fetchWithUpgradeError(
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/upgrade.ts:797: const response = await ...
Your result was empty and invalid. Continue the SAME read-only immutable security review of base ec83887a16f780f32fba4b7d710bad262dba3a22 head 878459c490576dbb7abb76d3a3dc1b5d9fc62c1f. Return a substantive non-empty findings-first report now. If no defects, state PASS with exact head file:line evidence for all requested fail-closed cache/error typing, stable package version validation, abort/source provenance, target routing, 404/affinity/pagination/redirect/SHA/delta/cache/offline/CodeQL boundaries and residual gaps. If blocked, name exact command/error. Classify PASS/CONCERN/MUST-FIX/BLOCKED. Final exactly MERGE or DO-NOT-MERGE. Never modify; never return empty.
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:219: source: resolved.source,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:225: if (method !== "curl" || !(error instanceof UpgradeTransportError)) {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:273:function getArtifactChannel(target: string): ReleaseChannel {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:274: return isNightlyVersion(target) ? "nightly" : "stable";
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:335: const lookupMethod = isNightlyVersion(pinnedTarget) ? "curl" : method;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:695: executeUpgrade(method, target, downloadTag, offline, setMessage, source)
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/commands/cli/upgrade.ts:1018: ...
b70aff436960c0e5550972c6366734ba5e1db82a1662319a7c59f199e836bb5d -
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:7: applyPatchChainInMemory,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:28: resolveAndApply,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:63: getStableTargetSha256,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:412: let expectedSha256 = "";
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:441: expectedSha256 = getPatchTargetSha256(manifest, binaryName) ?? "";
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:444: if (previousVersion !== opts.targetVersion || !expectedSha256) {
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:463: expectedSha256,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/delta-upgrade.ts:480: const ...
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:234: const url = `${GHCR_REGISTRY}/token?scope=repository:${source.ghcrRepo}:pull`;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:237: { headers: { "User-Agent": getUserAgent() } },
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:274: const url = `${GHCR_REGISTRY}/v2/${source.ghcrRepo}/manifests/${tag}`;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:279: Authorization: `Bearer ${token}`,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:385: const blobUrl = `${GHCR_REGISTRY}/v2/${source.ghcrRepo}/blobs/${digest}`;
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:393: Authorization: `Bearer ${token}`,
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:396: redirect: "manual",
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f:packages/cli/src/lib/ghcr.ts:409: if ...
878459c490576dbb7abb76d3a3dc1b5d9fc62c1f 8e36adccb73967ef42f9dc3b0015e5032ed41942 e22f0eba66298a7228a988a4adcf1ec5be85227b 741bd72559d1033c0f801cba98e635f85f0b96dcca6f2180f5d9158ffb5251fc - M packages/cli/src/commands/cli/upgrade.ts M packages/cli/src/lib/binary.ts M packages/cli/src/lib/delta-upgrade.ts M packages/cli/src/lib/errors.ts M packages/cli/src/lib/ghcr.ts M packages/cli/src/lib/release-notes.ts M packages/cli/src/lib/upgrade.ts M packages/cli/src/lib/version-check.ts M packages/cli/test/commands/cli.test.ts M packages/cli/test/commands/cli/upgrade.test.ts M packages/cli/test/lib/binary.test.ts M packages/cli/test/lib/delta-upgrade.mocked.test.ts M packages/cli/test/lib/delta-upgrade.test.ts M packages/cli/test/lib/ghcr.test.ts M packages/cli/test/lib/release-notes.test.ts M packages/cli/test/lib/upgrade.test.ts M packages/cli/test/lib/version-check.test.ts
GitHub pagination does not preserve an external abort reason.
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:523-526 passes the caller’s signal into each pagination request. However, /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:503-512 only preserves an error named AbortError; it never checks whether init.signal is aborted and never throws init.signal.reason.
If page two aborts with an object, primitive, or ordinary Error reason, fetchWithUpgradeError() converts that exact reason into UpgradeTransportError. This violates the required abort-identity invariant and makes cancellation indistinguishable from transport failure.
Fix: before classifying the caught value, check init.signal?.aborted and throw init.signal.reason.
Regression: return a valid paginated first response, then abort with an arbitrary object during page two and throw that reason from the fetch mock. Assert fetchLatestFromGitHubWithSource(signal) rejects by object identity and makes exactly two requests. Existing tests cover the initial source probe at packages/cli/test/lib/binary.test.ts:457-470, but no test covers the pagination stage (packages/cli/test/lib/upgrade.test.ts:215-316).
packages/cli/src/commands/cli/upgrade.ts:225 requires UpgradeTransportError; HTTP and metadata UpgradeErrors never enter cache fallback. Command regressions cover populated-cache transport fallback and HTTP 403/malformed-200 rejection at packages/cli/test/commands/cli/upgrade.test.ts:406-450.packages/cli/src/lib/upgrade.ts:575-587,838; malformed/dev/beta/rc coverage appears at ...* 🟡 (04:25) Immutable review identifiers reported by tool output: head `878459c490576dbb7abb76d3a3dc1b5d9fc62c1f`, tree `8e36adccb73967ef42f9dc3b0015e5032ed41942`, and object/commit `e22f0eba66298a7228a988a4adcf1ec5be85227b`; base-to-head binary diff SHA-256 was `741bd72559d1033c0f801cba98e635f85f0b96dcca6f2180f5d9158ffb5251fc`. * 🟡 (04:25) Security review classified a `MUST-FIX`: GitHub pagina…
* 🔴 (04:23) User stated the prior result was empty and invalid. * 🟡 (04:23) [requested-review] User requested continuation of the SAME read-only immutable security review from base `ec83887a16f780f32fba4b7d710bad262dba3a22` to head `878459c490576dbb7abb76d3a3dc1b5d9fc62c1f`, requiring a substantive, non-empty, findings-first report. * 🔴 (04:23) User requires review findings classified as exact…
* 🔴 (04:22) User expanded `packages/cli/test/lib/release-notes.test.ts` to import `afterEach`, `beforeEach`, `UPGRADE_SOURCES`, `fetchRecentReleases`, `fetchChangelog`, and `mockFetch`, adding source-aware changelog/release-fetch coverage alongside the existing release-note parsing tests. * 🔴 (04:22) User added a `packages/cli/test/lib/release-notes.test.ts` test named `"rejects normalized rele…
* 🔴 (04:22) User specified the parameterized upgrade-test invariant `"never uses the cached target after %s"` in `packages/cli/test/commands/cli/upgrade.test.ts`; the test seeds cached version-check data with `setVersionCheckInfo("88.88.88")` and verifies the cached target is not reused after the tested responses. * 🔴 (04:22) User stated the migration scenario: “Switch to nightly and use npm me…
* 🔴 (04:21) User’s cache-isolation requirement in `packages/cli/test/lib/response-cache.test.ts` is that each identity uses its own namespace so users “never see each other's cached data.” * 🔴 (04:21) User’s property test in `packages/cli/test/lib/sentryclirc-import.property.test.ts` requires that transformed output “never equals the original input.”
* 🔴 (04:21) User supplied test changes in `packages/cli/test/lib/binary.test.ts` importing and exercising `getGitHubReleaseByTagUrl`, `resolveUpgradeSource`, `UPGRADE_SOURCES`, and `UpgradeSourceNotFoundError`. * 🔴 (04:21) User updated `getBinaryDownloadUrl("1.0.0")` expectations: URLs now contain `/cli@1.0.0/` and begin with `https://github.com/getsentry/toolkit/releases/download/`, replacing …
* 🔴 (04:21) User supplied a source-aware stable release-notes flow in `packages/cli/src/lib/release-notes.ts`: `fetchReleasesForChangelog(source: UpgradeSource)` requests `${getGitHubReleasesUrl(source)}?per_page=${CHANGELOG_MAX_RELEASES}` with `getGitHubHeaders()` and normalizes successful array responses via `normalizeStableReleases(data as GitHubRelease[], source)`. * 🔴 (04:21) User supplied…
* 🔴 (04:21) User supplied source-aware GitHub release URL helpers: `getGitHubReleasesUrl(source: UpgradeSource = PRIMARY_UPGRADE_SOURCE)` returns `https://api.github.com/repos/${source.githubRepo}/releases`; `getGitHubReleaseByTagUrl(version, source)` prefixes the version with `source.tagPrefix` and URL-encodes the tag; `getGitHubLatestReleaseUrl(source)` discovers the latest CLI release for a s…
Date: Sep 10, 2026 * 🔴 (04:20) User stated package managers always need network to fetch and install packages; therefore offline upgrades are supported only for curl-installed binaries, and validateMethod() throws UpgradeError("unsupported_operation", "Offline upgrade is only supported for curl-installed binaries.") when offline is true and method !== "curl". * 🔴 (04:20) User stated nightly bui…
🔴 (04:20) User supplied immutable Git-object metadata showing BASE ec83887a16f780f32fba4b7d710bad262dba3a22 with parent 110d6530e57ca4fbfb2a3f98e52013d2668b5b5e, HEAD output containing ec83887a16f780f32fba4b7d710bad262dba3a22 with parent 110d6530e57ca4fbfb2a3f98e52013d2668b5b5e, and target head 878459c490576dbb7abb76d3a3dc1b5d9fc62c1f with parents 8e36adccb73967ef42f9dc3b0015e5032ed41942 and e22…
🔴 (04:19) [requested-review] User requested a READ-ONLY final security/supply-chain gate review of getsentry/cli PR #1569 at exact base commit ec83887a16f780f32fba4b7d710bad262dba3a22 and head commit 878459c490576dbb7abb76d3a3dc1b5d9fc62c1f in repository /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. 🔴 (04:19) User required inspection t…