Skip to content

feat(cli): Port the v3-to-v4 codemod from the archived getsentry/cli as cli3to4 - #1968

Open
sentry-junior[bot] wants to merge 11 commits into
mainfrom
feat/cli-codemod-port
Open

sentry-junior[bot] wants to merge 11 commits into
mainfrom
feat/cli-codemod-port

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

getsentry/cli is archived, which causes two problems:

  • The codemod's tag-triggered publish workflow there can't run anymore.
  • The v3 migration guide still told users to clone the archived repo to run the codemod.

This PR moves the codemod from codemods/sentry-v3-to-v4 (at getsentry/cli 3567c3c) into packages/codemod-cli3to4. Since it's now a workspace package, test.yml runs its jssg fixtures and its type check through the usual per-package matrix. The fixture directories are excluded from oxlint and oxfmt, because they're intentional input/expected pairs. The migration guide now points at this repo.

The package is renamed to @getsentry/cli3to4. To publish under a scope, Codemod has to verify it against a public repository whose GitHub owner matches the scope (docs). This repo's owner is getsentry, so @getsentry is the scope it can verify. The sentry account on GitHub is a user with no public repos, so @sentry can't be verified this way.

Publishing runs on pushes to main that change the version in packages/codemod-cli3to4/codemod.yaml. The workflow is path-filtered to that file, and scripts/codemod-version-changed.mjs compares the version with the push's previous commit. That way, other edits to codemod.yaml don't try to republish an existing version. It uses Codemod's codemod/publish-action, pinned to a SHA, which fetches the GitHub Actions OIDC token. The old workflow ran a bare codemod publish, and the CLI only reads CODEMOD_AUTH_TOKEN, so it would not have authenticated. Manual runs from the Actions tab still publish.

Checked against the real v3 and v4 APIs. I compared the transform with the @sentry/cli 3.8.2 typings and the current v4 SDK, and fixed what had drifted:

  • v3 exports the class as the named SentryCli. v2 used module.exports, and the old transform only handled that shape, so it left v3's import { SentryCli } and const { SentryCli } = require(...) untouched. These now map to v4's named createSentrySDK export, and so do require(...).SentryCli and X.SentryCli.
  • v4's typed release.deploy now takes environment and name, so newDeploy maps to it (renaming env) instead of falling back to run(...).
  • New mappings: listDeploys → release.deploys and sourceMaps.inject → sourcemap.inject.
  • releases.new(v, { projects }) becomes release.create({ orgVersion: v, project }), with the array joined into v4's comma-separated string. A string-array literal becomes a string literal; anything else is joined at runtime. A TODO is left only when the options object has keys other than projects.
  • v4 library change: sentry now exports getVersion() and getPath(), also available as static members of createSentrySDK. Because the codemod binds SentryCli to createSentrySDK, v3's SentryCli.getVersion() and SentryCli.getPath() keep working unchanged. getPath() returns dist/bin.cjs, which is a Node.js script rather than v3's native binary, so getPath() calls still get a TODO to spawn it with process.execPath. The library-usage and migration docs cover both, and the published type declarations include them.
  • The workflow also scans .jsx/.tsx, using the tsx grammar for those files.
  • The migration guide's v3 example now uses the named export.

End-to-end check: a small v3 TypeScript project type-checks against @sentry/cli 3.8.2. After running the codemod workflow on it, it type-checks against the locally bundled v4 sentry package. A CJS smoke run of the output loads the factory and runs run("--version").

Verification file: this PR adds .codemod/verification with the code from the Codemod UI. The publish workflow runs on merge, before Verify now has been pressed, so that first run will fail. After merge, press Verify now on app.codemod.com for getsentry/toolkit, then re-run the workflow (or run it manually) to publish 0.1.0.

Refs #1481, #1965

via @BYK.

--

View Session [Sentry]

getsentry/cli is archived, so its tag-triggered codemod publish workflow can
no longer run and the migration guide pointed users at an archived repo.

Move the codemod into packages/cli-codemod-v3-to-v4 so test.yml runs its
jssg fixtures and type check, and publish it from this repo with Codemod's
OIDC publish action. The package is now @getsentry/cli-v3-to-v4: Codemod
verifies a scope against a public repository whose GitHub owner matches it,
and this repository's owner is getsentry.

Refs #1481

Co-Authored-By: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sentry-local Ready Ready Preview Nov 5, 2026 12:31am UTC

Request Review

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

- Stop flagging `headers` as dropped: v4 `SentryOptions` accepts it. Point
  `customHeader` users at `headers` instead.
- Clear the whole argument list for nullish constructor args, so trailing
  commas (`new SentryCli(null,)`) no longer produce `SentryCli(,)`.
