Dashboard › cli › Session 0Mqh0DyE13U9
0Mqh0DyE13U96GI68Read-only code review. In /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade, review exact diff ec83887a16f780f32fba4b7d710bad262dba3a22..dbee7e9f2a4555e044f74dfebad002cccfa43a1f for correctness and regressions. Do not modify anything. Inspect all changed files and relevant tests. Focus on 404-only fallback, source affinity, stable/nightly resolution, SemVer sorting, delta/full downloads, offline cache, prefetch, changelogs, cancellation, API compatibility, and tests. Return findings first with PASS/CONCERN/MUST-FIX, exact file:line evidence, and deterministic regressions for defects. State residual gaps if no defects. Output must be non-empty and final line exactly MERGE or DO-NOT-MERGE.
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/apps/local/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/apps/cli-docs/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/codemods/sentry-v3-to-v4/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md</path> <type>file</type> <content> 1: # Jared (Outpost agent) 2: 3: Autonomous GitHub coding agent. Work in `/workspace/repo`. 4: 5: ## Model tiers 6: 7: The primary model is chosen per event (see `src/agents/models.ts`): heavy for 8: code-producing situations, cheaper for lightweight ones. 9: 10: | Role | Subagent | Model | 11: | --- | --- | --- | 12: | Triage / plan / review (heavy) | (primary Jared) | Claude Opus 4.8 | 13: | Triage / plan / review (light) | (primary Jared) | xAI Grok 4.3 | 14: | Explore | `explore` | OpenAI gpt-5-mini | 15: | Implement | `implement` | Moonshot kimi-k2.7-code | 16: | Ship (commit/push/PR) | `ship` | xAI Grok (`grok-build-0.1`) | 17: 18: Pipeline: triage → explore → plan → implement → review → ship. 19: (`worker` is a deprecated alias of `implement`.) 20: 21: Operators also talk to Jared directly from the Outpost dashboard. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md</path> <type>file</type> <content> 1: # AGENTS.md 2: 3: Guidelines for AI agents working in this codebase. 4: 5: ## Project Overview 6: 7: **Sentry CLI** is a command-line interface for [Sentry](https://sentry.io), built with Node.js, [pnpm](https://pnpm.io), and [Stricli](https://bloomberg.github.io/stricli/). It lives in `packages/cli` of a pnpm workspace; the docs site lives in `apps/cli-docs`. ...
Found 64 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md: Line 35: > **Note**: Always check `package.json` for the latest scripts. All commands run Line 55: # Testing (Vitest) Line 56: pnpm run test:unit # Run unit tests Line 57: pnpm run test:e2e # Run e2e tests Line 58: pnpm exec vitest run test/lib/foo.test.ts # Run a single test file (skips generate steps) Line 59: pnpm exec vitest # Watch mode Line 74: Node — do not reintroduce `Bun.*` globals, `bun:test`, or `bun` CLI commands.) Line 125: - **`test/`** — tests mirroring `src/` (unit, `*.property.test.ts`, Line 126: `*.model-based.test.ts`, `e2e/`, `fixtures/`, `mocks/`). Line 427: All command docs and skill files are generated via `pnpm run generate:docs` (which runs `generate:command-docs` then `generate:skill`). ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/CONTRIBUTING.md</path> <type>file</type> <content> 1: # Contributing to Sentry CLI 2: 3: This guide documents the patterns and conventions used in this CLI for consistency. 4: 5: ## Command Patterns 6: 7: We follow [gh CLI](https://cli.github.com/) conventions for best-in-class developer experience. 8: 9: ### List Commands 10: 11: List commands use **optional positional arguments** for context with smart auto-detection. 12: 13: ```bash 14: sentry org list [--limit N] [--json] 15: sentry project list [org] [--limit N] [--json] 16: sentry issue list [<org>/<project>] [--json] 17: ``` 18: 19: **Target syntax**: 20: - `<org>/<project>` - Explicit organization and project (e.g., `my-org/frontend`) 21: - `<org>/` - All projects in the specified organization 22: - `<project>` - Search for project by name across all accessible organizations 23: - *(omit)* - ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "toolkit-cli-workspace",
3: "version": "0.0.0",
4: "private": true,
5: "type": "module",
6: "packageManager": "pnpm@10.11.0",
7: "engines": {
8: "node": ">=20.0"
9: },
10: "scripts": {
11: "tsx": "pnpm --filter sentry run tsx",
12: "cli": "pnpm --filter sentry run cli",
13: "dev": "pnpm --filter sentry run dev",
14: "build": "pnpm --filter sentry run build",
15: "build:all": "pnpm --filter sentry run build:all",
16: "bundle": "pnpm --filter sentry run bundle",
17: "typecheck": "pnpm --filter sentry run typecheck",
18: "lint": "pnpm --filter sentry run lint",
19: "lint:fix": "pnpm --filter sentry run lint:fix",
20: "test": "pnpm --filter sentry run test",
21: "test:unit": "pnpm --filter sentry run test:unit",
22: "test:changed": "pnpm --filter ...
Found 16 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md: Line 17: * **binpatch TRDIFF10 wire format + OCI tag scheme constants**: binpatch wire format (TRDIFF10): 8-byte magic \`TRDIFF10\x00\`, then LE int64 \`controlLen\`/\`diffLen\`/\`newSize\` (sign-magnitude), 24-byte control tuples with \`readDiffBy\`/\`readExtraBy\`/\`seekBy\` fields, zstd-compressed control/diff/extra blocks. OCI tag scheme: \`\<repo>:nightly\` (mutable pointer), \`\<repo>:nightly-\<version>\` (immutable), \`\<repo>:patch-\<version>\` (patches). Annotations: \`from-version=\<prev>\` (pointer, NOT hash — trust model), \`sha256-\<binaryName>=\<hex>\` (final binary hash only), \`org.opencontainers.image.title\`. Artifact type: \`application/vnd.\<prefix>.patch\`. Security limits: \`MAX\_OUTPUT\_SIZE=2\_147\_483\_648\` (2 GiB), \`MAX\_NIGHTLY\_CHAIN\_DEPTH=30\`, \`MAX\_STABLE\_CHAIN\_DEPTH=10\`, \`SIZE\_THRESHOLD\_RATIO=0.6\`. ...
dbee7e9f2a4555e044f74dfebad002cccfa43a1f ec83887a16f780f32fba4b7d710bad262dba3a22 dbee7e9f2a4555e044f74dfebad002cccfa43a1f a84a52f92f46526431f9d1572166676d68542683797142d9116d2bee61de48ea -
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/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 packages/cli/src/commands/cli/upgrade.ts | 114 +++++--- packages/cli/src/lib/binary.ts | 154 +++++++++- packages/cli/src/lib/delta-upgrade.ts | 139 +++++++-- packages/cli/src/lib/ghcr.ts | 68 +++-- packages/cli/src/lib/release-notes.ts | 119 +++++--- ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_088e773a1001cSfuZ0U1B4W6Nr
const cmd =
versionArg && !CHANNEL_VERSIONS.has(versionArg)
? `sentry cli upgrade ${target}`
: "sentry cli upgrade";
result.warnings = [`Run '${cmd}' to update.`];
}
return result;
}
/**
* Maximum number of spawn attempts for the new binary.
*
* On Windows, Defender/SmartScreen may lock a newly-written executable for
* antivirus scanning after the file handle is closed. This causes EBUSY from
* uv_spawn. Retrying with backoff lets the scan complete without a fixed sleep.
*/
const SPAWN_MAX_ATTEMPTS = 5;
/** Base delay (ms) between spawn retry attempts. Delay = attempt * base. */
const SPAWN_RETRY_BASE_MS = 500;
/**
* Check whether an error is an EBUSY system error from spawn.
*
* On Windows, Defender/SmartScreen locks newly-written executables for
* scanning. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_088e773a1001cSfuZ0U1B4W6Nr</path> <type>file</type> <content> 1: diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts 2: index 8d8282368..610a12025 100644 3: --- a/packages/cli/src/commands/cli/upgrade.ts 4: +++ b/packages/cli/src/commands/cli/upgrade.ts 5: @@ -1,414 +1,450 @@ 6: /** 7: * sentry cli upgrade 8: * 9: * Self-update the Sentry CLI to the latest or a specific version. 10: * After upgrading, spawns the NEW binary with `cli setup` to update 11: * completions, agent skills, and record installation metadata. 12: * 13: * Supports two release channels: 14: * - stable (default): tracks the latest GitHub release 15: * - nightly: tracks the rolling nightly prerelease built from main 16: * 17: * The channel can be set via --channel or by passing "nightly"/"stable" 18: * as the version argument. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_088e773a1001cSfuZ0U1B4W6Nr</path>
<type>file</type>
<content>
1201: *
1202: * Matches the naming convention used by GitHub Releases and GHCR:
1203: * `sentry-<os>-<arch>[-musl][.exe]` (e.g., `sentry-linux-x64`, `sentry-linux-arm64-musl`).
1204: */
1205: export function getPlatformBinaryName(): string {
1206: let os: string;
1207: if (process.platform === "darwin") {
1208: os = "darwin";
1209: } else if (process.platform === "win32") {
1210: os = "windows";
1211: } else {
1212: os = "linux";
1213: }
1214: const arch = process.arch === "arm64" ? "arm64" : "x64";
1215: const libcSuffix = isMusl() ? "-musl" : "";
1216: const suffix = process.platform === "win32" ? ".exe" : "";
1217: return `sentry-${os}-${arch}${libcSuffix}${suffix}`;
1218: }
1219:
1220: /**
1221: * Build the download URL for a platform-specific binary from GitHub releases. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_088e7d021001OkOag2S3VdegUf
// No AbortSignal.timeout here: this fetch covers both connection AND
// body streaming. For full nightly binaries (~30 MB), a 30s timeout
// would require sustained ~8 Mbps throughput and fail on slow connections.
// The GHCR step 1 timeout above guards against GHCR-side latency;
// Azure Blob Storage has reliable latency characteristics.
let redirectResponse: Response;
try {
redirectResponse = await customFetch(redirectUrl, {
headers: { "User-Agent": getUserAgent() },
signal,
});
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
throw new UpgradeError(
"network_error",
`Failed to download from blob storage: ${msg}`
);
}
if (!redirectResponse.ok) {
throw new UpgradeError(
"network_error",
...
<path>/home/byk/.local/share/opencode/tool-output/tool_088e7d021001OkOag2S3VdegUf</path> <type>file</type> <content> 1: diff --git a/packages/cli/src/lib/ghcr.ts b/packages/cli/src/lib/ghcr.ts 2: index 43d6deb7e..5bc671a6a 100644 3: --- a/packages/cli/src/lib/ghcr.ts 4: +++ b/packages/cli/src/lib/ghcr.ts 5: @@ -1,520 +1,546 @@ 6: /** 7: * GHCR (GitHub Container Registry) Client 8: * 9: * Encapsulates the OCI download protocol for fetching nightly CLI binaries 10: * from ghcr.io/getsentry/cli. Nightly builds are pushed as OCI artifacts 11: * via ORAS with the version baked into the manifest annotation. 12: * 13: * Key design decisions: 14: * - Anonymous access: nightly package is public; no token needed beyond the 15: * standard ghcr.io anonymous token exchange. 16: * - Version discovery from manifest annotation: `annotations.version` in the 17: * OCI manifest holds the nightly version. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_088e7d021001OkOag2S3VdegUf</path>
<type>file</type>
<content>
1201: * For stable upgrades, accepts optional pre-fetched releases to avoid
1202: * a redundant GitHub Releases API call when the delta-upgrade flow has
1203: * already fetched the same data.
1204: */
1205: export async function fetchChangelog(
1206: opts: FetchChangelogOptions
1207: ): Promise<ChangelogSummary | null> {
1208: try {
1209: - const { channel, fromVersion, toVersion, maxItems, prefetchedReleases } =
1210: - opts;
1211: + const {
1212: + channel,
1213: + fromVersion,
1214: + toVersion,
1215: + maxItems,
1216: + prefetchedReleases,
1217: + source = PRIMARY_UPGRADE_SOURCE,
1218: + } = opts;
1219: if (channel === "nightly") {
1220: - return await fetchNightlyChangelog(fromVersion, toVersion, maxItems);
1221: + return await fetchNightlyChangelog(
1222: + fromVersion,
1223: + ...
diff --git a/packages/cli/src/lib/upgrade.ts b/packages/cli/src/lib/upgrade.ts
index 945adc48d..64228ba41 100644
--- a/packages/cli/src/lib/upgrade.ts
+++ b/packages/cli/src/lib/upgrade.ts
@@ -1,119 +1,158 @@
/**
* Upgrade Module
*
* Detects how the CLI was installed and provides self-upgrade functionality.
* Binary management helpers (download URLs, locking, replacement) live in
* binary.ts and are shared with the setup --install flow.
*/
import { spawn } from "node:child_process";
import {
chmodSync,
closeSync,
existsSync,
openSync,
realpathSync,
statSync,
unlinkSync,
writeSync,
} from "node:fs";
import { writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, isAbsolute, join, sep } from "node:path";
import { setTimeout } from "node:timers/promises";
+import { valid as semverValid } from "semver";
import {
acquireLock,
cleanupOldBinary,
+ compareVersions,
determineInstallDir,
...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts</path>
<type>file</type>
<content>
430: digests.map(async (digest) => {
431: const data = new Uint8Array(
432: await client.downloadBlobBuffer(opts.token, digest, opts.signal)
433: );
434: return { data, size: data.byteLength };
435: })
436: );
437: return {
438: patches,
439: totalSize: patches.reduce((sum, patch) => sum + patch.size, 0),
440: expectedSha256,
441: steps,
442: };
443: }
444:
445: export function applyPatchChain(
446: chain: PatchChain,
447: oldBinaryPath: string,
448: destPath: string,
449: onBytes?: (bytes: number) => void
450: ): Promise<string> {
451: return withTracingSpan(
452: "apply-patches",
453: "upgrade.delta.apply",
454: async (span) => {
455: span.setAttribute("patches.count", chain.patches.length);
456: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts</path>
<type>file</type>
<content>
688: export function prefetchNightlyPatches(
689: targetVersion: string,
690: signal?: AbortSignal,
691: source: UpgradeSource = getPrimaryUpgradeSource()
692: ): Promise<void> {
693: return prefetch(nightlySource(source), targetVersion, signal);
694: }
695:
696: export function prefetchStablePatches(
697: targetVersion: string,
698: signal?: AbortSignal,
699: source: UpgradeSource = getPrimaryUpgradeSource()
700: ): Promise<void> {
701: return prefetch(stableSource(source), targetVersion, signal);
702: }
(End of file - total 702 lines)
</content>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/version-check.ts</path>
<type>file</type>
<content>
1: /**
2: * Background version check for "new version available" notifications.
3: *
4: * For nightly builds (CLI_VERSION contains "-dev.<timestamp>"), checks GHCR for the
5: * latest nightly version via the OCI manifest annotation. For stable builds,
6: * checks GitHub Releases. Results are cached in the database and shown on
7: * subsequent runs.
8: */
9:
10: // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
11: import * as Sentry from "@sentry/node-core/light";
12: import { compare as semverCompare } from "semver";
13: import type { UpgradeSource } from "./binary.js";
14: import { CLI_VERSION } from "./constants.js";
15: import { getReleaseChannel } from "./db/release-channel.js";
16: import {
17: getVersionCheckInfo,
18: markUpdateNotified,
19: ...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 170: test("parses valid methods", () => {
Line 179: test("parses case-insensitively", () => {
Line 185: test("throws on invalid method", () => {
Line 193: test("selects the latest CLI-prefixed Toolkit release", async () => {
Line 214: test("falls back to the legacy latest release only on Toolkit HTTP 404", async () => {
Line 233: test("does not use an MCP release as the latest CLI release", async () => {
Line 250: test("returns version from GitHub API", async () => {
Line 263: test("strips v prefix from version", async () => {
Line 276: test("handles version without v prefix", async () => {
Line 289: test("throws on HTTP error", async () => {
Line 303: test("throws on network failure (DNS, timeout, etc.)", async () => {
Line 314: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
180: expect(parseInstallationMethod("NPM")).toBe("npm");
181: expect(parseInstallationMethod("Curl")).toBe("curl");
182: expect(parseInstallationMethod("YARN")).toBe("yarn");
183: });
184:
185: test("throws on invalid method", () => {
186: expect(() => parseInstallationMethod("pip")).toThrow("Invalid method: pip");
187: expect(() => parseInstallationMethod("apt")).toThrow("Invalid method: apt");
188: expect(() => parseInstallationMethod("")).toThrow("Invalid method: ");
189: });
190: });
191:
192: describe("fetchLatestFromGitHub", () => {
193: test("selects the latest CLI-prefixed Toolkit release", async () => {
194: const requests: string[] = [];
195: mockFetch(async (url) => {
196: requests.push(String(url));
197: return new Response(
198: ...
Found 2 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:
Line 692: export async function versionExists(
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts:
Line 297: if (!(await versionExists(lookupMethod, target))) {
Found 22 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 1827: test("aborts early if signal is already aborted", async () => {
Line 1828: const controller = new AbortController();
Line 1829: controller.abort();
Line 1833: ).rejects.toMatchObject({ name: "AbortError" });
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/version-check.test.ts:
Line 21: abortPendingVersionCheck,
Line 402: describe("abortPendingVersionCheck", () => {
Line 405: expect(() => abortPendingVersionCheck()).not.toThrow();
Line 411: abortPendingVersionCheck();
Line 412: abortPendingVersionCheck();
Line 413: abortPendingVersionCheck();
Line 439: // Abort any pending check to clean up
Line 440: abortPendingVersionCheck();
Line 468: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
1780: "Content-Type": "application/vnd.oci.image.manifest.v1+json",
1781: },
1782: }
1783: );
1784: }
1785: return new Response("Not Found", { status: 404 });
1786: });
1787:
1788: const version = await fetchLatestNightlyVersion();
1789: expect(version).toBe("0.0.0-dev.1740000000");
1790: expect(callCount).toBe(2); // token + manifest
1791: });
1792:
1793: test("throws UpgradeError when GHCR token exchange fails", async () => {
1794: mockFetch(async (url) =>
1795: String(url) === "https://api.github.com/repos/getsentry/toolkit"
1796: ? new Response(null, { status: 200 })
1797: : new Response("Unauthorized", { status: 401 })
1798: );
1799:
1800: await ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
1690: schemaVersion: 2,
1691: layers: [],
1692: annotations: { version: "0.0.0-dev.1740000000" },
1693: }),
1694: { status: 200 }
1695: );
1696: }
1697: return new Response("Unexpected", { status: 500 });
1698: });
1699:
1700: await expect(fetchLatestNightlyVersion()).resolves.toBe(
1701: "0.0.0-dev.1740000000"
1702: );
1703: expect(requests).toContain(
1704: "https://ghcr.io/v2/getsentry/cli/manifests/nightly"
1705: );
1706: });
1707:
1708: test("does not fall back from a non-404 Toolkit nightly failure", async () => {
1709: const requests: string[] = [];
1710: mockFetch(async (url) => {
1711: const request = String(url);
1712: requests.push(request);
1713: if ...
Found 54 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 120: getBinaryDownloadUrl,
Line 208: await expect(fetchLatestFromGitHub()).resolves.toBe("1.3.0");
Line 226: await expect(fetchLatestFromGitHub()).resolves.toBe("1.2.3");
Line 242: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 259: const version = await fetchLatestFromGitHub();
Line 272: const version = await fetchLatestFromGitHub();
Line 285: const version = await fetchLatestFromGitHub();
Line 297: await expect(fetchLatestFromGitHub()).rejects.toThrow(UpgradeError);
Line 298: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 308: await expect(fetchLatestFromGitHub()).rejects.toThrow(UpgradeError);
Line 309: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 323: await ...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/ghcr.test.ts:
Line 66: "org.opencontainers.image.source": "https://github.com/getsentry/cli",
Line 96: test("uses the selected source's GHCR repository", async () => {
Line 171: test("uses the selected source's GHCR repository", async () => {
Line 184: mockFetch(async () => new Response("Not Found", { status: 404 }));
Line 188: 'Failed to fetch manifest for tag "nightly": HTTP 404'
Line 414: test("throws UpgradeError on HTTP 404", async () => {
Line 415: mockFetch(async () => new Response("Not Found", { status: 404 }));
Line 423: status: 404,
Line 424: message: 'Failed to fetch manifest for tag "patch-0.13.0": HTTP ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_088eab942001n6A2a6uqpvlQv4
+ });
+
+ await fetchRecentReleases(undefined, LEGACY_UPGRADE_SOURCE);
+
+ expect(urls).toEqual([
+ "https://api.github.com/repos/getsentry/cli/releases?per_page=12",
+ ]);
+ });
+
test("returns empty array on HTTP error", async () => {
mockFetch(async () => new Response("Server Error", { status: 500 }));
const result = await fetchRecentReleases();
expect(result).toEqual([]);
});
test("returns empty array on network failure", async () => {
@@ -916,82 +940,99 @@ describe("resolveStableChain", () => {
return new Response(patchData.buffer as ArrayBuffer, {
status: 200,
});
}
return new Response("Not Found", { status: 404 });
});
}
- test("resolves single-hop chain with mocked fetch", async () => {
+ test("resolves prefixed Toolkit CLI releases and ignores other ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts</path>
<type>file</type>
<content>
420: } catch {
421: log.debug("Failed to persist install info (DB may be read-only)");
422: }
423: }
424:
425: return legacyMethod;
426: }
427:
428: // Version Fetching
429:
430: /**
431: * Fetch the latest version from GitHub releases.
432: *
433: * @param signal - Optional AbortSignal to cancel the request
434: * @returns Latest version string (without 'v' prefix)
435: * @throws {UpgradeError} When fetch fails or response is invalid
436: * @throws {Error} AbortError if signal is aborted
437: */
438: export async function fetchLatestFromGitHubWithSource(
439: signal?: AbortSignal,
440: sources: readonly UpgradeSource[] = UPGRADE_SOURCES
441: ): Promise<ResolvedUpgradeVersion> {
442: const { source, response } = await resolveUpgradeSource({
443: getProbeUrl: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts</path>
<type>file</type>
<content>
220: os = "linux";
221: }
222: const arch = process.arch === "arm64" ? "arm64" : "x64";
223: const libcSuffix = isMusl() ? "-musl" : "";
224: const suffix = process.platform === "win32" ? ".exe" : "";
225: return `sentry-${os}-${arch}${libcSuffix}${suffix}`;
226: }
227:
228: /**
229: * Build the download URL for a platform-specific binary from GitHub releases.
230: *
231: * @param version - Version to download (without 'v' prefix)
232: * @returns Download URL for the binary
233: */
234: export function getBinaryDownloadUrl(
235: version: string,
236: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE
237: ): string {
238: const tag = `${source.tagPrefix}${version}`;
239: return `https://github.com/${source.githubRepo}/releases/download/${tag}/${getPlatformBinaryName()}`;
240: }
241: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/ghcr.ts</path>
<type>file</type>
<content>
60: */
61: function buildSignal(
62: timeout: number,
63: externalSignal?: AbortSignal
64: ): AbortSignal {
65: const timeoutSignal = AbortSignal.timeout(timeout);
66: return externalSignal
67: ? AbortSignal.any([timeoutSignal, externalSignal])
68: : timeoutSignal;
69: }
70:
71: /**
72: * Returns true when the given error was triggered by the external
73: * (caller-provided) abort signal rather than by our timeout.
74: */
75: function isExternalAbort(error: Error, externalSignal?: AbortSignal): boolean {
76: return Boolean(externalSignal?.aborted && error.name === "AbortError");
77: }
78:
79: type RetryOptions = {
80: timeout?: number;
81: signal?: AbortSignal;
82: };
83:
84: /**
85: * Fetch with timeout and retry for GHCR requests. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_088eab942001n6A2a6uqpvlQv4</path>
<type>file</type>
<content>
1: diff --git a/packages/cli/test/commands/cli.test.ts b/packages/cli/test/commands/cli.test.ts
2: index cd1514eb0..187ae70f4 100644
3: --- a/packages/cli/test/commands/cli.test.ts
4: +++ b/packages/cli/test/commands/cli.test.ts
5: @@ -119,17 +119,17 @@ describe("upgradeCommand.func", () => {
6:
7: // Note: We skip testing "unknown installation method" case because
8: // detectInstallationMethod() runs actual shell commands (npm list, etc.)
9: // which can be slow/flaky in CI. The unknown method handling is tested
10: // indirectly through the upgrade.ts unit tests in lib/upgrade.test.ts.
11:
12: test("shows installation info with specified method", async () => {
13: globalThis.fetch = (async () =>
14: - new Response(JSON.stringify({ tag_name: "v0.0.0-dev" }), {
15: + new Response(JSON.stringify([{ tag_name: "cli@0.0.0-dev" }]), {
16: ...
dbee7e9f2a4555e044f74dfebad002cccfa43a1f a84a52f92f46526431f9d1572166676d68542683797142d9116d2bee61de48ea -
resolveNightlyManifest() probes the GitHub repository before contacting GHCR:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:533-545/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:341-350A GitHub 401, 403, 429, 5xx, or transport failure now prevents a nightly upgrade even when the selected GHCR package and manifest are available. This also contradicts the documented two-request nightly path at upgrade.ts:503-508.
Fix: Resolve nightly sources directly through each source’s GHCR token and manifest. Advance to the next source only for a typed manifest HTTP 404. Propagate every other failure.
Deterministic regression: Mock the GitHub repository endpoint as 429 and provide a valid Toolkit GHCR token and nightly manifest. Assert that nightly resolution succeeds without any GitHub API request. Also assert that a Toolkit manifest 404 advances to legacy while 403/500 and transport failures never do.
The prefixed source uses one fixed page:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:258-264fetchLatestFromGitHubWithSource() consumes only that response and throws when it contains no CLI tag:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:442-457In a monorepo, 100 newer releases for other packages can hide the latest cli@ release on page two. Stable checks and upgrades then fail with “No version found” despite a valid CLI release. Existing tests cover mixed products on one page but not pagination. ...