Dashboard › cli › Session 1YKYe1QydycF
1YKYe1QydycFdtJ3LPerform an independent, read-only adversarial correctness review of getsentry/cli PR #1569 pinned to exact base ec83887a16f780f32fba4b7d710bad262dba3a22 and exact head 723375b6ce4e37884e760fa0f848e7e7e3effcc9 in worktree /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade. Do not modify files, branches, commits, or PR state. Inspect the complete base..head diff and surrounding implementation/tests. Focus on dual-source CLI upgrade correctness: exact ordered Toolkit then legacy sources; fallback only on genuine typed HTTP 404; no network/non-404 fallback; successful probe response reuse; product-prefixed stable release discovery and SemVer ordering; source affinity through version validation, stable/nightly delta, full download, prefetch, and changelog; installer intentionally unchanged; compatibility wrappers and offline cache behavior. Verify all current source and test changes, not summaries. Report findings first with severity and exact file:line evidence plus deterministic regression for each defect. If none, state PASS with residual risks. End exactly MERGE or DO-NOT-MERGE.
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade</path> <type>directory</type> <entries> .craft.yml .git .github/ .gitignore .lore.md .nojekyll .npmrc AGENTS.md apps/ CHANGELOG.md codemods/ docs/ LICENSE.md node_modules/ package.json packages/ pnpm-lock.yaml pnpm-workspace.yaml README.md (19 entries) </entries>
<path>/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md</path> <type>file</type> <content> 1: # Jared (Outpost agent) 2: 3: Autonomous GitHub coding agent. Work in `/workspace/repo`. 4: 5: ## Model tiers 6: 7: The primary model is chosen per event (see `src/agents/models.ts`): heavy for 8: code-producing situations, cheaper for lightweight ones. 9: 10: | Role | Subagent | Model | 11: | --- | --- | --- | 12: | Triage / plan / review (heavy) | (primary Jared) | Claude Opus 4.8 | 13: | Triage / plan / review (light) | (primary Jared) | xAI Grok 4.3 | 14: | Explore | `explore` | OpenAI gpt-5-mini | 15: | Implement | `implement` | Moonshot kimi-k2.7-code | 16: | Ship (commit/push/PR) | `ship` | xAI Grok (`grok-build-0.1`) | 17: 18: Pipeline: triage → explore → plan → implement → review → ship. 19: (`worker` is a deprecated alias of `implement`.) 20: 21: Operators also talk to Jared directly from the Outpost dashboard. ...
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/AGENTS.md /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/AGENTS.md
/home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgrade/packages/cli/CONTRIBUTING.md
No files found
<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/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: ...
723375b6ce4e37884e760fa0f848e7e7e3effcc9 ec83887a16f780f32fba4b7d710bad262dba3a22 723375b6ce4e37884e760fa0f848e7e7e3effcc9 ec83887a16f780f32fba4b7d710bad262dba3a22 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/upgrade.test.ts M packages/cli/test/lib/binary.test.ts M packages/cli/test/lib/delta-upgrade.mocked.test.ts M packages/cli/test/lib/delta-upgrade.test.ts M packages/cli/test/lib/ghcr.test.ts M packages/cli/test/lib/release-notes.test.ts M packages/cli/test/lib/upgrade.test.ts M packages/cli/test/lib/version-check.test.ts packages/cli/src/commands/cli/upgrade.ts | 114 +++++--- packages/cli/src/lib/binary.ts | 154 +++++++++- packages/cli/src/lib/delta-upgrade.ts | 139 +++++++-- ...
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,209 +1,248 @@
/**
* 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,411 +1,416 @@
/**
* 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/upgrade.test.ts b/packages/cli/test/commands/cli/upgrade.test.ts
index ca9688e46..b79a9c342 100644
--- a/packages/cli/test/commands/cli/upgrade.test.ts
+++ b/packages/cli/test/commands/cli/upgrade.test.ts
@@ -135,159 +135,164 @@ function createMockContext(
env,
stdout: stdoutWriter,
stderr: {
write: (s: string) => {
errors.push(s);
return true;
},
},
stdin: process.stdin,
setFlags: () => {
// no-op for tests
},
} as unknown as SentryContext;
return {
context,
// Combine stderr (progress) and stdout (rendered result) so assertions
// work regardless of which stream a message goes to
getOutput: () => stderrChunks.join("") + stdoutChunks.join(""),
errors,
restore: () => {
process.stderr.write = origStderrWrite;
process.stdout.write = origStdoutWrite;
if (origPlain === undefined) {
delete ...
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,123 +1,249 @@
/**
* 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
@@ -42,145 +42,145 @@ vi.mock("../../src/lib/constants.js", async (importOriginal) => {
import { getPlatformBinaryName } from "../../src/lib/binary.js";
import {
attemptDeltaUpgrade,
resolveNightlyDelta,
resolveStableDelta,
} from "../../src/lib/delta-upgrade.js";
// ============================================================================
// Fetch mock infrastructure
// ============================================================================
let originalFetch: typeof globalThis.fetch;
beforeEach(() => {
originalFetch = globalThis.fetch;
});
afterEach(() => {
globalThis.fetch = originalFetch;
});
function mockFetch(
fn: (url: string | URL | Request, init?: RequestInit) => ...
diff --git a/packages/cli/test/lib/delta-upgrade.test.ts b/packages/cli/test/lib/delta-upgrade.test.ts
index 3752d4ae6..993e7c89a 100644
--- a/packages/cli/test/lib/delta-upgrade.test.ts
+++ b/packages/cli/test/lib/delta-upgrade.test.ts
@@ -1,100 +1,109 @@
/**
* Unit Tests for Delta Upgrade Module
*
* Tests the exported pure-computation functions that drive chain resolution
* for both stable (GitHub Releases) and nightly (GHCR) channels, plus
* async orchestration functions tested via fetch mocking.
*/
import { createHash } from "node:crypto";
import { existsSync, unlinkSync } from "node:fs";
import { access, readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
-import { afterEach, beforeEach, describe, expect, test } from "vitest";
-import { getPlatformBinaryName } from "../../src/lib/binary.js";
+import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
+import {
+ getPlatformBinaryName,
+ ...
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,235 +1,262 @@
/**
* 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
@@ -73,251 +73,295 @@ function fakeProcess(exitCode: number, stdoutData = ""): FakeProc {
});
return emitter;
}
/** Build a fake ChildProcess that emits an 'error' event instead of closing. */
function fakeErrorProcess(message: string): FakeProc {
const emitter = new EventEmitter() as FakeProc;
emitter.stdout = {
on: (_e: string, _cb: (chunk: Buffer) => void) => emitter.stdout,
resume: noopStream,
};
emitter.stderr = {
on: (_e: string, _cb: (chunk: Buffer) => void) => emitter.stderr,
resume: noopStream,
};
queueMicrotask(() => emitter.emit("error", new Error(message)));
return emitter;
}
// Swappable spawn implementation. Individual tests replace `spawnImpl.fn`
// before calling the code under test. ...
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,91 +1,94 @@
/**
* 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
@@ -122,0 +123 @@ import {
+ UPGRADE_SOURCES,
@@ -142,0 +144 @@ const {
+ resolveExistingUpgradeVersion,
@@ -190,0 +193,57 @@ describe("fetchLatestFromGitHub", () => {
+ test("selects the latest CLI-prefixed Toolkit release", async () => {
+ const requests: string[] = [];
+ mockFetch(async (url) => {
+ requests.push(String(url));
+ return new Response(
+ JSON.stringify([
+ { tag_name: "mcp@9.0.0" },
+ { tag_name: "cli@not-a-version" },
+ { tag_name: "cli@1.2.3" },
+ { tag_name: "cli@1.3.0" },
+ ]),
+ { status: 200 }
+ );
+ });
+
+ await expect(fetchLatestFromGitHub()).resolves.toBe("1.3.0");
+ expect(requests).toEqual([
+ ...
723375b6c fix(cli): address upgrade review findings 9c874298e fix(cli): address toolkit bridge review 04867eaf9 feat(cli): add toolkit upgrade bridge 3417558ce792780a5ada5fef4c6a57cd9a2f7106236abc803b3cea31fe79efbf -
<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: ...
* 🔴 (23:55) Install-method detection must always check for Homebrew first because stored install information may be stale. * 🔴 (23:55) The upgrade file-availability flow throws `UpgradeError` when the file never becomes visible or stays empty.
* 🔴 (23:55) `packages/cli/test/lib/upgrade.test.ts` added a Toolkit stable-release fixture with `{ tag_name: "cli@1.3.0" }`. * 🔴 (23:55) `versionExists` test coverage now verifies fallback to an unprefixed legacy tag when a Toolkit lookup returns HTTP 404. * 🔴 (23:55) `versionExists` test coverage distinguishes genuine missing-source HTTP 404 responses from transport failures; error text `"Fai…
* 🔴 (23:55) `packages/cli/test/lib/upgrade.test.ts` now imports `UPGRADE_SOURCES` from `../../src/lib/binary.js`. * 🔴 (23:55) Upgrade test fixtures now use Toolkit stable tags such as `{ tag_name: "cli@1.2.3" }`. * 🔴 (23:55) The nightly curl-method upgrade test now mocks `https://api.github.com/repos/getsentry/toolkit` as available with HTTP 200 before handling the GHCR token and `/manifests/n…
* 🟡 (23:54) `packages/cli/test/lib/delta-upgrade.test.ts` now imports `vi`, `UPGRADE_SOURCES`, and `useTestConfigDir`; it defines `LEGACY_UPGRADE_SOURCE = UPGRADE_SOURCES[1]` and throws `Legacy upgrade source is not configured` if that source is absent. * 🟡 (23:54) Stable delta tests in `packages/cli/test/lib/delta-upgrade.test.ts` now expect release-list requests against `api.github.com/repos/…
* 🟡 (23:53) `packages/cli/test/commands/cli/upgrade.test.ts` updated GHCR nightly mocks to recognize the Toolkit repository probe `https://api.github.com/repos/getsentry/toolkit` with HTTP 200 before handling token exchange and `/manifests/nightly`. * 🟡 (23:53) Stable-version mocks in `packages/cli/test/commands/cli/upgrade.test.ts` now serve `[{ tag_name: \`cli@${version}\` }]` for URLs contai…
Date: Sep 9, 2026 * 🔴 (23:53) User specified that `detectInstallationMethod()` in `packages/cli/src/lib/upgrade.ts` must always check Homebrew first because stored installation information may be stale—for example, when a user switches from a recorded curl installation to Homebrew. The cheap realpath check is authoritative. * 🟡 (23:53) `detectInstallationMethod()` installation-detection priorit…
* 🟡 (23:52) `packages/cli/src/lib/delta-upgrade.ts` was made source-aware: it imports `getGitHubReleasesUrl`, `PRIMARY_UPGRADE_SOURCE`, and `UpgradeSource` from `./binary.js`, replacing the fixed `GITHUB_RELEASES_URL` and fixed `GHCR_REPO` dependencies. * 🟡 (23:52) `packages/cli/src/lib/delta-upgrade.ts` added `isGitHubRelease(value: unknown)`, which accepts only non-null objects having string …
Date: Sep 9, 2026 * 🔴 [enforced-network-requirement] (23:51) User stated package managers always need network access to fetch and install packages; `--offline` upgrade is supported only for curl-installed binaries, and `validateMethod()` throws `UpgradeError("unsupported_operation", "Offline upgrade is only supported for curl-installed binaries.")` when `offline && method !== "curl"`. * 🔴 [enfo…
Date: Sep 9, 2026 * 🔴 [enforced-workflow] (23:50) User stated to always load `repo-setup` before situation skills; repository skills live under `.agents/skills/`, are generated from the canonical `skills/` tree by `scripts/sync-skills.mjs`, and target-repository `AGENTS.md` / `CONTRIBUTING.md` files must be read first. * 🔴 [enforced-workflow] (23:50) User stated to always check `package.json` f…
Date: Sep 9, 2026 * 🔴 [requested-review] (23:49) User requested an independent, read-only adversarial correctness review of getsentry/cli PR #1569, pinned to exact base commit ec83887a16f780f32fba4b7d710bad262dba3a22 and exact head commit 723375b6ce4e37884e760fa0f848e7e7e3effcc9, using worktree /home/byk/.local/share/opencode/worktree/0e8c2f3bbe145a3280d6fb6ed900a5121f382cf0/toolkit-bridge-upgra…