- Run the workflow over `.tsx` files, with a separate `tsx`-grammar step
  for JSX/TSX so the typescript step keeps `<T>x` assertions parsing.
…ented set-commits options

- Rewrite a bare `require("@sentry/cli")` to `require("sentry").default`.
  v3 exported the class as `module.exports`; v4's CJS module object only
  exposes the factory as `.default`/`createSentrySDK`.
- Keep the user's `setCommits` options object text verbatim and prepend
  `orgVersion`, so a trailing `// comment` can't swallow the closing brace.
Codemod checks `.codemod/verification` on the default branch to confirm
getsentry owns the `@getsentry` scope before the first publish.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename this to cli3to4 or something short like that? Also please verify the Codemod still works and is accurate after all the changes we have done to the repo recently.

Comment thread .github/workflows/publish-codemod.yml Resolved
…atch v3's real API

Rename `@getsentry/cli-v3-to-v4` to `@getsentry/cli3to4` (package dir
`packages/cli3to4`).

Publish from pushes to main that change the `version` in codemod.yaml,
instead of from tags. `scripts/codemod-version-changed.mjs` compares the
version with the push's previous commit so other codemod.yaml edits don't
try to republish an existing version. Manual runs still publish.

Checked the transform against @sentry/cli 3.8.2's typings and the current v4
SDK, and fixed what had drifted:

- v3 exports the class as the named `SentryCli` (v2 used `module.exports`),
  so `import { SentryCli }`, `const { SentryCli } = require(...)`,
  `require(...).SentryCli` and `X.SentryCli` were left untouched before.
  They now map to v4's named `createSentrySDK` export. `SentryCliOptions`
  maps to `SentryOptions`; other v3 type exports get a TODO.
- v4's typed `release.deploy` now takes `environment`/`name`, so `newDeploy`
  maps to it (renaming `env`) instead of falling back to `run(...)`.
- Map `listDeploys` → `release.deploys` and `sourceMaps.inject` →
  `sourcemap.inject`; flag `releases.new` `projects` and the static
  `getVersion()`/`getPath()` helpers.
- Fix the v3 example in the migration guide to use the named export.
@sentry-junior
sentry-junior Bot force-pushed the feat/cli-codemod-port branch from 1e0c02d to 87afd56 Compare November 4, 2026 23:23
@sentry-junior sentry-junior Bot changed the title feat(cli): Port the v3-to-v4 codemod from the archived getsentry/cli feat(cli): Port the v3-to-v4 codemod from the archived getsentry/cli as cli3to4 Nov 4, 2026
@github-actions github-actions Bot removed the risk: medium PR risk score: medium label Nov 4, 2026

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/cli3to4/scripts/codemod.ts Resolved
A file with only `import type { SentryCliOptions }` recorded no class
binding, so the codemod dropped its specifier and type rewrites and left
it on @sentry/cli. Commit those import edits when there is no binding.
…to packages/codemod-cli3to4

- releases.new(v, { projects }) now becomes release.create({ orgVersion, project })
  with the array joined into v4's comma-separated string (a literal for
  string-array literals, a runtime join otherwise). Only options with
  extra keys still get a TODO.
- The v4 sentry library gains getVersion() and getPath(), also exposed as
  static members of createSentrySDK, so v3's SentryCli.getVersion() and
  SentryCli.getPath() keep working after the codemod. getPath() keeps a
  TODO: it is a Node.js script now, not a native binary.
- Rename the directory to packages/codemod-cli3to4; the registry name
  stays @getsentry/cli3to4.

Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 21a6295. Configure here.

A conditional or other low-precedence projects value got `.join(",")`
appended to its last operand only. Wrap anything that isn't a plain
member/call/array expression in parentheses first.
…ine getPath

`require("@sentry/cli")` assigned later (`X = require(…)`), behind a
ternary or parens, or constructed inline (`new (require(…))()`) recorded
no binding, so constructor and method migration was skipped. Follow the
value through parens and ternary branches to its declarator or
assignment, treat an inline require as a constructor, and drop the
no-binding early return so these passes always run.

Also flag `require("@sentry/cli").getPath()` with the spawn TODO.
Comment thread .github/workflows/publish-codemod.yml Resolved
Comment thread packages/cli/src/index.ts Resolved
…m main

`new SentryCli().releases.finalize(v)` dropped `new` but kept the v3
`releases` call, since only variables counted as instances. Treat an
inline `new` expression as an instance too, and rewrite only the part of
the call after its receiver so the constructor edits still apply.

Manual runs of the publish workflow could publish any selected branch;
gate the publish job on `refs/heads/main`.

This branch was successfully deployed

1 active deployment
Preview — 244ddb90 Deployed Nov 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant