Dashboard › cli › Session 0f65cuTQhG2D
0f65cuTQhG2DCVsQ9READ-ONLY final security/supply-chain merge-gate review of getsentry/cli PR #1569. Exact base ec83887a16f780f32fba4b7d710bad262dba3a22, exact head b6484e0fb1eb0f8bce68272d538f5c3b26cd5bd5, worktree /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Verify exact clean immutable state and inspect all changed files/tests. Focus on typed 404-only fallback, fail-closed non-404/malformed metadata, source affinity, trusted canonical pagination reconstruction, strict nightly version annotations, raw Toolkit changelog prefix provenance vs normalized prefetched inputs, GHCR token/redirect handling, cancellation, SHA/delta/cache/offline, installer unchanged. Intentional: nightly GitHub repository probe; source-neutral verified version-pair cache. Return NON-EMPTY findings-first report with PASS/CONCERN/MUST-FIX/BLOCKED labels, exact current file:line evidence, deterministic regression for defects, residual gaps. Final line exactly MERGE or DO-NOT-MERGE. Never modify files.
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade</path> <type>directory</type> <entries> .craft.yml .git .github/ .gitignore .lore.md .nojekyll .npmrc AGENTS.md apps/ CHANGELOG.md codemods/ docs/ LICENSE.md node_modules/ package.json packages/ pnpm-lock.yaml pnpm-workspace.yaml README.md (19 entries) </entries>
<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. ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md</path>
<type>file</type>
<content>
1: <!-- Managed by lore (https://github.com/BYK/loreai) — manual edits are imported on next session. -->
2:
3: ## Long-term Knowledge
4:
5: ### Architecture
6:
7: <!-- lore:01a02357-f0a8-7fdb-9e9d-b0710cfe65c9 -->
8: * **API output binary and HTTP contracts**: Always preserve strict output-mode and binary-safety contracts. `sentry api` must preserve `rawApiRequest()` status text; empty textual non-2xx bodies must produce an HTTP status/request fallback, and JSON mode must expose `{status, statusText, body}` so empty success and error responses are distinguishable. Binary `Uint8Array` successes bypass formatters and remain raw; binary errors emit only status/content-type/byte-count summaries, never bytes or JSON coercion. Prompts must never block scripted runs or interleave with stdout JSON.
9:
10: <!-- ...
<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`. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "sentry",
3: "version": "0.45.0-dev.0",
4: "description": "Sentry CLI - A command-line interface for using Sentry built by robots and humans for robots and humans",
5: "repository": {
6: "type": "git",
7: "url": "git+https://github.com/getsentry/cli.git"
8: },
9: "main": "./dist/index.cjs",
10: "type": "module",
11: "types": "./dist/index.d.cts",
12: "exports": {
13: ".": {
14: "import": {
15: "types": "./dist/index.d.mts",
16: "default": "./dist/index.mjs"
17: },
18: "require": {
19: "types": "./dist/index.d.cts",
20: "default": "./dist/index.cjs"
21: }
22: }
23: },
24: "bin": {
25: "sentry": "./dist/bin.cjs"
26: },
27: "files": [
28: "dist/bin.cjs",
29: "dist/index.cjs",
30: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/.cursor/rules/ultracite.mdc</path>
<type>file</type>
<content>
1: ---
2: description: Ultracite Rules - AI-Ready Formatter and Linter
3: globs: "**/*.{ts,tsx,js,jsx,json,jsonc,html,vue,svelte,astro,css,yaml,yml,graphql,gql,md,mdx,grit}"
4: alwaysApply: false
5: ---
6:
7: # Ultracite Code Standards
8:
9: This project uses **Ultracite**, a zero-config Biome preset that enforces strict code quality standards through automated formatting and linting.
10:
11: ## Quick Reference
12:
13: - **Format code**: `npx ultracite fix`
14: - **Check for issues**: `npx ultracite check`
15: - **Diagnose setup**: `npx ultracite doctor`
16:
17: Biome (the underlying engine) provides extremely fast Rust-based linting and formatting. Most issues are automatically fixable. ...
b6484e0fb1eb0f8bce68272d538f5c3b26cd5bd5 ec83887a16f780f32fba4b7d710bad262dba3a22 b6484e0fb1eb0f8bce68272d538f5c3b26cd5bd5 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 bb42358da5b31162013c1f8a8fb5ad28049c988485d0df51ab9738035efd140b -
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0891eb8e3001Yk9NKJDgohjGrp
* and env — so it can be unit-tested; `getKnownCurlPaths` memoizes the result.
*/
export function buildKnownCurlPaths(
homeDir: string,
env: NodeJS.ProcessEnv
): string[] {
const paths = KNOWN_CURL_DIRS.map((dir) => join(homeDir, dir) + sep);
// Honor an absolute XDG_BIN_HOME, matching determineInstallDir's precedence.
const xdgBinHome = env.XDG_BIN_HOME;
if (xdgBinHome && isAbsolute(xdgBinHome)) {
// join(dir, ".") strips any trailing separator so we don't emit a double
// separator (e.g. `/custom/bin//`) that would break the startsWith checks.
paths.push(join(xdgBinHome, ".") + sep);
}
return paths;
}
/**
* Memoized known curl paths. Computed lazily (not at module load) to avoid TDZ
* issues from circular imports — `KNOWN_CURL_DIRS` must be fully initialized
* before access. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891eb8e3001Yk9NKJDgohjGrp</path> <type>file</type> <content> 1: packages/cli/src/commands/cli/upgrade.ts | 126 ++++-- 2: packages/cli/src/lib/binary.ts | 154 +++++++- 3: packages/cli/src/lib/delta-upgrade.ts | 139 +++++-- 4: packages/cli/src/lib/ghcr.ts | 80 ++-- 5: packages/cli/src/lib/release-notes.ts | 119 ++++-- 6: packages/cli/src/lib/upgrade.ts | 397 +++++++++++++++---- 7: packages/cli/src/lib/version-check.ts | 21 +- 8: packages/cli/test/commands/cli.test.ts | 23 +- 9: packages/cli/test/commands/cli/upgrade.test.ts | 136 ++++++- 10: packages/cli/test/lib/binary.test.ts | 130 +++++- 11: packages/cli/test/lib/delta-upgrade.mocked.test.ts | 14 +- 12: packages/cli/test/lib/delta-upgrade.test.ts | 201 +++++++++- 13: packages/cli/test/lib/ghcr.test.ts ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891eb8e3001Yk9NKJDgohjGrp</path>
<type>file</type>
<content>
1476: --- a/packages/cli/src/lib/delta-upgrade.ts
1477: +++ b/packages/cli/src/lib/delta-upgrade.ts
1478: @@ -1,368 +1,438 @@
1479: /** Delta upgrade discovery and application backed by binpatch. */
1480:
1481: import { join } from "node:path";
1482: // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
1483: import * as Sentry from "@sentry/node-core/light";
1484: import {
1485: applyPatchChainInMemory,
1486: extractStableChain as binpatchExtractStableChain,
1487: filterAndSortChainTags as binpatchFilterAndSortChainTags,
1488: validateChainStep as binpatchValidateChainStep,
1489: type DeltaTelemetry,
1490: type DeltaUnavailableReason,
1491: type ExtractStableChainOpts,
1492: type GitHubRelease,
1493: getPatchFromVersion,
1494: getPatchTargetSha256,
1495: ghcrSource,
1496: ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891eb8e3001Yk9NKJDgohjGrp</path>
<type>file</type>
<content>
3037: const ts = Number.parseInt(match[1], 10);
3038: return Number.isNaN(ts) ? null : ts;
3039: }
3040:
3041: /** GitHub Commits API response entry (subset) */
3042: type GitHubCommit = {
3043: commit: {
3044: message: string;
3045: };
3046: };
3047:
3048: /**
3049: * Parse conventional commit messages into changelog sections.
3050: *
3051: * Only `feat:`, `fix:`, and `perf:` prefixes pass through.
3052: * Commits with `#skip-changelog` in the body are filtered out.
3053: * Only the first line of multi-line messages is used. ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0891f2f08001y3JkQF5NjPZyN5
- new Response(
- JSON.stringify({
- tag_name: "v1.2.3",
- }),
+ test("selects the latest CLI-prefixed Toolkit release", async () => {
+ const requests: string[] = [];
+ mockFetch(async (url) => {
+ requests.push(String(url));
+ return new Response(
+ JSON.stringify([
+ { tag_name: "mcp@9.0.0" },
+ { tag_name: "cli@not-a-version" },
+ { tag_name: "cli@1.2.3" },
+ { tag_name: "cli@1.3.0" },
+ ]),
+ { status: 200 }
+ );
+ });
+
+ await expect(fetchLatestFromGitHub()).resolves.toBe("1.3.0");
+ expect(requests).toEqual([
+ "https://api.github.com/repos/getsentry/toolkit/releases?per_page=100",
+ ]);
+ });
+
+ test("follows Toolkit release pagination to find the latest CLI release", async () => {
+ const requests: string[] = [];
+ ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891f2f08001y3JkQF5NjPZyN5</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: @@ -77,160 +77,163 @@ describe("feedbackCommand.func", () => {
6: });
7:
8: test("throws ValidationError for whitespace-only message", async () => {
9: const func = await feedbackCommand.loader();
10: const mockContext = {
11: stdout: { write: vi.fn(() => true) },
12: stderr: { write: vi.fn(() => true) },
13: };
14:
15: await expect(func.call(mockContext, {}, " ")).rejects.toThrow(
16: "Please provide a feedback message."
17: );
18: });
19:
20: test("throws ConfigError when Sentry is disabled", async () => {
21: const func = await feedbackCommand.loader();
22: const ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891f2f08001y3JkQF5NjPZyN5</path>
<type>file</type>
<content>
1499: browser_download_url: `https://example.com/${BINARY_NAME}`,
1500: },
1501: {
1502: name: `${BINARY_NAME}.patch`,
1503: size: 500,
1504: browser_download_url: patchUrl,
1505: },
1506: {
1507: name: `${BINARY_NAME}.gz`,
1508: size: 100_000,
1509: browser_download_url: `https://example.com/${BINARY_NAME}.gz`,
1510: },
1511: ],
1512: },
1513: {
1514: - tag_name: "0.13.0",
1515: + tag_name: "cli@0.13.0",
1516: assets: [
1517: {
1518: name: BINARY_NAME,
1519: size: 100_000,
1520: browser_download_url: `https://example.com/${BINARY_NAME}`,
1521: },
1522: ],
1523: },
1524: ];
1525:
1526: // A ...
<path>/home/byk/.local/share/opencode/tool-output/tool_0891f2f08001y3JkQF5NjPZyN5</path>
<type>file</type>
<content>
3062: // ──────────────────── extractNightlyTimestamp ───────────────────────────────
3063:
3064: describe("extractNightlyTimestamp", () => {
3065: test("extracts timestamp from standard nightly format", () => {
3066: expect(extractNightlyTimestamp("0.22.0-dev.1772661724")).toBe(
3067: 1_772_661_724
3068: );
3069: });
3070:
3071: test("returns null for stable versions", () => {
3072: expect(extractNightlyTimestamp("0.21.0")).toBeNull();
3073: });
3074:
3075: test("returns null for invalid format", () => {
3076: expect(extractNightlyTimestamp("not-a-version")).toBeNull();
3077: expect(extractNightlyTimestamp("")).toBeNull();
3078: });
3079: });
3080:
3081: // ──────────────────── countListItems ────────────────────────────────────────
3082:
3083: describe("countListItems", () => {
3084: test("counts ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts</path>
<type>file</type>
<content>
1: /**
2: * Binary Management
3: *
4: * Shared utilities for installing, replacing, and managing the CLI binary.
5: * Used by both `setup --install` (fresh installs) and `upgrade` (self-updates).
6: */
7:
8: import { spawnSync } from "node:child_process";
9: import {
10: existsSync,
11: mkdirSync,
12: readFileSync,
13: renameSync,
14: unlinkSync,
15: writeFileSync,
16: } from "node:fs";
17: import { chmod, copyFile, mkdir, realpath, unlink } from "node:fs/promises";
18: import { delimiter, dirname, isAbsolute, join, resolve, sep } from "node:path";
19: import { compare as semverCompare } from "semver";
20: import { getUserAgent } from "./constants.js";
21: import {
22: buildTlsErrorDetail,
23: customFetch,
24: isTlsCertError,
25: } from "./custom-ca.js";
26: import { stringifyUnknown, ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/ghcr.ts</path> <type>file</type> <content> 1: /** 2: * GHCR (GitHub Container Registry) Client 3: * 4: * Encapsulates the OCI download protocol for fetching nightly CLI binaries 5: * from ghcr.io/getsentry/cli. Nightly builds are pushed as OCI artifacts 6: * via ORAS with the version baked into the manifest annotation. 7: * 8: * Key design decisions: 9: * - Anonymous access: nightly package is public; no token needed beyond the 10: * standard ghcr.io anonymous token exchange. 11: * - Version discovery from manifest annotation: `annotations.version` in the 12: * OCI manifest holds the nightly version. Checking the latest version only 13: * requires a token exchange + manifest fetch (2 HTTP requests total). 14: * - Redirect quirk: ghcr.io blob downloads return 307 to Azure Blob Storage. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts</path>
<type>file</type>
<content>
1: /**
2: * Upgrade Module
3: *
4: * Detects how the CLI was installed and provides self-upgrade functionality.
5: * Binary management helpers (download URLs, locking, replacement) live in
6: * binary.ts and are shared with the setup --install flow.
7: */
8:
9: import { spawn } from "node:child_process";
10: import {
11: chmodSync,
12: closeSync,
13: existsSync,
14: openSync,
15: realpathSync,
16: statSync,
17: unlinkSync,
18: writeSync,
19: } from "node:fs";
20: import { writeFile } from "node:fs/promises";
21: import { homedir } from "node:os";
22: import { dirname, isAbsolute, join, sep } from "node:path";
23: import { setTimeout } from "node:timers/promises";
24: import { valid as semverValid } from "semver";
25: import {
26: acquireLock,
27: cleanupOldBinary,
28: ...
No files found
No files found
No files found
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch</path> <type>directory</type> <entries> dist/ LICENSE package.json README.md (4 entries) </entries>
Found 4 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js:
Line 1225: function githubReleaseSource(config) {
Line 1227: const { releasesUrl, binaryName, userAgent } = config;
Line 1235: () => doFetch(`${releasesUrl}?per_page=${perPage}`, {
Line 1318: export { BinpatchError, MAX_NIGHTLY_CHAIN_DEPTH, MAX_OUTPUT_SIZE, MAX_STABLE_CHAIN_DEPTH, OciClient, PATCH_TAG_PREFIX, SIZE_THRESHOLD_RATIO, addDiffChunk, applyPatch, applyPatchChainInMemory, applyPatchToMemory, chainFileName, extractSha256, extractStableChain, filterAndSortChainTags, getPatchFromVersion, getPatchTargetSha256, getStableTargetSha256, ghcrSource, githubReleaseSource, makeCache, offtin, parsePatchHeader, patchFileName, resolveAndApply, safeProgress, validateChainStep };
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
1160: report
1161: });
1162: } catch (error) {
1163: if (error instanceof BinpatchError) {
1164: report?.("network");
1165: return null;
1166: }
1167: throw error;
1168: }
1169: }
1170: };
1171: }
1172:
1173: // src/sources/github-release.ts
1174: var SHA256_DIGEST_PATTERN = /^sha256:([0-9a-f]+)$/i;
1175: function extractSha256(asset) {
1176: if (!asset.digest) return null;
1177: const match = SHA256_DIGEST_PATTERN.exec(asset.digest);
1178: return match ? match[1]?.toLowerCase() ?? null : null;
1179: }
1180: function getStableTargetSha256(release, binaryName) {
1181: const binaryAsset = release.assets.find((a) => a.name === binaryName);
1182: if (!binaryAsset) return null;
1183: return ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts</path>
<type>file</type>
<content>
120: // "nightly" and "stable" as positional args select the channel rather than
121: // installing a specific version. Match case-insensitively for convenience.
122: const lower = positional?.toLowerCase();
123: if (lower === "nightly" || lower === "stable") {
124: return {
125: channel: lower,
126: versionArg: undefined,
127: };
128: }
129:
130: return {
131: channel: getReleaseChannel(),
132: versionArg: positional,
133: };
134: }
135:
136: /**
137: * Resolve the target version from the local cache (SQLite) instead of
138: * fetching from the network. Used by `--offline` and as automatic
139: * fallback when `fetchLatestVersion()` hits a network error. ...
{"baseRefOid":"ec83887a16f780f32fba4b7d710bad262dba3a22","headRefOid":"b6484e0fb1eb0f8bce68272d538f5c3b26cd5bd5","mergeStateStatus":"BLOCKED","mergeable":"MERGEABLE","number":1569,"state":"OPEN","statusCheckRollup":[{"__typename":"CheckRun","completedAt":"0001-01-01T00:00:00Z","conclusion":"","detailsUrl":"https://github.com/getsentry/cli/actions/runs/34428983405/job/102720210189","name":"warden","startedAt":"2026-09-10T02:19:21Z","status":"IN_PROGRESS","workflowName":"Warden"},{"__typename":"CheckRun","completedAt":"2026-09-10T02:19:25Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/getsentry/cli/actions/runs/34428983456/job/102720210374","name":"Detect Changes","startedAt":"2026-09-10T02:19:19Z","status":"COMPLETED","workflowName":"Build"},{"__typename":"CheckRun","completedAt":"2026-09-10T02:20:04Z","conclusion":"SUCCESS","detailsUrl":"https://github.com/getsentry/cli/actions/runs/34428980765/job/102720204405","name":"Analyze ...
Found 15 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 144: resolveExistingUpgradeVersion,
Line 650: annotations: { version: "0.0.0-dev.1740393600" },
Line 675: annotations: { version: "0.0.0-dev.1740393600" },
Line 740: await expect(resolveExistingUpgradeVersion("1.0.0")).rejects.toThrow(
Line 1806: annotations: { version: "0.0.0-dev.1740000000" },
Line 1889: annotations: { version: "0.0.0-dev.1740000000" },
Line 2018: annotations: { version: "0.0.0-dev.1740000000" },
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/ghcr.test.ts:
Line 248: ])("rejects invalid nightly version annotation %s", (version) => {
Line 249: const manifest = makeManifest({ annotations: { version } });
Line 252: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts</path>
<type>file</type>
<content>
1720: });
1721:
1722: test("removes .old file if it exists", async () => {
1723: const oldPath = getOldPath();
1724: // Create the directory and file
1725: mkdirSync(join(oldPath, ".."), { recursive: true });
1726: writeFileSync(oldPath, "test content");
1727:
1728: // Verify file exists
1729: expect(
1730: await access(oldPath).then(
1731: () => true,
1732: () => false
1733: )
1734: ).toBe(true);
1735:
1736: // Clean up is fire-and-forget async, so we need to wait a bit
1737: startCleanupOldBinary();
1738: await sleep(50);
1739:
1740: // File should be gone
1741: expect(
1742: await access(oldPath).then(
1743: () => true,
1744: () => false
1745: )
1746: ).toBe(false);
1747: });
1748: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/release-notes.ts</path>
<type>file</type>
<content>
540: }
541:
542: const description = ccMatch[2].trim();
543: const existing = items.get(category) ?? [];
544: existing.push(description);
545: items.set(category, existing);
546: }
547:
548: const sections: ChangeSection[] = [];
549: for (const category of CATEGORY_ORDER) {
550: const descriptions = items.get(category);
551: if (descriptions && descriptions.length > 0) {
552: const markdown = `${descriptions.map((d) => `- ${d}`).join("\n")}\n`;
553: sections.push({ category, markdown });
554: }
555: }
556:
557: return sections;
558: }
559:
560: // ────────────────────────────── Fetch Functions ────────────────────────────
561:
562: /**
563: * Max releases to fetch for changelog purposes. ...
b6484e0fb1eb0f8bce68272d538f5c3b26cd5bd5 bb42358da5b31162013c1f8a8fb5ad28049c988485d0df51ab9738035efd140b - installer_diff_exit=0
warden pending 0 https://github.com/getsentry/cli/actions/runs/34428983405/job/102720210189 Cursor Bugbot pass 3m26s https://cursor.com/docs/bugbot Run skill eval skipping 0 https://github.com/getsentry/cli/actions/runs/34428981840/job/102720205698 Publish Nightly to GHCR skipping 0 https://github.com/getsentry/cli/actions/runs/34428983456/job/102721314483 Generate Delta Patches skipping 0 https://github.com/getsentry/cli/actions/runs/34428983456/job/102721314475 Build npm Package (smoke Node 22) pass 47s https://github.com/getsentry/cli/actions/runs/34428983456/job/102721082705 Build Binary (darwin-arm64) pass 1m2s https://github.com/getsentry/cli/actions/runs/34428983456/job/102721082731 Build Binary (linux-x64) pass 53s https://github.com/getsentry/cli/actions/runs/34428983456/job/102721082698 CI Status pass 4s https://github.com/getsentry/cli/actions/runs/34428983456/job/102721606725 Build npm Package (smoke Node ...
getNightlyVersion() strictly validates nightly annotations at:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/ghcr.ts:306-320But two security-sensitive paths bypass it:
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:682-692/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:942-959A missing, malformed, or mismatched annotation can therefore pass pinned-version validation and supply the executable. The CLI may install one nightly while reporting another, defeating the new strict annotation contract and leaving a discovery/download binding gap.
Fix: introduce one strict helper that parses the annotation with getNightlyVersion() and, for versioned tags, requires exact equality with the requested version. Call it during both pinned resolution and the final manifest download.
Deterministic regressions:
resolveExistingUpgradeVersion("0.14.0-dev.123"), return HTTP 200 with:
UpgradeError(reason="network_error") and must never probe the legacy source.executeUpgrade("curl", "0.14.0-dev.123", …, toolkitSource), return a manifest annotated 0.14.0-dev.124 with an otherwise valid layer. Assert rejection occurs before any blob request or executable setup. Keep an exact-match passing case. ...