Dashboard › cli › Session 1Hgf4Sk9zS26
1Hgf4Sk9zS26LxHTrREAD-ONLY correctness merge-gate review of getsentry/cli PR #1569 exact base ec83887a16f780f32fba4b7d710bad262dba3a22 and head 2f9b5d0d26124d445a916b8bfc584ad518720584. Worktree /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Verify clean immutable state and inspect all changed files/tests. Review entire bridge: pinned exact versions bypass latest; channel selectors still resolve latest; 404-only fallback; source affinity; canonical pagination/highest SemVer; strict/exact nightly annotations; raw HTTP and raw/already-normalized prefetched changelog filtering; GHCR AbortError identity/token/redirect; stable/nightly delta/full/cache/offline/API compatibility. Intentional nightly repo probe and source-neutral verified cache. Return NON-EMPTY findings-first report labeled PASS/CONCERN/MUST-FIX/BLOCKED with exact current file:line evidence, deterministic regression per defect, residual gaps. Final line exactly MERGE or DO-NOT-MERGE. Never modify files. Empty output forbidden.
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/.lore.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/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/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/AGENTS.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/init-eval/templates/sveltekit-app/package.json /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-b...
<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`. ...
<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/.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 ...
<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. ...
JJ_ABSENT HEAD 2f9b5d0d26124d445a916b8bfc584ad518720584 BASE_EXISTS commit HEAD_EXISTS commit MERGE_BASE ec83887a16f780f32fba4b7d710bad262dba3a22 STATUS_BEGIN STATUS_END DIFF_SHA256 e09b531a57734a11d4114214239aadefb6bca3fc16092f87989f71d825611552 - NAME_STATUS_BEGIN 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 NAME_STATUS_END STAT_BEGIN packages/cli/src/commands/cli/upgrade.ts ...
diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts index 8d8282368..2d4f927d2 100644 --- a/packages/cli/src/commands/cli/upgrade.ts +++ b/packages/cli/src/commands/cli/upgrade.ts @@ -1,414 +1,461 @@ /** * 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
@@ -25,287 +25,429 @@ import {
} from "./custom-ca.js";
import { stringifyUnknown, UpgradeError } from "./errors.js";
import { logger } from "./logger.js";
import { isProcessRunning } from "./process-utils.js";
/** Known directories where the curl installer may place the binary */
export const KNOWN_CURL_DIRS = [".local/bin", "bin", ".sentry/bin"];
/**
* Whether the current platform's filesystem is case-insensitive by default
* (Windows, macOS). Resolved once at module load — `process.platform` never
* changes at runtime.
*/
const IS_CASE_INSENSITIVE_FS =
process.platform === "win32" || process.platform === "darwin";
/**
* Legacy install directory (relative to home) that predates the XDG layout.
* The curl installer used to drop the binary here; migration moves it out. ...
diff --git a/packages/cli/src/lib/upgrade.ts b/packages/cli/src/lib/upgrade.ts
index 945adc48d..663adf772 100644
--- a/packages/cli/src/lib/upgrade.ts
+++ b/packages/cli/src/lib/upgrade.ts
@@ -1,149 +1,236 @@
/**
* 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/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,358 +1,428 @@
/** 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..dbdef1fb6 100644 --- a/packages/cli/src/lib/ghcr.ts +++ b/packages/cli/src/lib/ghcr.ts @@ -1,520 +1,567 @@ /** * 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..8cf12b4d4 100644 --- a/packages/cli/src/lib/release-notes.ts +++ b/packages/cli/src/lib/release-notes.ts @@ -1,109 +1,112 @@ /** * 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/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,120 +1,124 @@
/**
* 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,
...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 169: describe("parseInstallationMethod", () => {
Line 170: test("parses valid methods", () => {
Line 179: test("parses case-insensitively", () => {
Line 185: test("throws on invalid method", () => {
Line 192: describe("fetchLatestFromGitHub", () => {
Line 193: test("selects the latest CLI-prefixed Toolkit release", async () => {
Line 214: test("follows Toolkit release pagination to find the latest CLI release", async () => {
Line 245: test("selects the highest CLI SemVer across Toolkit release pages", async () => {
Line 265: test("rejects GitHub release pagination outside the selected source", async () => {
Line 283: test("classifies malformed GitHub release pagination as a network error", async () => {
Line 300: test("rejects cyclic GitHub ...
diff --git a/packages/cli/test/lib/upgrade.test.ts b/packages/cli/test/lib/upgrade.test.ts
index 7c6f63a4b..cb3daa43c 100644
--- a/packages/cli/test/lib/upgrade.test.ts
+++ b/packages/cli/test/lib/upgrade.test.ts
@@ -88,221 +88,369 @@ function fakeErrorProcess(message: string): FakeProc {
};
queueMicrotask(() => emitter.emit("error", new Error(message)));
return emitter;
}
// Swappable spawn implementation. Individual tests replace `spawnImpl.fn`
// before calling the code under test. The holder object is hoisted so
// vi.mock() can capture the reference; tests mutate `.fn` to swap behavior.
const { spawnImpl } = vi.hoisted(() => ({
spawnImpl: {
fn: (() => {
// placeholder — replaced per-test
}) as (cmd: string, args: string[], opts: object) => FakeProc,
},
}));
// Initialize with the real default now that fakeProcess is defined
spawnImpl.fn = () => fakeProcess(0);
vi.mock("node:child_process", async (importOriginal) => {
const orig = ...
diff --git a/packages/cli/test/lib/release-notes.test.ts b/packages/cli/test/lib/release-notes.test.ts
index 78963b471..e0a2e04bd 100644
--- a/packages/cli/test/lib/release-notes.test.ts
+++ b/packages/cli/test/lib/release-notes.test.ts
@@ -1,61 +1,67 @@
/**
* 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 type { GitHubRelease } from "../../src/lib/delta-upgrade.js";
+import { afterEach, beforeEach, describe, expect, test } from "vitest";
+import { UPGRADE_SOURCES } from "../../src/lib/binary.js";
+import {
+ fetchRecentReleases,
+ type GitHubRelease,
+} from ...
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,103 +1,229 @@
/**
* 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
@@ -67,95 +67,95 @@ function mockFetch(
}
// ============================================================================
// Helpers
// ============================================================================
const BINARY_NAME = getPlatformBinaryName();
function versionHex(version: string): string {
return Array.from(version)
.map((c) => c.charCodeAt(0).toString(16).padStart(2, "0"))
.join("");
}
function tempFile(name: string): string {
return join(
tmpdir(),
`delta-iso-${Date.now()}-${Math.random().toString(36).slice(2)}-${name}`
);
}
// ============================================================================
// resolveStableDelta
// ...
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
@@ -87,150 +87,153 @@ describe("feedbackCommand.func", () => {
"Please provide a feedback message."
);
});
test("throws ConfigError when Sentry is disabled", async () => {
const func = await feedbackCommand.loader();
const mockContext = {
stdout: { write: vi.fn(() => true) },
stderr: { write: vi.fn(() => true) },
};
// Sentry is disabled in test environment (no DSN)
await expect(
func.call(mockContext, {}, "test", "feedback")
).rejects.toThrow("Feedback not sent: telemetry is disabled.");
});
});
// Test the upgrade command func
describe("upgradeCommand.func", () => {
let originalFetch: typeof globalThis.fetch;
let restoreStderr: (() => void) | undefined;
beforeEach(() => {
...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/commands/cli.test.ts</path>
<type>file</type>
<content>
180:
181: await func.call(
182: context,
183: { check: true, method: "curl", json: true },
184: "2.0.0"
185: );
186:
187: const data = JSON.parse(getStdout()) as UpgradeResult;
188: expect(data.action).toBe("checked");
189: expect(data.warnings).toContain(
190: "Run 'sentry cli upgrade 2.0.0' to update."
191: );
192: });
193:
194: test("check mode shows already on target when versions match", async () => {
195: globalThis.fetch = (async () =>
196: new Response(JSON.stringify([{ tag_name: "cli@0.0.0-dev" }]), {
197: status: 200,
198: headers: { "Content-Type": "application/json" },
199: })) as typeof fetch;
200:
201: const func = await upgradeCommand.loader();
202: const { context, getStdout, restore } = ...
No files found
Found 3 matches
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/pnpm-lock.yaml:
Line 221: binpatch:
Line 2696: binpatch@0.4.2:
Line 7801: binpatch@0.4.2: {}
{
"name": "binpatch",
"version": "0.4.2",
"type": "module",
"license": "MIT",
"description": "Reusable binary delta-update engine: apply TRDIFF10/bsdiff+zstd patch chains to a binary and discover chains from a pluggable source (OCI/GHCR or GitHub Releases). Pure Node, zero product coupling.",
"author": "BYK",
"homepage": "https://github.com/BYK/binpatch#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/BYK/binpatch.git"
},
"bugs": {
"url": "https://github.com/BYK/binpatch/issues"
},
"keywords": [
"bsdiff",
"bspatch",
"trdiff10",
"zstd",
"binary",
"delta",
"patch",
"update",
"self-update",
"oci",
"ghcr"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist/",
"README.md",
"LICENSE"
],
"sideEffects": false,
"engines": {
"node": ...
import { createHash } from 'crypto';
import { copyFileSync, constants, openSync, writeSync, closeSync } from 'fs';
import { open, unlink, readFile, readdir, mkdir, writeFile } from 'fs/promises';
import { tmpdir } from 'os';
import { join } from 'path';
import { Readable } from 'stream';
import { zstdDecompressSync, createZstdDecompress } from 'zlib';
// src/bspatch.ts
var TRDIFF10_MAGIC = "TRDIFF10";
var HEADER_SIZE = 32;
var MAX_OUTPUT_SIZE = 2 * 1024 * 1024 * 1024;
function offtin(buf, offset) {
const view = new DataView(buf.buffer, buf.byteOffset + offset, 8);
const lo = view.getUint32(0, true);
const hi = view.getUint32(4, true);
const magnitude = hi % 2147483648 * 4294967296 + lo;
if (magnitude !== 0 && hi >= 2147483648) {
return -magnitude;
}
return magnitude;
}
function parsePatchHeader(patch) {
if (patch.byteLength < HEADER_SIZE) {
throw new Error(
`Patch too small: ${patch.byteLength} bytes (need at least ${HEADER_SIZE})`
);
}
const ...
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,46 +1,49 @@
/**
* 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 ...
2f9b5d0d2 (HEAD -> feat/toolkit-bridge-upgrade, origin/feat/toolkit-bridge-upgrade) fix(cli): isolate pinned upgrade resolution c6030fbf4 fix(cli): bind nightly manifests to versions b6484e0fb fix(cli): validate resolved upgrade metadata c8c815bbe fix(cli): validate GitHub release pagination 5ffa5cd0f fix(cli): harden upgrade source resolution dbee7e9f2 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
2f9b5d0d2 fix(cli): isolate pinned upgrade resolution
packages/cli/src/commands/cli/upgrade.ts | 51 ++++++++++++++++----------
packages/cli/src/lib/ghcr.ts | 11 ++++++
packages/cli/src/lib/release-notes.ts | 37 +++++++++++++++----
packages/cli/test/commands/cli/upgrade.test.ts | 8 ++--
packages/cli/test/lib/ghcr.test.ts | 34 +++++++++++++++++
packages/cli/test/lib/release-notes.test.ts | 31 ++++++++++++++++
6 files changed, 140 insertions(+), 32 deletions(-)
commit 2f9b5d0d26124d445a916b8bfc584ad518720584
Author: Burak Yigit Kaya <byk@sentry.io>
AuthorDate: Thu Sep 10 02:53:12 2026 +0000
Commit: Burak Yigit Kaya <byk@sentry.io>
CommitDate: Thu Sep 10 02:53:12 2026 +0000
fix(cli): isolate pinned upgrade resolution
diff --git a/packages/cli/src/commands/cli/upgrade.ts b/packages/cli/src/commands/cli/upgrade.ts
index 274789d10..2d4f927d2 100644
--- a/packages/cli/src/commands/cli/upgrade.ts
+++ ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts</path>
<type>file</type>
<content>
270: }
271: }
272:
273: type ResolveTargetOptions = {
274: method: InstallationMethod;
275: channel: ReleaseChannel;
276: versionArg: string | undefined;
277: channelChanged: boolean;
278: flags: UpgradeFlags;
279: };
280:
281: /**
282: * Result of resolving the target version.
283: *
284: * - `target`: the version string to upgrade/downgrade to (proceed with upgrade)
285: * - `UpgradeResult`: structured result when no upgrade should proceed
286: * (check-only mode, or already up to date)
287: */
288: type ResolveResult =
289: | { kind: "target"; target: string; source?: UpgradeSource }
290: | { kind: "done"; result: UpgradeResult; source?: UpgradeSource };
291:
292: async function resolvePinnedVersion(
293: lookupMethod: InstallationMethod,
294: target: string
295: ): ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts</path>
<type>file</type>
<content>
1000: persistChannel(channel, channelChanged, version),
1001: })
1002: );
1003: // Early exit for check-only (online) and up-to-date results.
1004: if (resolved.kind === "done") {
1005: const result = resolved.result;
1006: // For --check with a version diff, fetch changelog before returning.
1007: if (
1008: result.action === "checked" &&
1009: result.currentVersion !== result.targetVersion
1010: ) {
1011: result.changelog = await startChangelogFetch({
1012: channel,
1013: currentVersion: CLI_VERSION,
1014: targetVersion: result.targetVersion,
1015: offline: false,
1016: source: resolved.source,
1017: });
1018: }
1019: return yield new ...
285 async function resolveTargetVersion(
286 opts: ResolveTargetOptions
287 ): Promise<ResolveResult> {
288 const { method, channel, versionArg, channelChanged, flags } = opts;
289 const latest = await fetchLatestVersion(method, channel);
290 const target = versionArg?.replace(VERSION_PREFIX_REGEX, "") ?? latest;
291
292 log.debug(`Channel: ${channel}`);
293 log.debug(`Latest version: ${latest}`);
294 if (versionArg) {
295 log.debug(`Target version: ${target}`);
296 }
297
298 if (flags.check) {
299 return {
300 kind: "done",
301 result: buildCheckResult({ target, versionArg, method, channel, flags }),
302 };
303 }
304
305 // Skip if already on target — unless forced or switching channels
306 if (CLI_VERSION === target && !flags.force && !channelChanged) {
307 return {
308 kind: "done",
309 result: {
310 action: "up-to-date",
311 ...
<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/packages/cli/node_modules</path> <type>directory</type> <entries> .bin/ .vite-temp/ .vite/ @anthropic-ai/ @biomejs/ @clack/ @hono/ @mastra/ @sentry/ @spotlightjs/ @stricli/ @types/ @vitest/ binpatch/ chalk/ cli-highlight/ consola/ esbuild/ fast-check/ fflate/ fossilize/ hono/ http-cache-semantics/ ignore/ ink-spinner/ ink/ jpeg-js/ marked/ node-sqlite3-wasm/ p-limit/ peggy/ picomatch/ pixelmatch/ pngjs/ pretty-ms/ qrcode-terminal/ react-devtools-core/ react/ semver/ string-width/ (Showing 40 of 48 entries. Use 'offset' parameter to read beyond entry 41) </entries>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts</path>
<type>file</type>
<content>
570: if (!data.version) {
571: throw new UpgradeError("network_error", "No version found in npm registry");
572: }
573:
574: return data.version;
575: }
576:
577: /**
578: * Fetch the latest nightly version from GHCR.
579: *
580: * Performs an anonymous token exchange then fetches the OCI manifest for the
581: * `:nightly` tag. The version is extracted from the manifest annotation —
582: * only 2 HTTP requests total (token + manifest), no blob download needed.
583: *
584: * @param signal - Optional AbortSignal to cancel the requests
585: * @returns Latest nightly version string (e.g., "0.13.0-dev.1740000000")
586: * @throws {UpgradeError} When fetch fails or the version annotation is missing
587: */
588: export async function fetchLatestNightlyVersionWithSource(
589: signal?: ...
Found 100 matches (more matches available)
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/test/lib/upgrade.test.ts:
Line 208: await expect(fetchLatestFromGitHub()).resolves.toBe("1.3.0");
Line 238: await expect(fetchLatestFromGitHub()).resolves.toBe("1.2.3");
Line 261: await expect(fetchLatestFromGitHub()).resolves.toBe("1.3.0");
Line 277: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 294: await expect(fetchLatestFromGitHub()).rejects.toMatchObject({
Line 312: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 330: await expect(fetchLatestFromGitHub()).resolves.toBe("1.2.3");
Line 346: await expect(fetchLatestFromGitHub()).rejects.toThrow(
Line 363: const version = await fetchLatestFromGitHub();
Line 376: const version = await fetchLatestFromGitHub();
Line 389: const version = await fetchLatestFromGitHub();
Line ...
Found 42 matches /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts: Line 536: export async function fetchLatestFromGitHub( Line 621: const token = await getAnonymousToken(source, signal); Line 622: const manifest = await fetchManifest(token, tag, signal, source); Line 635: export async function fetchLatestNightlyVersion( Line 664: return fetchLatestNightlyVersion(); Line 667: ? ...
No files found
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/index.ts
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/index.ts</path>
<type>file</type>
<content>
1: /**
2: * Library entry point for programmatic Sentry CLI usage.
3: *
4: * Provides `createSentrySDK()` as the single public API — a typed SDK client
5: * with named methods for every CLI command, plus a `run()` escape hatch for
6: * arbitrary command strings.
7: *
8: * CLI runner is re-exported as `_cli` for the npm bin wrapper (`dist/bin.cjs`).
9: *
10: * @example
11: * ```typescript
12: * import createSentrySDK from "sentry";
13: *
14: * const sdk = createSentrySDK({ token: "sntrys_..." });
15: * const orgs = await sdk.org.list();
16: * const issues = await sdk.issue.list({ orgProject: "acme/frontend" });
17: *
18: * // Escape hatch for arbitrary commands
19: * const version = await sdk.run("--version");
20: * ```
21: *
22: * @module
23: */
24:
25: import { buildInvoker, buildRunner } ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/release-notes.ts</path>
<type>file</type>
<content>
500: // ────────────────────────── Nightly Commit Parsing ─────────────────────────
501:
502: /** Conventional commit prefix → category mapping */
503: const COMMIT_PREFIX_TO_CATEGORY: ReadonlyMap<string, ChangeCategory> = new Map([
504: ["feat", "features"],
505: ["fix", "fixes"],
506: ["perf", "performance"],
507: ]);
508:
509: /**
510: * Extract the unix timestamp from a nightly version string.
511: *
512: * Nightly versions use the format `X.Y.Z-dev.<unix-seconds>`.
513: *
514: * @returns Unix timestamp in seconds, or null if not a nightly version
515: */
516: export function extractNightlyTimestamp(version: string): number | null {
517: const match = NIGHTLY_VERSION_RE.exec(version);
518: if (!match?.[1]) {
519: return null;
520: }
521: const ts = Number.parseInt(match[1], ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/release-notes.ts</path>
<type>file</type>
<content>
420: type ChangelogBuildOptions = {
421: /** Maximum total list items across all sections, or unlimited when omitted. */
422: maxItems?: number;
423: /** Selected release source whose tag prefix filters the release list. */
424: source?: UpgradeSource;
425: };
426:
427: function normalizeChangelogReleases(
428: releases: GitHubRelease[],
429: source: UpgradeSource,
430: allowNormalized: boolean
431: ): GitHubRelease[] {
432: return releases.flatMap((release) => {
433: if (release.tag_name.startsWith(source.tagPrefix)) {
434: return [
435: {
436: ...release,
437: tag_name: release.tag_name.slice(source.tagPrefix.length),
438: },
439: ];
440: }
441: if (
442: allowNormalized &&
443: ...
diff --git a/packages/cli/test/lib/ghcr.test.ts b/packages/cli/test/lib/ghcr.test.ts
index e5c81d532..680851f68 100644
--- a/packages/cli/test/lib/ghcr.test.ts
+++ b/packages/cli/test/lib/ghcr.test.ts
@@ -1,37 +1,39 @@
/**
* 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 ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/upgrade.ts</path>
<type>file</type>
<content>
450: // Check stored info (fast path for non-Homebrew installs)
451: const stored = getInstallInfo();
452: if (stored?.method) {
453: return stored.method;
454: }
455:
456: // Legacy detection for existing installs (pre-setup command)
457: const legacyMethod = await detectLegacyInstallationMethod();
458:
459: // Auto-save detected method for future runs (best-effort —
460: // a read-only or broken DB shouldn't block detection)
461: if (legacyMethod !== "unknown") {
462: try {
463: setInstallInfo({
464: method: legacyMethod,
465: path: process.execPath,
466: version: CLI_VERSION,
467: });
468: } catch {
469: log.debug("Failed to persist install info (DB may be read-only)");
470: }
471: }
472:
473: return legacyMethod;
474: }
475: ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/binary.ts</path>
<type>file</type>
<content>
120: tagPrefix: "cli@",
121: },
122: {
123: githubRepo: "getsentry/cli",
124: ghcrRepo: "getsentry/cli",
125: tagPrefix: "",
126: },
127: ] as const satisfies readonly [UpgradeSource, ...UpgradeSource[]];
128:
129: /** The first source used by direct helper calls that do not resolve a source. */
130: export const PRIMARY_UPGRADE_SOURCE = UPGRADE_SOURCES[0];
131:
132: /** Valid methods that can be specified via --method flag */
133: const VALID_METHODS: InstallationMethod[] = [
134: "curl",
135: "brew",
136: "npm",
137: "pnpm",
138: "bun",
139: "yarn",
140: ];
141:
142: /**
143: * Parse and validate an installation method from user input. ...
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/lib/ghcr.ts</path>
<type>file</type>
<content>
210:
211: /**
212: * Fetch a short-lived anonymous bearer token for read-only access to the
213: * public `ghcr.io/getsentry/cli` package.
214: *
215: * The token exchange endpoint returns a JSON object with a `token` field.
216: * No credentials are required for public packages.
217: *
218: * @returns Bearer token string
219: * @throws {UpgradeError} On network failure or malformed response
220: */
221: export async function getAnonymousToken(
222: sourceOrSignal: UpgradeSource | AbortSignal = PRIMARY_UPGRADE_SOURCE,
223: signal?: AbortSignal
224: ): Promise<string> {
225: const source =
226: "ghcrRepo" in sourceOrSignal ? sourceOrSignal : PRIMARY_UPGRADE_SOURCE;
227: const externalSignal = "ghcrRepo" in sourceOrSignal ? signal : sourceOrSignal;
228: const url = ...
HEAD 4a1d5bad105c006193c68459175f5eb3c7f78e08 MERGE_BASE ec83887a16f780f32fba4b7d710bad262dba3a22 STATUS_BEGIN STATUS_END DIFF_SHA256 e09b531a57734a11d4114214239aadefb6bca3fc16092f87989f71d825611552 -
4a1d5bad1 (HEAD -> feat/toolkit-bridge-upgrade, origin/feat/toolkit-bridge-upgrade) test(cli): update pinned version fixture M packages/cli/test/commands/cli.test.ts packages/cli/test/commands/cli.test.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-)
Initial verification succeeded:
2f9b5d0d26124d445a916b8bfc584ad518720584ec83887a16f780f32fba4b7d710bad262dba3a22e09b531a57734a11d4114214239aadefb6bca3fc16092f87989f71d825611552Final verification failed:
4a1d5bad105c006193c68459175f5eb3c7f78e08test(cli): update pinned version fixturepackages/cli/test/commands/cli.test.tsThe requested exact-head merge gate is therefore invalid. The following findings are provisional results from inspecting 2f9b5d0d....
At /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/src/commands/cli/upgrade.ts:330-333, lookup routing depends on the persisted channel rather than whether the pinned target is nightly. With a stable persisted channel and --method npm 0.45.0-dev.123, versionExists() correctly checks GHCR because /packages/cli/src/lib/upgrade.ts:793-795 recognizes the nightly version, but resolvePinnedVersion() discards the selected source at lines 296-303.
Execution then checks channel === "nightly" at /packages/cli/src/commands/cli/upgrade.ts:1066-1078. Because the persisted channel is stable, it enters the standard npm path and attempts npm install -g sentry@0.45.0-dev.123, although nightly artifacts exist only as standalone binaries.
Impact: a valid pinned nightly fails for npm, pnpm, Bun, Yarn, and Homebrew installations instead of migrating to the source-affine standalone binary.
Fix: derive nightly distribution behavior from isNightlyVersion(target), always retain the source returned by pinned nightly resolution, and route non-curl installations through standalone migration. ...