docs: add v3→v4 migration guide (sentry-cli → sentry)#1201
Conversation
Adds docs/src/content/docs/migrating-from-v3.md covering the major-release upgrade from the legacy Rust sentry-cli (v3) to the new TypeScript sentry CLI (v4): - The binary/package rename (sentry-cli → sentry, @sentry/cli → sentry) with install steps. - Command changes grouped into still-works / renamed (plural→singular) / moved (login→auth login, update→cli upgrade, deploys→release deploys, upload-dif→ debug-files upload, upload-proguard→proguard upload, difutil→debug-files) / removed (send-metric). - A copy-paste `sentry-cli()` compatibility shim that transparently translates moved/renamed commands so existing scripts keep working. - Output/scripting (--json), OAuth auth, semantic exit codes, debug-ID-first sourcemaps, and config backward-compat notes. Wires the page into the Getting Started sidebar. `astro build` passes.
|
Codecov Results 📊✅ Patch coverage is 100.00%. Project has 5342 uncovered lines. Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
+ Coverage 81.81% 81.81% —%
==========================================
Files 423 423 —
Lines 29374 29374 —
Branches 19131 19131 —
==========================================
+ Hits 24032 24032 —
- Misses 5342 5342 —
- Partials 1990 1989 -1Generated by Codecov Action |
Addresses the "will SentryCli().setCommits still work?" question on the guide: - migrating-from-v3.md: new "Node.js wrapper (SentryCli class)" section — the v4 package has no SentryCli class; it's usable as a library via createSentrySDK(). Includes a full v3→v4 method mapping table and notes sourcemaps are now debug-ID-first / decoupled from releases. - codemods/sentry-v3-to-v4.cjs: a jscodeshift transform that rewrites the @sentry/cli import, `new SentryCli(configFile, options)` → createSentrySDK (authToken→token), the releases.* method chain → release.*/sourcemap.*, and execute() → run(). Where option *shapes* changed (e.g. uploadSourceMaps include → sourcemap.upload directory), it inserts `// TODO(sentry-v4): …` breadcrumbs instead of guessing. - test/codemods/sentry-v3-to-v4.test.ts: 10 cases covering import/require, constructor, the release flow, setCommits/uploadSourceMaps/newDeploy reshaping, execute→run, and literal-inlining vs spread. Wired into test:unit. - codemods/README.md: usage. biome.jsonc: force-ignore the .cjs (its module graph trips Biome's type-analysis limit, same as custom-ca.ts). jscodeshift, @types/jscodeshift, tslib added as devDependencies.
szokeasaurusrex
left a comment
There was a problem hiding this comment.
Added some comments.
Most of them are small; my biggest concern is about the lack of mention about whether any of the command line flags need to be changed
|
Had the clanker take a quick look at the differences between the two CLI's APIs for some of the more important commands; seems like the new CLI lacks support for several flags that were supported in the old CLI. The full investigation is here. |
Migration guide (per @szokeasaurusrex review): - Add a "Global flags and options" section — the key gap: v4 keeps --org/--project/--log-level/-v as globals but drops per-command --auth-token/--url/--header (now SENTRY_AUTH_TOKEN / SENTRY_URL|SENTRY_HOST / SENTRY_CUSTOM_HEADERS) and --quiet. The shim now translates --auth-token/--url/--header into those env vars per-call. - Drop the `orgs` alias (v3 only had `organizations`) and the send-metric "Removed" row (metrics were removed back in v3.0, not a v3→v4 change). - Correct the sourcemaps section: v3 command is `sentry-cli sourcemaps upload` (not `releases files … upload-sourcemaps`); sourcemaps were debug-ID-first since v2 — drop the false "now debug-ID-first" framing; document the single-<directory> positional + dropped flags instead. - Clarify SENTRY_TOKEN is a v4 alias (not a v3 carryover); note upload-dif/dsym/ proguard were already soft-deprecated hidden aliases in v3; align the renamed-groups rows; fix the "open an issue" link to getsentry/cli. Codemod (per Cursor Bugbot): - Gate the .execute()→.run() and .releases.* rewrites on a tracked SentryCli instance so unrelated .execute()/.releases APIs (DB clients, etc.) are left alone. Add regression tests for both.
|
Thanks @szokeasaurusrex — all addressed in the latest push: Factual fixes
The big one (flags) — new Global flags and options section documenting which globals survive ( Codemod (Bugbot) — the One correction on the gist: |
…ions Two more review findings on the codemod: - HIGH (Sentry bot): `new SentryCli(myVar)` with a single non-object argument silently dropped it (only ObjectExpression args were kept), losing the user's config. Now the arg is preserved as `createSentrySDK(myVar)` with a TODO noting the v3 configFile-vs-options ambiguity (v3's first param was a configFile path, removed in v4). - Medium (Bugbot): `newDeploy(v, o)` spread v3 fields like `env`/`name` into `release.deploy(...)`, but v4 takes those as part of the positional `orgVersionEnvironmentName` (org/version/env/name), not option keys. The codemod no longer spreads the options for deploy and emits a TODO explaining the reshaping; the guide's mapping row is corrected to match. Also fixed `addTodo` to attach to `*Declaration` nodes (e.g. `const cli = …`), not just `*Statement`, so the constructor TODO actually lands. +2 tests (13 total).
…pre-replace - When constructor options are a variable/expression (not an inline object), the codemod can't rename `authToken`→`token` in place, so it now emits a TODO to do it manually (Cursor Bugbot: "authToken not renamed in variables"). - Emit all TODO breadcrumbs before `path.replace()` rather than after, so the comment always attaches to the original node's statement (Sentry bot). - +1 test (14 total).
Cursor Bugbot: always renaming the binding to `createSentrySDK` could leave
dangling references (re-exports, passing the module around) or duplicate
declarations (multiple requires in one scope).
Now the codemod only changes the module specifier (`@sentry/cli` → `sentry`) and
keeps the user's local name; `new <name>(…)` becomes `<name>(…)` since v4's
default export is the createSentrySDK factory, not a class. Every reference to
the binding stays valid. +1 test (custom name + `export {}` reference); 15 total.
|
Two more codemod findings from the latest review, both addressed in
|
…aders
Three more Cursor Bugbot findings:
- authToken shorthand: `{ authToken }` now expands to `{ token: authToken }`
(was printing a bare `{ token }`, referencing a nonexistent binding).
- execute→run: adds a TODO noting the argv tokens are raw v3 CLI args that may
need remapping to v4 command names (releases→release, new→create, …).
- shim: multiple `--header` flags now merge into one semicolon-separated
SENTRY_CUSTOM_HEADERS (v4's expected format) instead of overwriting.
+2 codemod tests (16 total).
|
Latest Bugbot review — all three addressed in
Codemod tests: 16, all green. |
Make the flag documentation accurate and complete against the v4 command surface (verified from the generated SDK): - Sourcemaps: list the flags v4 `sourcemap upload` keeps vs. the full set of v3 flags that are NOT present (added --note, --wait-for, --debug-id-reference); note `sourcemap inject` also drops --release and takes a single <directory>. - Global-flags caution: call out notable per-command drops — `release set-commits --ignore-missing` and `release deploy` env/name being positional.
sentry[bot]: in the 2+ arg constructor branch, a `null`/`undefined` second argument was assigned to `options` (a truthy AST node), so the codemod emitted a misleading "update authToken" TODO. Now `new SentryCli(cfg, null)` → `X()` with no options and no TODO, mirroring the single-arg null handling. Adds a fixture (18 total).
The three Cursor findings still showing ( |
Cursor Bugbot (High): the leading-flag loop broke on any unrecognized token, including still-valid v4 globals (`--org`/`--project`/`--log-level`/`--fields`/ `-v`/`--json`). If one preceded `--auth-token`/`--url`/`--header`, translation never ran and the removed flags were forwarded to `sentry`. Now those globals are consumed (with their values) into a `lead` prefix that is re-applied before the translated command, so env-flag translation still runs and scanning stops only at the real command word. Command-level `--url` stays untouched. Verified with a functional harness (leading `--org` + `--auth-token`; global vs command `--url`; `--log-level` + `releases new`).
Verified with a functional harness:
|
…ass env)
Cursor Bugbot (High): the SDK's typed `release.deploy` collapses
version/environment/name into a single `orgVersionEnvironmentName` positional and
forwards it as one argv token, so it can't supply the required `<environment>` —
`deploy({ orgVersionEnvironmentName: "1.0.0" })` fails at runtime with a
missing-environment error.
Both the codemod and guide now migrate `newDeploy` to the raw `run(...)` escape
hatch instead: the codemod emits `sdk.run("release", "deploy", <version>)` with a
TODO to add the environment/name positionals + url/started/finished/time flags;
the guide row shows `sdk.run("release", "deploy", v, env, name, "--url", url)`.
|
…ploys
Three findings:
- Codemod (High, sentry[bot]): `uploadSourceMaps` no longer spreads the v3
options — `include` is a path array but v4 `sourcemap.upload` takes a single
`directory` string, so spreading emitted an invalid `include` key. Now emits
`sourcemap.upload({ release })` + a TODO to set `directory`.
- Shim (High): value-taking leading flags used `shift 2`, which hangs the shell
when the value is missing (bash leaves `$#` unchanged). Now `shift 2 2>/dev/null
|| shift` — verified value-less `--org`/`--auth-token` no longer loop.
- Shim (High): v3 nested deploys under `releases`, so `releases deploys …` fell
through to `release deploys …` with invalid v3 subcommands/flags. Both the
top-level `deploys` and `releases deploys` now surface the manual-migration
note (create uses positional environment/name in v4).
|
Three more, all fixed:
|
…igration Cursor Bugbot: the blanket deploy rejection also refused `deploys`/`deploys list` (and `releases deploys … list`), which map cleanly to `release deploys`. A small `_scli_deploys` helper (reused by the top-level `deploys` and nested `releases deploys` paths) now drops a trailing v3 `list` and runs `release deploys`; only an invocation containing `new` (create — env/name became positionals) prints the manual-migration note. Verified list/create for both paths.
|
…1202) ## Problem The `sentry docs` PR-preview link was returning **404** for new PRs (e.g. #1201), while older previews still worked. Root cause: - `docs-preview.yml` triggered on `pull_request` `[opened, synchronize, reopened]` — **never `closed`** — so `rossjrw/pr-preview-action`'s auto-cleanup never ran. - **46 stale previews** (each a full copy of the docs site) accumulated in `gh-pages`, pushing the **GitHub Pages build over its size/time limits**. Builds started failing intermittently (`Page build failed`), so newly-deployed previews 404'd while previously-published ones kept serving. ## Fix (pattern ported from `getsentry/loreai`) **`docs-preview.yml`** - Add `closed` to the `pull_request` types and **remove the `pull_request` paths filter** — a `closed` event must always fire so the preview can be removed. - A `dorny/paths-filter` step + a central **`gate`** step decide: - `build` → push, or a docs PR being opened/updated (fork PRs still build to surface compile errors). - `deploy` → build, **or any `closed` event** (cleanup), never for fork PRs. - On close, `pr-preview-action` `action: auto` **removes** the preview — the actual cleanup that was missing. **`cleanup-doc-previews.yml` (new)** - Weekly (`cron`) + `workflow_dispatch` **safety net**: sweeps `gh-pages` for `_preview/pr-<n>` dirs whose PR is no longer `OPEN` and removes them. Uses a **blobless, no-checkout clone + index rewrite**, so it never downloads the (large) preview file contents. Catches anything the on-close path misses (e.g. PRs closed while the workflow was broken — exactly how we got here). ## Already done out-of-band The 46 already-stale previews were pruned from `gh-pages` directly (blobless index rewrite) to **unblock the failing Pages build immediately** — previews now serve again (verified `pr-1201` → 200). This PR prevents recurrence. ## Validation - Both workflows: valid YAML; `actionlint` clean (only a pre-existing SC2088 on the unchanged `--url-prefix "~/"` line, a deliberate Sentry URL prefix). - Gate logic walked through for push / docs-PR open+sync / non-docs PR / close / fork / fork-close.
Independent flag-compat review found real gaps beyond the common path: - Shim + guide: v3 `--allow-failure` / `SENTRY_ALLOW_FAILURE` (made any command exit 0) is not implemented in v4 and would be rejected as unknown. The shim now strips it and emulates the old "never fail" behavior; the Dropped section documents it. Verified: failing command → rc=3 normally, rc=0 with the flag/env; flag is not forwarded to `sentry`. - Codemod: the constructor now emits a TODO for v3 `SentryCliOptions` with no v4 equivalent (apiKey, url, dsn, silent, customHeader, headers, org, project, vcsRemote) — previously `apiKey` (auth-critical) was dropped silently. Added a `constructor-apikey` fixture (19 total). - Codemod: `setCommits` TODO now names the actually-dropped `ignoreMissing`/ `ignoreEmpty` keys instead of a generic hint. - Guide Dropped section: added `--silent` (alias of `--quiet`), `set-commits` `--ignore-empty`, `SENTRY_API_KEY`/`SENTRY_VCS_REMOTE` no longer honored, and the self-hosted-only note for `SENTRY_CUSTOM_HEADERS`.
|
Ran an independent flag-compatibility review pass. The common path (
|
… `live` Two sentry[bot] findings: - CI (High): `ci-status` didn't include `codemod-test` in `needs`/result checks, so a codemod-test failure wouldn't block merge — defeating the job. Added it to `needs`, the failure/cancel result scan, and the skipped-cascade guard (using the existing `changes.outputs.codemod` filter). - Codemod (Medium): the `execute(args, live)` → `run(...args)` transform dropped the `live` arg silently. (The bot framed it as dry-run→live, but v3 `live` actually controls output handling, not execution — verified in @sentry/cli helper.js: `live:true`/`'rejectOnError'` inherit stdio, `live:false`/absent capture+return stdout.) v4 `run()` always captures/returns output, so the TODO now calls out the dropped `live` and the streamed-stdio behavior change.
|
Two more findings addressed:
|
Two more findings:
- Shim (High): bare plurals only mapped to `list` when zero args remained, so
`sentry-cli projects --json` became `project --json` → `project`'s default
`view` (not `list`), and `releases --json` dropped the subcommand. Now a
remaining leading flag (or no args) still inserts `list` and forwards the
flags. Verified `projects --json` → `project list --json`, `projects list
--json` stays single-`list`, and subcommands (`new`/`view`) still route.
- Codemod (Medium): CJS binding detection missed `const X =
require("@sentry/cli").default` — the require's parent is a member_expression,
not the declarator, so the file was skipped. Now walks up through the member
expression to the declarator. Added a `require-default` fixture (20 total).
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 44c8a84. Configure here.
Cursor Bugbot (Medium): the constructor TODO wrongly listed `url`/`org`/`project`
as having no v4 equivalent — they're first-class `SentryOptions` keys
(sdk-types.ts), so the hint could push users to strip working options. Narrowed
the unmapped set to the keys that genuinely need attention: apiKey (→ token),
dsn (→ SENTRY_DSN), silent/customHeader/headers/vcsRemote (dropped). The
`{ token, org }` fixture now emits no TODO; the apiKey/silent fixture keeps it.
|
Sentry bot (High): `import * as X from "@sentry/cli"; new X.default()` wasn't recognized as a constructor (the filter only matched bare `bindings.has(c.text())`, never a `member_expression`), so the file was left partially migrated. Namespace import names are now tracked separately (`nsBindings`) and the constructor filter also accepts `new <ns>.default(...)`. The existing `new ` drop preserves the `X.default` callee. Added a `namespace-default` fixture (21 total).
|

Adds a migration guide for the upcoming major release (v3 → v4), where the legacy Rust
sentry-cliis replaced by the new TypeScriptsentryCLI.New page:
docs/src/content/docs/migrating-from-v3.md(wired into the Getting Started sidebar, between Installation and Self-Hosted).Covers
sentry-cli→sentrybinary,@sentry/cli→sentrynpm package, with install/uninstall steps for npm/brew/curl/npx.info,send-event,send-envelope,bash-hook,sourcemaps,debug-files,react-native …).releases→release,projects→project, …) with a note that bare plurals still work as list shortcuts.login→auth login,update→cli upgrade,uninstall→cli uninstall,deploys→release deploys,upload-dif/upload-dsym→debug-files upload,difutil→debug-files,upload-proguard→proguard upload).send-metric).sentry-cli()shim that transparently translates every moved/renamed command to its v4 form, so existing scripts and muscle memory keep working during migration (the user-requested "practical alternatives").--jsoninstead of screen-scraping,NO_COLOR/SENTRY_PLAIN_OUTPUT), OAuth device-flow auth (tokens still honored), semantic exit codes, debug-ID-first sourcemaps, and config/.sentryclircbackward-compat.All command mappings were verified against the actual v4 route map (
src/app.ts),src/commands/, and the existing exit-codes/configuration docs.cd docs && pnpm run buildpasses (page renders, no broken links).