Dashboard › cli › Session 0QTGnOoKYyCS
0QTGnOoKYyCS6ZxOkIndependently perform a read-only security and supply-chain review of getsentry/cli PR #1569. Pin all analysis to exact base ec83887a16f780f32fba4b7d710bad262dba3a22 and exact head dbee7e9f2a4555e044f74dfebad002cccfa43a1f in /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Do not modify files, VCS, GitHub, or PR state. First verify the local revisions, then inspect the complete base..head diff, every changed file, and relevant surrounding code/tests. Analyze: fallback only after a genuine typed HTTP 404; fail-closed handling for 401/403/429/5xx/transport/malformed responses; source affinity across GitHub/GHCR tokens, manifests, binaries, stable/nightly deltas, prefetches, and changelogs; tag-prefix and SemVer validation; untrusted GitHub/GHCR JSON, URLs, redirects, and authorization stripping; SHA-256/delta-chain integrity; abort/cancellation and cache behavior; installer unchanged boundary. Return a NON-EMPTY structured report. Findings come first and each must be labeled PASS, CONCERN, or MUST-FIX with exact current file:line evidence; every defect must include a deterministic regression. If no defects exist, state PASS explicitly and list residual risks/test gaps. If blocked, begin BLOCKED with the exact tool and error. The final line must be exactly MERGE or DO-NOT-MERGE.
I’ll verify the target repository and immutable base/head first, then inspect the full diff and trace every supply-chain boundary through surrounding implementation and tests without changing local or remote state.
[
{
"content": "Verify repository guidance, clean state, and exact base/head revisions",
"status": "in_progress",
"priority": "high"
},
{
"content": "Enumerate and inspect the complete base..head diff and every changed file",
"status": "pending",
"priority": "high"
},
{
"content": "Trace security and supply-chain invariants through surrounding code and tests",
"status": "pending",
"priority": "high"
},
{
"content": "Reverify immutable revisions/state and produce the structured verdict",
"status": "pending",
"priority": "high"
}
]
<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. ...
<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/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 ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/apps/cli-docs/src/content/docs/contributing.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/CONTRIBUTING.md
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade
ec83887a16f780f32fba4b7d710bad262dba3a22
dbee7e9f2a4555e044f74dfebad002cccfa43a1f
dbee7e9f2a4555e044f74dfebad002cccfa43a1f
ec83887a16f780f32fba4b7d710bad262dba3a22
# branch.oid dbee7e9f2a4555e044f74dfebad002cccfa43a1f # branch.head feat/toolkit-bridge-upgrade # branch.upstream origin/feat/toolkit-bridge-upgrade # branch.ab +0 -0
<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)* - ...
/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/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`. ...
The repository is clean, HEAD is exactly dbee7e9f2a4555e044f74dfebad002cccfa43a1f, both requested commits exist, and their merge base is exactly ec83887a16f780f32fba4b7d710bad262dba3a22. I’m now enumerating the immutable patch and its repository-specific validation surface.
<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. ...
<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: ...
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 +++++--- packages/cli/src/lib/upgrade.ts | 309 +++++++++++++++----- packages/cli/src/lib/version-check.ts | 21 +- packages/cli/test/commands/cli.test.ts | 23 +- packages/cli/test/commands/cli/upgrade.test.ts | 71 ++++- packages/cli/test/lib/binary.test.ts | 130 ++++++++- packages/cli/test/lib/delta-upgrade.mocked.test.ts | 14 +- packages/cli/test/lib/delta-upgrade.test.ts | 201 ++++++++++++- packages/cli/test/lib/ghcr.test.ts | 40 ++- packages/cli/test/lib/release-notes.test.ts | 113 ++++++- packages/cli/test/lib/upgrade.test.ts | 323 ...
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
dbee7e9f2 (HEAD -> feat/toolkit-bridge-upgrade, origin/feat/toolkit-bridge-upgrade) test(cli): update toolkit upgrade fixtures 723375b6c fix(cli): address upgrade review findings 9c874298e fix(cli): address toolkit bridge review 04867eaf9 feat(cli): add toolkit upgrade bridge
80 34 packages/cli/src/commands/cli/upgrade.ts 148 6 packages/cli/src/lib/binary.ts 107 32 packages/cli/src/lib/delta-upgrade.ts 47 21 packages/cli/src/lib/ghcr.ts 82 37 packages/cli/src/lib/release-notes.ts 237 72 packages/cli/src/lib/upgrade.ts 13 8 packages/cli/src/lib/version-check.ts 13 10 packages/cli/test/commands/cli.test.ts 59 12 packages/cli/test/commands/cli/upgrade.test.ts 128 2 packages/cli/test/lib/binary.test.ts 7 7 packages/cli/test/lib/delta-upgrade.mocked.test.ts 185 16 packages/cli/test/lib/delta-upgrade.test.ts 35 5 packages/cli/test/lib/ghcr.test.ts 112 1 packages/cli/test/lib/release-notes.test.ts 286 37 packages/cli/test/lib/upgrade.test.ts 124 1 packages/cli/test/lib/version-check.test.ts
[
{
"content": "Verify repository guidance, clean state, and exact base/head revisions",
"status": "completed",
"priority": "high"
},
{
"content": "Enumerate and inspect the complete base..head diff and every changed file",
"status": "in_progress",
"priority": "high"
},
{
"content": "Trace security and supply-chain invariants through surrounding code and tests",
"status": "pending",
"priority": "high"
},
{
"content": "Reverify immutable revisions/state and produce the structured verdict",
"status": "pending",
"priority": "high"
}
]
diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts index 8d8282368..610a12025 100644 --- a/packages/cli/src/commands/cli/upgrade.ts +++ b/packages/cli/src/commands/cli/upgrade.ts @@ -1,414 +1,450 @@ /** * 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. ...
diff --git a/packages/cli/src/lib/binary.ts b/packages/cli/src/lib/binary.ts
index 75582612a..61e10568d 100644
--- a/packages/cli/src/lib/binary.ts
+++ b/packages/cli/src/lib/binary.ts
@@ -5,327 +5,469 @@
* 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 { logger } from "./logger.js";
import { isProcessRunning } from "./process-utils.js";
/** Known ...
diff --git a/packages/cli/src/lib/delta-upgrade.ts b/packages/cli/src/lib/delta-upgrade.ts
index ec709b8c8..37f4b7d54 100644
--- a/packages/cli/src/lib/delta-upgrade.ts
+++ b/packages/cli/src/lib/delta-upgrade.ts
@@ -1,388 +1,458 @@
/** Delta upgrade discovery and application backed by binpatch. */
import { join } from "node:path";
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
import * as Sentry from "@sentry/node-core/light";
import {
applyPatchChainInMemory,
extractStableChain as binpatchExtractStableChain,
filterAndSortChainTags as binpatchFilterAndSortChainTags,
validateChainStep as binpatchValidateChainStep,
type DeltaTelemetry,
type DeltaUnavailableReason,
type ExtractStableChainOpts,
type GitHubRelease,
getPatchFromVersion,
getPatchTargetSha256,
ghcrSource,
githubReleaseSource,
type InstrumentHook,
MAX_NIGHTLY_CHAIN_DEPTH,
makeCache,
OciClient,
type OciManifest,
...
diff --git a/packages/cli/src/lib/ghcr.ts b/packages/cli/src/lib/ghcr.ts index 43d6deb7e..5bc671a6a 100644 --- a/packages/cli/src/lib/ghcr.ts +++ b/packages/cli/src/lib/ghcr.ts @@ -1,520 +1,546 @@ /** * GHCR (GitHub Container Registry) Client * * Encapsulates the OCI download protocol for fetching nightly CLI binaries * from ghcr.io/getsentry/cli. Nightly builds are pushed as OCI artifacts * via ORAS with the version baked into the manifest annotation. * * Key design decisions: * - Anonymous access: nightly package is public; no token needed beyond the * standard ghcr.io anonymous token exchange. * - Version discovery from manifest annotation: `annotations.version` in the * OCI manifest holds the nightly version. Checking the latest version only * requires a token exchange + manifest fetch (2 HTTP requests total). * - Redirect quirk: ghcr.io blob downloads return 307 to Azure Blob Storage. ...
diff --git a/packages/cli/src/lib/release-notes.ts b/packages/cli/src/lib/release-notes.ts index ddf617535..fb32efd78 100644 --- a/packages/cli/src/lib/release-notes.ts +++ b/packages/cli/src/lib/release-notes.ts @@ -1,139 +1,141 @@ /** * 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/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,189 +1,228 @@
/**
* 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,
...
diff --git a/packages/cli/src/lib/version-check.ts b/packages/cli/src/lib/version-check.ts
index c69ff0466..34393b1c7 100644
--- a/packages/cli/src/lib/version-check.ts
+++ b/packages/cli/src/lib/version-check.ts
@@ -1,431 +1,436 @@
/**
* Background version check for "new version available" notifications.
*
* For nightly builds (CLI_VERSION contains "-dev.<timestamp>"), checks GHCR for the
* latest nightly version via the OCI manifest annotation. For stable builds,
* checks GitHub Releases. Results are cached in the database and shown on
* subsequent runs.
*/
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
import * as Sentry from "@sentry/node-core/light";
import { compare as semverCompare } from "semver";
+import type { UpgradeSource } from "./binary.js";
import { CLI_VERSION } from "./constants.js";
import { getReleaseChannel } from "./db/release-channel.js";
import {
getVersionCheckInfo,
markUpdateNotified,
...
diff --git a/packages/cli/test/commands/cli.test.ts b/packages/cli/test/commands/cli.test.ts
index cd1514eb0..187ae70f4 100644
--- a/packages/cli/test/commands/cli.test.ts
+++ b/packages/cli/test/commands/cli.test.ts
@@ -67,170 +67,173 @@ describe("feedbackCommand.func", () => {
// Access func through loader
const func = await feedbackCommand.loader();
const mockContext = {
stdout: { write: vi.fn(() => true) },
stderr: { write: vi.fn(() => true) },
};
await expect(func.call(mockContext, {}, "")).rejects.toThrow(
"Please provide a feedback message."
);
});
test("throws ValidationError for whitespace-only message", async () => {
const func = await feedbackCommand.loader();
const mockContext = {
stdout: { write: vi.fn(() => true) },
stderr: { write: vi.fn(() => true) },
};
await expect(func.call(mockContext, {}, " ")).rejects.toThrow(
"Please provide a feedback message."
);
...
diff --git a/packages/cli/test/lib/binary.test.ts b/packages/cli/test/lib/binary.test.ts
index 6fcfb3b1a..5eaa53474 100644
--- a/packages/cli/test/lib/binary.test.ts
+++ b/packages/cli/test/lib/binary.test.ts
@@ -1,143 +1,269 @@
/**
* Binary Management Tests
*
* Tests for shared binary helpers: install directory selection, paths,
* download URLs, locking, and binary installation.
*/
import {
chmodSync,
mkdirSync,
readFileSync,
realpathSync,
rmSync,
symlinkSync,
writeFileSync,
} from "node:fs";
import { access, readFile, writeFile } from "node:fs/promises";
import { join, sep } from "node:path";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import {
acquireLock,
compareVersions,
determineInstallDir,
fetchWithUpgradeError,
getBinaryDownloadUrl,
getBinaryFilename,
getBinaryPaths,
+ getGitHubReleaseByTagUrl,
getLegacyInstallDirs,
getPlatformBinaryName,
installBinary,
isDowngrade,
...
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
@@ -22,185 +22,185 @@ import { useTestConfigDir } from "../helpers.js";
useTestConfigDir("delta-upgrade-mocked-");
// ============================================================================
// Mock Setup
// ============================================================================
/**
* Mock constants.js to pretend we're running a real stable version.
* This satisfies canAttemptDelta()'s CLI_VERSION !== "0.0.0-dev" check.
*/
vi.mock("../../src/lib/constants.js", async (importOriginal) => {
const orig =
await importOriginal<typeof import("../../src/lib/constants.js")>();
return {
...orig,
CLI_VERSION: "0.13.0",
};
});
// Import AFTER mock setup so the mocked constants are ...
diff --git a/packages/cli/test/lib/ghcr.test.ts b/packages/cli/test/lib/ghcr.test.ts
index e5c81d532..b971c7e1e 100644
--- a/packages/cli/test/lib/ghcr.test.ts
+++ b/packages/cli/test/lib/ghcr.test.ts
@@ -1,255 +1,282 @@
/**
* GHCR Client Tests
*
* Unit tests for the GHCR/OCI download protocol helpers.
* All HTTP calls are mocked via globalThis.fetch to avoid network access.
*/
import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import { UpgradeError } from "../../src/lib/errors.js";
import {
downloadLayerBlob,
downloadNightlyBlob,
fetchManifest,
fetchNightlyManifest,
findLayerByFilename,
GHCR_REPO,
GHCR_TAG,
+ GhcrManifestHttpError,
getAnonymousToken,
getNightlyVersion,
listTags,
type OciManifest,
} from "../../src/lib/ghcr.js";
/** Store original fetch for restoration */
let originalFetch: typeof globalThis.fetch;
/** Helper to mock fetch without ...
diff --git a/packages/cli/test/lib/release-notes.test.ts b/packages/cli/test/lib/release-notes.test.ts
index 78963b471..d60066802 100644
--- a/packages/cli/test/lib/release-notes.test.ts
+++ b/packages/cli/test/lib/release-notes.test.ts
@@ -1,101 +1,104 @@
/**
* Unit Tests for Release Notes Parser & Aggregation
*
* 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 { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import type { GitHubRelease } from "../../src/lib/delta-upgrade.js";
import {
buildChangelogSummary,
type ChangeCategory,
...
diff --git a/packages/cli/test/lib/upgrade.test.ts b/packages/cli/test/lib/upgrade.test.ts
index 7c6f63a4b..bffea175b 100644
--- a/packages/cli/test/lib/upgrade.test.ts
+++ b/packages/cli/test/lib/upgrade.test.ts
@@ -43,742 +43,890 @@ function noopStream() {
// intentional no-op
}
/**
* Build a minimal fake ChildProcess EventEmitter that emits 'close'
* with the given exit code after a microtask tick.
*/
function fakeProcess(exitCode: number, stdoutData = ""): FakeProc {
const emitter = new EventEmitter() as FakeProc;
const listeners: Array<(chunk: Buffer) => void> = [];
emitter.stdout = {
on: (_event: string, cb: (chunk: Buffer) => void) => {
listeners.push(cb);
return emitter.stdout;
},
resume: noopStream,
};
emitter.stderr = {
on: (_event: string, _cb: (chunk: Buffer) => void) => emitter.stderr,
resume: noopStream,
};
queueMicrotask(() => {
if (stdoutData) {
for (const cb of listeners) {
...
diff --git a/packages/cli/test/lib/version-check.test.ts b/packages/cli/test/lib/version-check.test.ts
index 20172cd83..dd047a5cb 100644
--- a/packages/cli/test/lib/version-check.test.ts
+++ b/packages/cli/test/lib/version-check.test.ts
@@ -1,111 +1,114 @@
/**
* Version Check Logic Tests
*/
import { setTimeout as sleep } from "node:timers/promises";
-import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
import { setReleaseChannel } from "../../src/lib/db/release-channel.js";
import {
getVersionCheckInfo,
setVersionCheckInfo,
} from "../../src/lib/db/version-check.js";
+// biome-ignore lint/performance/noNamespaceImport: Vitest requires the module namespace to spy on an ESM export
+import * as deltaUpgrade from "../../src/lib/delta-upgrade.js";
import {
ApiError,
ContextError,
ValidationError,
} from ...
diff --git a/packages/cli/test/lib/upgrade.test.ts b/packages/cli/test/lib/upgrade.test.ts
index 7c6f63a4b..bffea175b 100644
--- a/packages/cli/test/lib/upgrade.test.ts
+++ b/packages/cli/test/lib/upgrade.test.ts
@@ -113,40 +113,42 @@ vi.mock("node:child_process", async (importOriginal) => {
});
// Dynamic imports: must run AFTER vi.mock() so upgrade.ts picks up the
// mocked spawn.
import { isEnoentSpawnError } from "../../src/commands/cli/upgrade.js";
import {
acquireLock,
getBinaryDownloadUrl,
isNightlyVersion,
releaseLock,
+ UPGRADE_SOURCES,
} from "../../src/lib/binary.js";
import {
clearInstallInfo,
setInstallInfo,
} from "../../src/lib/db/install-info.js";
import { UpgradeError } from "../../src/lib/errors.js";
import { isProcessRunning } from "../../src/lib/process-utils.js";
const {
buildKnownCurlPaths,
detectInstallationMethod,
detectPackageManagerFromPath,
downloadBinaryToTemp,
executeUpgrade,
fetchLatestFromGitHub,
...
<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/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: ...
<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/delta-upgrade.ts</path>
<type>file</type>
<content>
1: /** Delta upgrade discovery and application backed by binpatch. */
2:
3: import { join } from "node:path";
4: // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
5: import * as Sentry from "@sentry/node-core/light";
6: import {
7: applyPatchChainInMemory,
8: extractStableChain as binpatchExtractStableChain,
9: filterAndSortChainTags as binpatchFilterAndSortChainTags,
10: validateChainStep as binpatchValidateChainStep,
11: type DeltaTelemetry,
12: type DeltaUnavailableReason,
13: type ExtractStableChainOpts,
14: type GitHubRelease,
15: getPatchFromVersion,
16: getPatchTargetSha256,
17: ghcrSource,
18: githubReleaseSource,
19: type InstrumentHook,
20: MAX_NIGHTLY_CHAIN_DEPTH,
21: makeCache,
22: OciClient,
23: type OciManifest,
24: ...
No files found
No files found
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules</path> <type>directory</type> <entries> .modules.yaml .pnpm-workspace-state.json .pnpm/ (3 entries) </entries>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm</path> <type>directory</type> <entries> @a2a-js+sdk@0.3.14_express@5.2.1/ @ai-sdk+provider-utils@2.2.8_zod@4.4.3/ @ai-sdk+provider-utils@3.0.30_zod@4.4.3/ @ai-sdk+provider-utils@4.0.40_zod@4.4.3/ @ai-sdk+provider-utils@5.0.13_zod@4.4.3/ @ai-sdk+provider@1.1.3/ @ai-sdk+provider@2.0.3/ @ai-sdk+provider@3.0.14/ @ai-sdk+provider@4.0.4/ @ai-sdk+ui-utils@1.2.11_zod@4.4.3/ @alcalzone+ansi-tokenize@0.3.0/ @anthropic-ai+sdk@0.39.0/ @apm-js-collab+code-transformer-bundler-plugins@0.5.0/ @apm-js-collab+code-transformer-bundler-plugins@0.7.3/ @apm-js-collab+code-transformer@0.15.0/ @apm-js-collab+code-transformer@0.18.1/ @apm-js-collab+tracing-hooks@0.10.1/ @apm-js-collab+tracing-hooks@0.13.0/ @astrojs+compiler-binding-linux-x64-gnu@0.3.2/ @astrojs+compiler-binding-linux-x64-musl@0.3.2/ @astrojs+compiler-binding@0.3.2_@emnapi+core@1.10.0_@emnapi+runtime@1.10.0/ @astrojs...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch</path> <type>directory</type> <entries> dist/ LICENSE package.json README.md (4 entries) </entries>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist</path> <type>directory</type> <entries> index.d.ts index.js index.js.map (3 entries) </entries>
Found 45 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist/index.js:
Line 155: /** File offset the cached block starts at, or -1 when the cache is empty. */
Line 186: /** True when the cached block fully covers `[start, end)`. */
Line 191: * Refill the cache block starting at `start`. The length is clamped to the
Line 234: async function loadOldBinary(oldPath) {
Line 420: async function savePatchesToCache(cacheDir, chain, steps) {
Line 421: await mkdir(cacheDir, { recursive: true, mode: 448 });
Line 427: cacheDir,
Line 441: cachedAt: Date.now(),
Line 449: cacheDir,
Line 456: async function loadAllChainMetas(cacheDir) {
Line 459: files = await readdir(cacheDir);
Line 471: await readFile(join(cacheDir, file), "utf-8")
Line 504: async function loadCachedChain(cacheDir, ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
400: await reader.close();
401: }
402: }
403: function applyPatch(oldPath, patchData, destPath) {
404: return applyPatchChainInMemory(oldPath, [patchData], destPath);
405: }
406: var CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
407: var MAX_CHAIN_WALK_DEPTH = 10;
408: function sanitizeVersion(version) {
409: return version.replace(/[^a-zA-Z0-9.-]/g, "_");
410: }
411: function patchFileName(fromVersion, toVersion) {
412: return `${sanitizeVersion(fromVersion)}-${sanitizeVersion(toVersion)}.patch`;
413: }
414: function chainFileName(fromVersion, toVersion) {
415: return `chain-${sanitizeVersion(fromVersion)}-${sanitizeVersion(toVersion)}.json`;
416: }
417: function isNotFound(err) {
418: return err instanceof Error && "code" in err && err.code === ...
Found 29 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/patch-cache.test.ts:
Line 15: cleanupPatchCache,
Line 16: clearPatchCache,
Line 20: } from "../../src/lib/patch-cache.js";
Line 24: const getConfigDir = useTestConfigDir("patch-cache-test-");
Line 30: /** Get the patch-cache subdirectory path */
Line 32: return join(getConfigDir(), "patch-cache");
Line 352: // cleanupPatchCache
Line 355: describe("cleanupPatchCache", () => {
Line 358: await cleanupPatchCache();
Line 383: await cleanupPatchCache();
Line 416: await cleanupPatchCache();
Line 433: await cleanupPatchCache();
Line 477: await cleanupPatchCache();
Line 540: await cleanupPatchCache();
Line 563: // clearPatchCache — wipe all cached patches after successful upgrade
Line 566: describe("clearPatchCache", () => {
Line 596: await clearPatchCache();
Line ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/patch-cache.ts</path>
<type>file</type>
<content>
1: import { join } from "node:path";
2: import { makeCache, type PatchCache, type PatchChain } from "binpatch";
3: import { getConfigDir } from "./db/index.js";
4:
5: export type { ChainMeta, PatchStepMeta } from "binpatch";
6: // biome-ignore lint/performance/noBarrelFile: preserve the existing cache API
7: export { chainFileName, patchFileName } from "binpatch";
8:
9: function cache(): PatchCache {
10: return makeCache(join(getConfigDir(), "patch-cache"));
11: }
12:
13: export function savePatchesToCache(
14: chain: Pick<PatchChain, "patches" | "expectedSha256">,
15: steps: { fromVersion: string; toVersion: string }[]
16: ): Promise<void> {
17: return cache().save(chain, steps);
18: }
19:
20: export async function loadCachedChain(
21: currentVersion: string,
22: targetVersion: string
23: ...
Found 34 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist/index.js:
Line 324: const hasher = createHash("sha256");
Line 723: const sha256 = await applyPatchChainInMemory(
Line 734: if (sha256 !== chain.expectedSha256) {
Line 736: `SHA-256 mismatch after patching: got ${sha256}, expected ${chain.expectedSha256}`
Line 741: sha256,
Line 767: var OciClient = class {
Line 830: Authorization: `Bearer ${token}`,
Line 855: Authorization: `Bearer ${token}`,
Line 887: * Download a blob by digest. The registry returns a 3xx redirect to blob
Line 888: * storage; the redirect is followed manually without the auth header. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
750: var BLOB_TIMEOUT = 3e4;
751: var TAGS_PAGE_SIZE = 100;
752: var OCI_MANIFEST_TYPE = "application/vnd.oci.image.manifest.v1+json";
753: function isRetryableError(error) {
754: if (error.name === "TimeoutError" || error.name === "AbortError") {
755: return true;
756: }
757: const msg = error.message.toLowerCase();
758: return msg.includes("timeout") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("network") || msg.includes("fetch failed");
759: }
760: function buildSignal(timeout, externalSignal) {
761: const timeoutSignal = AbortSignal.timeout(timeout);
762: return externalSignal ? AbortSignal.any([timeoutSignal, externalSignal]) : timeoutSignal;
763: }
764: function isExternalAbort(error, externalSignal) {
765: return ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm/binpatch@0.4.2/node_modules/binpatch/dist/index.js</path>
<type>file</type>
<content>
1240: signal
1241: })
1242: );
1243: } catch {
1244: return null;
1245: }
1246: if (!response.ok) return null;
1247: return await response.json();
1248: }
1249: async function downloadPatch(url, signal) {
1250: let response;
1251: try {
1252: response = await i(
1253: "download-patch",
1254: () => doFetch(url, {
1255: headers: { "User-Agent": userAgent },
1256: signal
1257: })
1258: );
1259: } catch {
1260: return null;
1261: }
1262: if (!response.ok) return null;
1263: return new Uint8Array(await response.arrayBuffer());
1264: }
1265: return {
1266: async resolveChain(currentVersion, targetVersion, signal, report) {
1267: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts</path> <type>file</type> <content> 1: /** 2: * sentry cli upgrade 3: * 4: * Self-update the Sentry CLI to the latest or a specific version. 5: * After upgrading, spawns the NEW binary with `cli setup` to update 6: * completions, agent skills, and record installation metadata. 7: * 8: * Supports two release channels: 9: * - stable (default): tracks the latest GitHub release 10: * - nightly: tracks the rolling nightly prerelease built from main 11: * 12: * The channel can be set via --channel or by passing "nightly"/"stable" 13: * as the version argument. The choice is persisted in the local database 14: * so that subsequent bare `sentry cli upgrade` calls use the same channel. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts</path>
<type>file</type>
<content>
951: default: false,
952: },
953: offline: {
954: kind: "boolean",
955: brief:
956: "Upgrade using only cached version info and patches (no network)",
957: default: false,
958: },
959: "no-agent-skills": {
960: kind: "boolean",
961: brief: "Skip agent skill installation for AI coding assistants",
962: default: false,
963: },
964: method: {
965: kind: "parsed",
966: parse: parseInstallationMethod,
967: brief: "Installation method to use (curl, brew, npm, pnpm, bun, yarn)",
968: optional: true,
969: placeholder: "method",
970: },
971: },
972: },
973: async *func(this: SentryContext, flags: UpgradeFlags, version?: string) {
974: const { channel, ...
Found 32 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts:
Line 462: export async function fetchLatestFromGitHub(
Line 547: const token = await getAnonymousToken(source, signal);
Line 548: const manifest = await fetchManifest(token, tag, signal, source);
Line 561: export async function fetchLatestNightlyVersion(
Line 590: return fetchLatestNightlyVersion();
Line 593: ? fetchLatestFromGitHub()
Line 648: const token = await getAnonymousToken(source);
Line 650: await fetchManifest(token, `nightly-${version}`, undefined, source);
Line 692: export async function versionExists(
Line 866: const token = await getAnonymousToken(source);
Line 868: ? ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/release-notes.ts</path>
<type>file</type>
<content>
360:
361: const originalCount = sections.reduce(
362: (sum, s) => sum + countMarkdownListItems(s.markdown),
363: 0
364: );
365:
366: let totalItems = originalCount;
367: let truncated = false;
368:
369: if (maxItems !== undefined && originalCount > maxItems) {
370: const result = applySectionTruncation(sections, maxItems, originalCount);
371: totalItems = result.totalItems;
372: truncated = result.truncated;
373: }
374:
375: return {
376: fromVersion,
377: toVersion,
378: sections,
379: totalItems,
380: truncated,
381: originalCount,
382: };
383: }
384:
385: /**
386: * Merge extracted sections by category across multiple releases.
387: *
388: * Concatenates markdown from the same category and strips author attributions. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/version-check.ts</path>
<type>file</type>
<content>
210: return true;
211: }
212: }
213: return false;
214: }
215:
216: /**
217: * Abort any pending version check to allow process exit.
218: * Call this when main CLI work is complete.
219: */
220: export function abortPendingVersionCheck(): void {
221: pendingAbortController?.abort();
222: pendingAbortController = null;
223: }
224:
225: /**
226: * Pre-fetch delta patches for a newly discovered version.
227: *
228: * Best-effort: errors are silently caught so the version check still succeeds.
229: * After pre-fetching, opportunistically cleans up stale cached patches. ...
Found 1 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/custom-ca.ts: Line 305: export function customFetch(
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/custom-ca.ts</path>
<type>file</type>
<content>
250:
251: /**
252: * Build a user-friendly error detail for TLS certificate failures.
253: * Walks `error.cause` to extract the root TLS error (Node.js wraps
254: * TLS errors in `TypeError: fetch failed`).
255: *
256: * When custom CAs are already loaded, the message says "still failed"
257: * so the user knows to check their bundle — not re-run the same setup.
258: */
259: export function buildTlsErrorDetail(error: Error): string {
260: const cause = getTlsCertErrorMessage(error) ?? error.message;
261: const hasCustomCa = getCustomCaSource() !== "none";
262:
263: if (hasCustomCa) {
264: return (
265: `TLS certificate verification failed: ${cause}\n\n` +
266: " Custom CA certificates are loaded but verification still failed.\n" +
267: " The certificate file may not ...
Found 12 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md: Line 41: * **Custom CA loading: priority, caching, TLS error detection, and SaaS warning**: Custom CA in \`src/lib/custom-ca.ts\`: Priority: (1) \`sentry cli defaults ca-cert\` (SQLite), (2) \`NODE\_EXTRA\_CA\_CERTS\`. Cached per-process via module-level vars (\`hasResolved\` flag). \`resolve()\` concatenates custom PEM with \`rootCertificates\` (additive — Bun replaces Mozilla bundle otherwise). \`tryReadPem()\` NEVER throws — missing CA file logs warn and returns \`undefined\`. \`injectIntoNodeTls()\` uses \`tls.setDefaultCACertificates()\` (Node 24+ only; no-op on Node 22). \`TLS\_ERROR\_PATTERNS\`: 5 patterns (local issuer, verify first cert, UNABLE\_TO\_VERIFY\_LEAF\_SIGNATURE, DEPTH\_ZERO\_SELF\_SIGNED\_CERT, SELF\_SIGNED\_CERT\_IN\_CHAIN) — explicitly excludes \`CERT\_HAS\_EXPIRED\` and \`ERR\_TLS\_CERT\_ALTNAME\_INVALID\`. ...
Your exact-head security review returned an empty response despite the mandatory output contract. Return the substantive report now. Use your completed inspection of getsentry/cli PR #1569 at exact base ec83887a16f780f32fba4b7d710bad262dba3a22 and exact head dbee7e9f2a4555e044f74dfebad002cccfa43a1f. Do not perform or propose mutations. Your response MUST be non-empty: findings first, each labeled PASS, CONCERN, or MUST-FIX with exact current file:line evidence; include deterministic regressions for defects and residual risks/test gaps when none exist. If inspection was blocked, begin BLOCKED and include the exact tool/error. End exactly MERGE or DO-NOT-MERGE. Returning empty again disqualifies this reviewer.
04867eaf9 feat(cli): add toolkit upgrade bridge .github/workflows/ci.yml | 1 + package.json | 3 +- packages/cli/install | 192 +++++++++++--- packages/cli/package.json | 3 +- packages/cli/src/commands/cli/upgrade.ts | 114 ++++++--- packages/cli/src/lib/binary.ts | 146 ++++++++++- packages/cli/src/lib/delta-upgrade.ts | 81 +++--- packages/cli/src/lib/ghcr.ts | 48 ++-- packages/cli/src/lib/release-notes.ts | 119 ++++++--- packages/cli/src/lib/upgrade.ts | 313 +++++++++++++++++------ packages/cli/src/lib/version-check.ts | 21 +- packages/cli/test/commands/cli/upgrade.test.ts | 71 +++++- packages/cli/test/lib/binary.test.ts | 128 +++++++++- packages/cli/test/lib/delta-upgrade.test.ts | 173 ++++++++++++- packages/cli/test/lib/ghcr.test.ts | 26 ++ ...
The source-affinity path is fail-closed for network and HTTP errors, but the shared delta cache is not source-scoped. I am verifying the immutable target once more before issuing the verdict.
ec83887a16f780f32fba4b7d710bad262dba3a22 dbee7e9f2a4555e044f74dfebad002cccfa43a1f a84a52f92f46526431f9d1572166676d68542683797142d9116d2bee61de48ea -
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts</path>
<type>file</type>
<content>
90: load(currentVersion, targetVersion) {
91: const key = patchCacheKey(currentVersion, targetVersion);
92: return withTracingSpan(key, "cache.get", async (span) => {
93: span.setAttribute("cache.key", [key]);
94: const result = await base.load(currentVersion, targetVersion);
95: span.setAttribute("cache.hit", result !== null);
96: if (result) {
97: span.setAttribute("cache.item_size", result.totalSize);
98: }
99: return result;
100: });
101: },
102: save(chain, steps) {
103: const first = steps.at(0);
104: const last = steps.at(-1);
105: if (!(first && last)) {
106: return base.save(chain, steps);
107: }
108: const key = patchCacheKey(first.fromVersion, last.toVersion);
109: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/delta-upgrade.ts</path>
<type>file</type>
<content>
1: /** Delta upgrade discovery and application backed by binpatch. */
2:
3: import { join } from "node:path";
4: // biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
5: import * as Sentry from "@sentry/node-core/light";
6: import {
7: applyPatchChainInMemory,
8: extractStableChain as binpatchExtractStableChain,
9: filterAndSortChainTags as binpatchFilterAndSortChainTags,
10: validateChainStep as binpatchValidateChainStep,
11: type DeltaTelemetry,
12: type DeltaUnavailableReason,
13: type ExtractStableChainOpts,
14: type GitHubRelease,
15: getPatchFromVersion,
16: getPatchTargetSha256,
17: ghcrSource,
18: githubReleaseSource,
19: type InstrumentHook,
20: MAX_NIGHTLY_CHAIN_DEPTH,
21: makeCache,
22: OciClient,
23: type OciManifest,
24: ...
Found 53 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts:
Line 51: * them as migration sources would relocate a working binary out of an active
Line 115: /** Ordered CLI release sources. The resolver falls through only on HTTP 404. */
Line 116: export const UPGRADE_SOURCES = [
Line 129: /** The first source used by direct helper calls that do not resolve a source. */
Line 130: export const PRIMARY_UPGRADE_SOURCE = UPGRADE_SOURCES[0];
Line 234: export function getBinaryDownloadUrl(
Line 236: source: UpgradeSource = PRIMARY_UPGRADE_SOURCE
Line 238: const tag = `${source.tagPrefix}${version}`;
Line 239: return `https://github.com/${source.githubRepo}/releases/download/${tag}/${getPlatformBinaryName()}`;
Line 242: /** Build the GitHub API base URL for a release source. ...
* 🔴 (00:20) [requested-review] User again requested an independent, read-only security and supply-chain review of getsentry/cli PR #1569, pinned to exact base `ec83887a16f780f32fba4b7d710bad262dba3a22` and exact head `dbee7e9f2a4555e044f74dfebad002cccfa43a1f` in `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade`. * 🔴 (00:20) User prohibite…
* 🔴 (00:42) User stated `tryReadPem()` in `src/lib/custom-ca.ts` NEVER throws; a missing CA file logs a warning and returns `undefined`. * 🔴 (00:42) User stated custom CA resolution in `src/lib/custom-ca.ts` uses priority (1) `sentry cli defaults ca-cert` from SQLite, then (2) `NODE_EXTRA_CA_CERTS`; resolution is cached per process using module-level state including `hasResolved`. * 🔴 (00:42) …
* 🔴 (00:35) `abortPendingVersionCheck()` in `packages/cli/src/lib/version-check.ts` aborts `pendingAbortController` via `pendingAbortController?.abort()` and then sets it to `null`; it is intended to allow process exit after the main CLI work completes. * 🔴 (00:35) `maybePrefetchPatches(channel, latestVersion, signal, source)` in `packages/cli/src/lib/version-check.ts` is best-effort and return…
* 🔴 (00:32) In `packages/cli/src/commands/cli/upgrade.ts`, package managers always need network access to fetch and install packages. * 🔴 (00:32) In `packages/cli/src/commands/cli/upgrade.ts`, nightly builds are GitHub-only, so nightly version resolution always uses the `curl` (GitHub) lookup path. * 🔴 (00:32) The `sentry cli upgrade nightly` command switches to the nightly channel and updates…
* 🔴 (00:32) `packages/cli/src/lib/patch-cache.ts` is exactly 43 lines. It imports `join` from `node:path`, imports `makeCache`, `PatchCache`, and `PatchChain` from `binpatch`, and imports `getConfigDir` from `./db/index.js`. * 🔴 (00:32) `packages/cli/src/lib/patch-cache.ts` re-exports the `ChainMeta` and `PatchStepMeta` types plus `chainFileName` and `patchFileName` from `binpatch`; the value r…
* 🔴 (00:30) The pnpm virtual store at `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/node_modules/.pnpm` contained 872 entries; the tool displayed entries 1–300. Relevant installed packages included `binpatch@0.4.2/`, `binpunch@1.0.0/`, `@biomejs+biome@2.3.8/`, `@types+node@18.19.130/`, `@types+node@22.20.1/`, `@types+node@24.13.3/`, `@v…
* 🔴 (00:30) User provided upgrade-related source code implementing GHCR access, including `getUserAgent`, retry handling for `TimeoutError` and `AbortError`, combined cancellation via `AbortSignal.any([timeoutSignal, externalSignal])`, `RetryOptions`, `GhcrManifestHttpError extends UpgradeError`, OCI manifest/layer types, anonymous bearer-token authentication, manifest retrieval, nightly version…
* 🔴 (00:29) Upgrade installation-type detection must always check for Homebrew first because stored installation information may be stale. * 🔴 (00:29) The upgrade file-readiness check throws an `UpgradeError` when the file never becomes visible or stays empty.
* 🔴 (00:29) Upgrade path normalization strips a trailing path separator, but never strips it from a bare filesystem root such as `/`. * 🔴 (00:29) Upgrade-source resolution returns the successful probe response to the caller so the caller never repeats the successful probe request. * 🔴 (00:29) When every CLI upgrade source responds with HTTP 404, source resolution throws the exact message `No C…
* 🔴 (00:29) `packages/cli/test/lib/version-check.test.ts` now imports `vi` from `vitest`, `UPGRADE_SOURCES` from `../../src/lib/binary.js`, and the `../../src/lib/delta-upgrade.js` module namespace as `deltaUpgrade`; the namespace import has `biome-ignore lint/performance/noNamespaceImport` because Vitest requires the module namespace to spy on an ESM export. * 🔴 (00:29) `maybeCheckForUpdateInB…
* 🔴 (00:29) `packages/cli/test/lib/release-notes.test.ts` now imports `afterEach`, `beforeEach`, `UPGRADE_SOURCES`, `fetchChangelog`, and `mockFetch` to test changelog source affinity while restoring `globalThis.fetch` after each test. * 🔴 (00:29) `fetchChangelog` stable-source affinity test explicitly selects `UPGRADE_SOURCES[0]` (Toolkit), receives releases tagged `mcp@99.0.0` and `cli@0.21.0…
* 🔴 (00:28) `packages/cli/test/lib/delta-upgrade.mocked.test.ts` mocks `../../src/lib/constants.js` with `CLI_VERSION: "0.13.0"` so `canAttemptDelta()` passes its `CLI_VERSION !== "0.0.0-dev"` eligibility check. * 🔴 (00:28) `attemptDeltaUpgrade("0.0.0-dev.100", "/tmp/fake-binary", "/tmp/fake-dest")` is tested to return `null` for a stable-to-nightly cross-channel upgrade when the current mocked…
* 🔴 (00:28) `packages/cli/test/commands/cli.test.ts` includes a migration-path test where `setReleaseChannel("nightly")` followed by use of the npm installation method triggers migration. * 🔴 (00:28) Stable GitHub release mocks in `packages/cli/test/commands/cli.test.ts` were updated from the legacy `getsentry/cli` `releases/latest` object response to the `getsentry/toolkit/releases?per_page=10…
* 🔴 (00:28) `packages/cli/src/lib/upgrade.ts` now imports `valid as semverValid` from `semver`, plus `compareVersions` and source-aware upgrade helpers/types including `UpgradeSourceNotFoundError`. * 🔴 (00:28) Installation-method detection priority in `packages/cli/src/lib/upgrade.ts` is: 1. Homebrew via cheap authoritative realpath check, 2. stored install info in DB as the fast path, 3. legac…
* 🔴 (00:26) `packages/cli/src/lib/ghcr.ts` expanded from 520 to 546 lines and added source-specific GHCR repository support by importing `PRIMARY_UPGRADE_SOURCE` and `UpgradeSource` from `./binary.js`. * 🔴 (00:26) `GHCR_REPO` in `packages/cli/src/lib/ghcr.ts` is now the default repository alias `PRIMARY_UPGRADE_SOURCE.ghcrRepo` instead of the hard-coded `"getsentry/cli"`. * 🔴 (00:26) Added exp…
* 🔴 (00:26) `packages/cli/src/lib/delta-upgrade.ts` was expanded from 388 to 458 lines and changed delta-upgrade discovery/application to support source-specific repositories through `UpgradeSource`. * 🔴 (00:26) `packages/cli/src/lib/delta-upgrade.ts` replaced imports of `GITHUB_RELEASES_URL` and `GHCR_REPO` with `getGitHubReleasesUrl`, `PRIMARY_UPGRADE_SOURCE`, and `UpgradeSource` from `./bina…
Date: Sep 10, 2026 * 🔴 (00:24) `packages/cli/package.json` identifies package `sentry` version `0.45.0-dev.0`, requires Node.js `>=20.0`, uses development runtime Node.js `>=22.15`, and pins package manager `pnpm@10.11.0`. * 🔴 (00:24) `packages/cli/package.json` build/test workflow includes `build` via `pnpm tsx script/build.ts --single`, `build:all` via `pnpm tsx script/build.ts`, `test` deleg…
Date: Sep 10, 2026 * 🔴 (00:24) User directed contributors to always check `package.json` for the latest scripts. * 🔴 (00:24) User stated all packages must be added to `devDependencies`, never `dependencies`, because everything is bundled at build time via esbuild; CI enforces this with `pnpm run check:deps`. * 🔴 (00:24) User directed that packages always be added with `pnpm add -D <package>` u…
Date: Sep 10, 2026 * 🔴 (00:21) Root `package.json` defines workspace package `toolkit-cli-workspace` version `0.0.0`, with `"private": true`, `"type": "module"`, and Node engine `>=20.0`. * 🔴 (00:21) Root `package.json` scripts delegate to the `sentry` package via `pnpm --filter sentry run`: `tsx`, `cli`, `dev`, `build`, `build:all`, `bundle`, `typecheck`, `lint`, `lint:fix`, `test`, `test:unit…
Date: Sep 10, 2026 * 🔴 (00:20) Repository worktree is `/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade`; root contains `.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`, `…