Repository navigation
feat(publish): Add built-in development version bump after release - #950
Merged
Merged
Conversation
Add an opt-in postReleaseBump setting. After craft publish merges the release branch, Craft bumps the release unit to the next development version (default preminor with preid dev) through the same target logic as the automatic pre-release bump, commits it with #skip-changelog, pulls with rebase, and pushes to the merge target. The bump is skipped after a pre-release or when the release branch isn't merged, and a failure warns without failing the release. Workspaces inherit the setting and can opt out with postReleaseBump: false. Closes #949 Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
Contributor
|
…alVer and templates
postReleaseBump now writes the development version with the same step
craft prepare uses: the preReleaseCommand when set, otherwise the
automatic target bumps. Repos whose post-release scripts just re-run
their bump script with a dev version (sentry, snuba, self-hosted,
sentry-godot) can switch to it.
Replace bump/preid with bump (patch, minor, major, calver) and a version
template ('{{version}}-dev.0' by default). calver moves to the first
version of the next CalVer period, and the default for the calver
policy. A template without {{version}}, such as 'nightly', is used as
is. postReleaseBump: true enables the defaults.
Move runPreReleaseCommand to src/utils/preReleaseCommand.ts so publish
can use it without importing the prepare command.
Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
Contributor
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 80bdb6f. Configure here.
postReleaseBump: patch (or minor, major, calver) is shorthand for
{ bump: patch } with the default -dev.0 template. The schema normalizes
true and the bump-type shorthand to the object form, so publish only
deals with one shape. Document why the dev marker should be a
pre-release suffix rather than +build metadata.
Co-Authored-By: BYK <126780+BYK@users.noreply.github.com>
The development version reaches spawned commands as an argument, and
spawnProcess expands whole-argument ${VAR} values from the full process
env. A crafted template such as '${NPM_TOKEN}' could write a secret into
a version file that is then committed and pushed. Allow only letters,
digits, '.', '_', '+', '-' and the {{version}} placeholder, and replace
the placeholder directly instead of rendering it with mustache.
BYK
approved these changes
Nov 2, 2026
BYK
added a commit
that referenced
this pull request
Nov 2, 2026
The skip magic word was matched as a plain substring anywhere in a commit message or PR body. #950's squash commit described the post-release bump commit in backticks, so the feature dropped itself: `craft prepare auto` found zero commits since 3.6.0 and the [3.7.0 release failed](https://github.com/getsentry/craft/actions/runs/41427876670/job/125222689992) with "no commits found since the last release". Now mentions inside Markdown inline code spans (including ones hard-wrapped across lines, as in squash commits) and fenced code blocks are ignored. The word still works anywhere in plain prose, so the documented "anywhere in your commit message or PR body" behavior holds, and Craft's own `meta: Bump new development version` commits are still skipped. Both checks (the early git commit-body filter and `shouldExcludePR` for PR bodies) share the new helper. Once this merges, the next auto release will count #950 again (verified against its actual commit message), so a plain re-run should get 3.7.0 out with #950 in the changelog. <!-- junior-request-attribution:start --> via @BYK. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3Af8508ca3568070a0389d1c35 --> -- [View Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Af8508ca3568070a0389d1c35/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
8 tasks done
BYK
added a commit
that referenced
this pull request
Nov 2, 2026
Moves Craft's own release config to the built-in post-release bump from #950. That's the follow-up for #949. ## Changes - **`postReleaseCommand` → `postReleaseBump: true`.** The old command ran `npm version preminor --preid=dev`, then committed with `#skip-changelog`, pulled with rebase, and pushed. Now Craft computes the default `{{version}}-dev.0` (`3.7.0` → `3.8.0-dev.0`, the same as before) and writes it through the existing `preReleaseCommand`, which runs `npm version $CRAFT_NEW_VERSION`. The commit message stays the same. - **`minVersion: '2.14.0'` → `'3.7.0'`.** An older Craft drops the unknown `postReleaseBump` key without an error, so this makes it fail loudly instead of skipping the bump. The only behavior gates between those versions are: - auto-bump (2.21.0): doesn't apply, since we keep `preReleaseCommand`. - workspaces (2.29.0): not used here. One small behavior change: after a pre-release, Craft now skips the bump. The old script bumped anyway. ## Merge order This stays a draft until #950 is merged **and** shipped in 3.7.0. `craft prepare` here runs the build artifact from `master`, but `publish` may run a released Craft. Merging early could fail a release on the `minVersion` check. ## Checks - I didn't rebuild `dist/craft` for the final schema. The #950 config tests cover `postReleaseBump: true`. - `prettier --check .craft.yml` passes. <!-- junior-request-attribution:start --> via @BYK. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3Af8508ca3568070a0389d1c35 --> -- [View Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3Af8508ca3568070a0389d1c35/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
This was referenced Nov 2, 2026
BYK
added a commit
that referenced
this pull request
Nov 2, 2026
…955) Closes #948. A release unit can now list extra files that automatic version bumping writes the new version into. Until now, the only option was a custom `preReleaseCommand`, which also turns off the automatic bump. ```yaml minVersion: '3.8.0' workspaces: packages/cli: versioning: policy: auto files: - path: packages/mcp-server/package.json # json, field: version - path: packages/cli/plugins/sentry-cli/.claude-plugin/plugin.json stripPrerelease: true - path: packages/cli/plugins/sentry-cli/skills/sentry-cli/**/*.md # frontmatter - path: crates/*/Cargo.toml # toml, field: package.version - path: src/sdk/_version.py format: regex pattern: '/__version__ = "([^"]+)"/' ``` ### Formats The formats cover every kind of file Craft's version-bumping targets edit, plus `frontmatter`, and `regex` as an escape hatch. Craft infers the format from the file name: | `format` | Inferred for | Updates | Implementation | | --- | --- | --- | --- | | `msbuild` | `*.csproj`, `*.fsproj`, `*.vbproj`, `*.props`, `*.targets` | Version properties, and clears `<VersionSuffix>` when there's a `<VersionPrefix>` | The `nuget` target's code | | `gem` | `*.gemspec`, `version.rb` | `spec.version = "…"`, `VERSION = "…"` | The `gem` target's patterns | | `mix` | `mix.exs` | `version: "…"`, `@version "…"` | The `hex` target's patterns | | `json` | `*.json` | String at `field` (default `version`) | New: `jsonc-parser` replaces only the value's characters | | `toml` | `*.toml` | String at `field`. Default: `package.version` in `Cargo.toml`, `project.version` in `pyproject.toml`, otherwise `version` | New: a table-aware line scanner that skips arrays of tables. Shared with the `pypi` target | | `yaml` | `*.yaml`, `*.yml` | Scalar at `field`; skips sequences and block scalars | New: an indentation-aware scanner. The result is parsed back with js-yaml to confirm the new version is at `field`. Shared with the `pub-dev` target | | `frontmatter` | `*.md`, `*.mdx` | Same as `yaml`, inside the `---` frontmatter | Shares the `yaml` code | | `regex` | never inferred | Capturing group of every match of a `/source/flags` string (same syntax as `includeNames`) | New | The gem, hex and nuget patterns moved to `src/utils/versionPatterns.ts`, so those targets and `versioning.files` share one definition. `NugetTarget.updateVersionInXml` now calls the shared `setMsBuildVersion`. Behavior is unchanged. The new TOML and YAML editors live in the same module, and the targets that direct-edit those files now use them: - `pypi` direct edit: replaced its regex, which rewrote the first `version = ` line in any table, with the TOML editor on `project.version`. Hatch, poetry and setuptools_scm are unchanged. - `pub-dev`: edits `version` in place instead of loading and re-dumping the whole pubspec with js-yaml, which dropped comments and formatting. It falls back to the old rewrite only when there's no single top-level `version` to edit, so a pubspec without one still gets it added. `npm` (`npm version`) and `crates` (`cargo set-version`) keep their CLIs: those also update `package-lock.json`, workspace members and dependent crates' version requirements, which a one-file edit doesn't cover. `json` has no target to share with, since only `npm` writes JSON. ### Design decisions - **Location:** `versioning.files`, with `stripPrerelease` set on each entry. Workspaces replace `versioning` as a whole, like every other field except `github`, so a workspace that declares `versioning` also replaces the top-level `files`. The workspace docs say so. - **Paths are relative to the repository root**, same as `changelog.includePaths`. Absolute paths and `..` are rejected using the same safety check as workspace globs. Glob matches that resolve outside the repository (symlinks) are dropped. - **`preReleaseCommand` plus `versioning.files` is a config error.** The check runs on the resolved config, so a workspace can't get `files` from one level and a `preReleaseCommand` from another. `preReleaseCommand: ''` counts as a conflict too. An empty `files: []` is allowed. - **`minVersion: 3.8.0` is required** (3.7.0 shipped without this feature). Older Crafts silently drop unknown keys. This gate makes those configs also set a minVersion that older Crafts refuse to run, instead of releasing without updating the files. - **Strict, all-or-nothing:** a path that matches no files, or a matched file with nothing to update, fails `craft prepare` before any file is written. Writes go through `safeFs`. In a dry run, the files are written to the temporary copy of the repo (the path comes from the isolated git client). - **Updated files count as a bump:** they trigger the `--all` commit, and a unit whose targets can't bump anything no longer gets the "nothing to bump" warning or error. - **`postReleaseBump` writes them too.** `postReleaseBump` (#950) writes the development version with the same `runPreReleaseCommand` step, so it now passes `versioning.files` as well. With `stripPrerelease`, a `1.3.0-dev.0` development version is written as `1.3.0`. A template that isn't semver, such as `version: nightly`, can't be stripped: the bump fails with a warning, and the release itself still succeeds. ### Testing - `src/utils/__tests__/versionFiles.test.ts` (21 tests): every format, formatting preservation, default TOML fields, nested YAML keys that skip sequences and block scalars, globs, CRLF, symlink escape, writing nothing when a later entry fails, several entries for one file. - `src/__tests__/config.test.ts`: format inference, schema errors, the minVersion gate, unsafe paths, the `preReleaseCommand` conflict including the inherited case. - `runPreReleaseCommand` tests in `src/commands/__tests__/prepare.test.ts`, plus a `postReleaseBump` test in `publish-main.test.ts`. Existing gem, hex and nuget bump tests still pass with the shared patterns. New `versionBump.test.ts` cases cover the pypi edit staying in `[project]`, pubspec comments surviving, and a pubspec without a version still getting one. - `pnpm typecheck`, `pnpm lint` and `prettier --check .` are clean. - Merged with `master` (3.7.1). `pnpm test`: 1883 passed. 19 failed, all in `prepare-dry-run.e2e` plus one `git.test.ts` case, the same tests that fail on unmodified `master` in my sandbox. They fail because the sandbox rejects test-repo commits with custom git identities. The PR's checks so far are CodeQL, Socket and Semgrep, with no test job, so those tests have only run locally. <!-- junior-request-attribution:start --> via @BYK. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:local%3Aweb%3A62122f49891ab1ec203dce05 --> -- [View Session in Sentry](https://sentry.sentry.io/explore/conversations/local%3Aweb%3A62122f49891ab1ec203dce05/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: BYK <126780+BYK@users.noreply.github.com>
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #949.
Many repos use
postReleaseCommandfor one job: write the next development version, commit, pull with rebase, and push. In almost every case the script just re-runs the repo's own pre-release bump with a dev version. This PR builds that into Craft as an opt-in setting:Behavior
bumppicks the next version: a semverpatch/minor/majorincrement, orcalver, the first version of the next CalVer period underversioning.calver.format(e.g.26.10.1→26.11.0,26.12.3→27.1.0).calverwhenversioning.policyiscalver, otherwiseminor.versionis a template, rendered withrenderTemplateSafe({{version}}= next version). The default{{version}}-dev.0matchesnpm version preminor --preid=dev. A value with no placeholder, such asnightly, is used as is.runPreReleaseCommand, the stepcraft prepareuses: thepreReleaseCommandwhen set (withCRAFT_NEW_VERSIONset to the dev version), otherwise the automatic target bumps in the selected workspace. It then commitsmeta: Bump new development version+#skip-changelogwith--all, runsgit pull --rebase(aborting the rebase if that fails), and pushes to the merge target.runPreReleaseCommandmoves fromcommands/prepare.tstoutils/preReleaseCommand.ts, sopublishdoesn't importprepare, which already importspublish.pullWithRebasehelper.noMerge, a partial-target publish, or a merge or push failure. ABranchCleanupErrorstill counts as merged.trueand the bump-type shorthand normalize to the object form, sopublishonly handles one shape. The dev marker should stay a pre-release (-dev.0):+devbuild metadata is ignored when ordering versions, dropped by npm, and rejected by PyPI.postReleaseBump: false.postReleaseCommand.Who can switch
A survey of getsentry repos found these post-release scripts. Each one re-runs the repo's bump script with a dev version and then commits and pushes:
postReleaseBump: true→ #951postReleaseBump: { version: '{{version}}.dev0' }→26.11.0.dev0after26.10.1, same as todaypostReleaseBump: { version: nightly }postReleaseBump: true(2.5.0-dev.0, vs.-devfrom its current script). That script doesn't seem to run:mainis still2.4.0after the 2.4.0 release, and it calls'pwsh.\scripts\bump-version.ps1'.postReleaseBump: truere-runsbump-version.tsin pre mode. Its frontmatter sync only happens in--postmode today, so it either needs a small script change or #948Migration PRs follow once this ships in 3.7.0.
Checks
tsc --noEmit,pnpm lint, andprettier --checkall pass.vitest run src/commands/__tests__ src/__tests__/config.test.ts src/utils/__tests__/calver.test.ts: 235 passed. That covers:preReleaseCommandthrough; commit/pull/push; and every skip and failure path.getNextCalVerPeriod: month and day formats, year rollover, and versions that don't match.true,false, the object form, the bump-type shorthand and how each normalizes, and invalid values such aspreminor.master. The causes are sandbox-specific: a forced Git identity breaks commits in tests, npm env vars leak in, and the defaultpnpmis v12. CI is the source of truth.via @BYK.
--
View Session in Sentry