diff --git a/docs/releases/README.md b/docs/releases/README.md index 2f96b4b0..437fec3f 100644 --- a/docs/releases/README.md +++ b/docs/releases/README.md @@ -1,6 +1,6 @@ # Releasing NitroSQLite -Every push to `main` keeps a Release Please PR open with the next version and changelog. Conventional commits since the last release choose the version. `fix` and `perf` commits make a patch release, `feat` commits make a minor release, and breaking changes (`!` or a `BREAKING CHANGE:` footer) make a major release. The workflow also commits the regenerated workspace and CocoaPods lockfiles to that PR. Merging the PR creates the draft release and tag, then runs the release checks and publishes as described below. +Every push to `main` keeps a Release Please PR open with the next version and changelog. Conventional commits since the last release choose the version. `fix` and `perf` commits make a patch release. `feat` commits and breaking changes (`!` or a `BREAKING CHANGE:` footer) make a minor release, and breaking changes still appear under a breaking changes heading in the changelog. To release a new major version, merge a commit to `main` whose body contains `Release-As: X.0.0`. The same footer sets any other exact version for the next release. The workflow also commits the regenerated workspace and CocoaPods lockfiles to that PR. Merging the PR creates the draft release and tag, then runs the release checks and publishes as described below. To choose the version yourself, release a maintenance branch, or release without waiting for a merge, start the workflow manually: diff --git a/release-please-config.json b/release-please-config.json index ba5f2ad5..9ced407b 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -3,6 +3,7 @@ "packages": { ".": { "release-type": "node", + "versioning": "breaking-as-minor", "include-component-in-tag": false, "draft": true, "force-tag-creation": true, diff --git a/scripts/merge-release.ts b/scripts/merge-release.ts index 960253b5..a0404cff 100644 --- a/scripts/merge-release.ts +++ b/scripts/merge-release.ts @@ -1,5 +1,6 @@ import { execFileSync } from 'node:child_process' import { GitHub, Manifest } from 'release-please' +import './release-versioning.ts' import { github, output, diff --git a/scripts/prepare-release.ts b/scripts/prepare-release.ts index 4ab53930..f8913fc3 100644 --- a/scripts/prepare-release.ts +++ b/scripts/prepare-release.ts @@ -1,4 +1,5 @@ import { GitHub, Manifest } from 'release-please' +import './release-versioning.ts' import { Version } from 'release-please/build/src/version.js' import { releasePlan, output } from './release-plan.ts' diff --git a/scripts/release-on-push.ts b/scripts/release-on-push.ts index 21678e03..8c2923da 100644 --- a/scripts/release-on-push.ts +++ b/scripts/release-on-push.ts @@ -1,4 +1,5 @@ import { GitHub, Manifest } from 'release-please' +import './release-versioning.ts' import { output } from './release-plan.ts' const { GH_TOKEN, GITHUB_REPOSITORY, GITHUB_REF_NAME } = process.env diff --git a/scripts/release-versioning.ts b/scripts/release-versioning.ts new file mode 100644 index 00000000..09833534 --- /dev/null +++ b/scripts/release-versioning.ts @@ -0,0 +1,31 @@ +import { registerVersioningStrategy } from 'release-please' +import type { ConventionalCommit } from 'release-please/build/src/commit.js' +import type { Version } from 'release-please/build/src/version.js' +import { + MajorVersionUpdate, + MinorVersionUpdate, + type VersionUpdater, +} from 'release-please/build/src/versioning-strategy.js' +import { DefaultVersioningStrategy } from 'release-please/build/src/versioning-strategies/default.js' + +/** Release Please versioning that proposes a minor release where conventional commits call for a major one. + * Features and breaking changes bump the minor version, other releasable commits bump the patch version, + * and a `Release-As: x.y.z` commit footer still sets an exact version, including a new major. + */ +export class BreakingAsMinorVersioningStrategy extends DefaultVersioningStrategy { + override determineReleaseType( + version: Version, + commits: ConventionalCommit[], + ): VersionUpdater { + const update = super.determineReleaseType(version, commits) + return update instanceof MajorVersionUpdate + ? new MinorVersionUpdate() + : update + } +} + +// Every script that loads release-please-config.json must import this module before reading the manifest. +registerVersioningStrategy( + 'breaking-as-minor', + (options) => new BreakingAsMinorVersioningStrategy(options), +) diff --git a/scripts/release.test.ts b/scripts/release.test.ts index ea8bd58c..bb5f8762 100644 --- a/scripts/release.test.ts +++ b/scripts/release.test.ts @@ -31,6 +31,7 @@ import { checkTrustedPublishers, verifyPublication, } from './release-registry.ts' +import { BreakingAsMinorVersioningStrategy } from './release-versioning.ts' test('patch/minor/major resolve from the selected maintenance branch', () => { fixture(() => { @@ -364,3 +365,35 @@ function fixture(run: (directory: string) => void): void { function git(...args: string[]): void { execFileSync('git', args, { stdio: 'pipe' }) } + +test('proposed releases bump minor for breaking changes and features, and patch for fixes', () => { + const config: { packages: Record } = + JSON.parse( + readFileSync( + new URL('../release-please-config.json', import.meta.url), + 'utf8', + ), + ) + assert.equal(config.packages['.']?.versioning, 'breaking-as-minor') + const strategy = new BreakingAsMinorVersioningStrategy() + const next = (...messages: string[]) => + strategy + .bump( + Version.parse('10.2.0'), + parseConventionalCommits( + messages.map((message, index) => ({ + message, + sha: String(index + 1).repeat(40), + })), + ), + ) + .toString() + assert.equal(next('fix: close statements'), '10.2.1') + assert.equal(next('feat: add executeRaw'), '10.3.0') + assert.equal(next('chore!: ship ESM only', 'fix: close statements'), '10.3.0') + assert.equal( + next('feat: add API\n\nBREAKING CHANGE: removes CommonJS'), + '10.3.0', + ) + assert.equal(next('chore: release 11\n\nRelease-As: 11.0.0'), '11.0.0') +})