From f56059e216076b59a4f28fe4e8e2a7a8f609cdea Mon Sep 17 00:00:00 2001 From: reggi Date: Wed, 11 Dec 2024 11:42:50 -0500 Subject: [PATCH 1/4] fix: prereleases --- lib/release/release-please.js | 7 +- lib/release/semver-versioning-strategy.js | 66 ++++++++++++++++ test/release/version.js | 96 +++++++++++++++++++++++ 3 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 lib/release/semver-versioning-strategy.js create mode 100644 test/release/version.js diff --git a/lib/release/release-please.js b/lib/release/release-please.js index d4b515da..95a7e270 100644 --- a/lib/release/release-please.js +++ b/lib/release/release-please.js @@ -1,6 +1,4 @@ const RP = require('release-please') -const { DefaultVersioningStrategy } = require('release-please/build/src/versioning-strategies/default.js') -const { PrereleaseVersioningStrategy } = require('release-please/build/src/versioning-strategies/prerelease.js') const { ROOT_PROJECT_PATH } = require('release-please/build/src/manifest.js') const { CheckpointLogger, logger } = require('release-please/build/src/util/logger.js') const assert = require('assert') @@ -9,6 +7,7 @@ const omit = require('just-omit') const ChangelogNotes = require('./changelog.js') const NodeWorkspaceFormat = require('./node-workspace-format.js') const { getPublishTag, noop } = require('./util.js') +const { SemverVersioningStrategy } = require('./semver-versioning-strategy.js') /* istanbul ignore next: TODO fix flaky tests and enable coverage */ class ReleasePlease { @@ -52,9 +51,7 @@ class ReleasePlease { async init() { RP.registerChangelogNotes('default', ({ github, ...o }) => new ChangelogNotes(github, o)) - RP.registerVersioningStrategy('default', o => - o.prerelease ? new PrereleaseVersioningStrategy(o) : new DefaultVersioningStrategy(o), - ) + RP.registerVersioningStrategy('default', o => new SemverVersioningStrategy(o)) RP.registerPlugin( 'node-workspace-format', ({ github, targetBranch, repositoryConfig, ...o }) => diff --git a/lib/release/semver-versioning-strategy.js b/lib/release/semver-versioning-strategy.js new file mode 100644 index 00000000..0bae00f9 --- /dev/null +++ b/lib/release/semver-versioning-strategy.js @@ -0,0 +1,66 @@ +const semver = require('semver'); +const { Version } = require('release-please/build/src/version.js'); + +const inc = (version, release, _preid) => { + const parsed = new semver.SemVer(version); + const implicitPreid = + parsed.prerelease.length > 1 ? parsed.prerelease[0]?.toString() : undefined; + const preid = _preid || implicitPreid; + const next = new semver.SemVer(version).inc(release, preid); + const isFreshMajor = parsed.minor === 0 && parsed.patch === 0; + const isFreshMinor = parsed.patch === 0; + if ( + parsed.prerelease.length && + next.prerelease.length && + ((release === 'premajor' && isFreshMajor) || + (release === 'preminor' && isFreshMinor) || + release === 'prepatch') + ) { + return semver.inc(version, 'prerelease', preid); + } + return semver.inc(version, release, preid); +}; + +const parseCommits = (commits) => { + let release = 'patch'; + for (const commit of commits) { + if (commit.breaking) { + release = 'major'; + break; + } else if (['feat', 'feature'].includes(commit.type)) { + release = 'minor'; + } + } + return release; +}; + +class SemverVersioningStrategy { + constructor(options) { + this.options = options; + } + + determineReleaseType(_version, _commits) { + const options = this.options; + class Shell { + bump() { + return new SemverVersioningStrategy(options).bump(_version, _commits); + } + } + return new Shell(); + } + + bump(currentVersion, commits) { + const prerelease = this.options.prerelease; + const tag = this.options.prereleaseType; + const releaseType = parseCommits(commits); + const addPreIfNeeded = prerelease ? `pre${releaseType}` : releaseType; + const version = inc(currentVersion.toString(), addPreIfNeeded, tag); + /* istanbul ignore next */ + if (!version) { + throw new Error('Could not bump version'); + } + return Version.parse(version); + } +} + +module.exports = { SemverVersioningStrategy }; diff --git a/test/release/version.js b/test/release/version.js new file mode 100644 index 00000000..880cd6db --- /dev/null +++ b/test/release/version.js @@ -0,0 +1,96 @@ +const t = require('tap'); +const { Version } = require('release-please/build/src/version.js'); +const { SemverVersioningStrategy } = require('../../lib/release/semver-versioning-strategy'); + +const commit = { + type: 'chore', + breaking: false, + notes: [], + references: [], + scope: '', + bareMessage: '', + sha: '', + message: '', +}; + +const g = (v) => ({ ...commit, ...v }); + +const COMMITS = { + major: [{ type: 'feat' }, {}, {}, { breaking: true }].map(g), + minor: [{}, {}, { type: 'feat' }].map(g), + patch: [{}, { type: 'chore' }, { type: 'fix' }].map(g), +}; + +const throws = "THROWS" + +const checks = [ + // Normal releases + ['2.0.0', 'major', false, undefined, '3.0.0'], + ['2.0.0', 'minor', false, undefined, '2.1.0'], + ['2.0.0', 'patch', false, undefined, '2.0.1'], + // premajor -> normal + ['2.0.0-pre.1', 'major', false, undefined, '2.0.0'], + ['2.0.0-pre.5', 'minor', false, undefined, '2.0.0'], + ['2.0.0-pre.4', 'patch', false, undefined, '2.0.0'], + // preminor -> normal + ['2.1.0-pre.1', 'major', false, undefined, '3.0.0'], + ['2.1.0-pre.5', 'minor', false, undefined, '2.1.0'], + ['2.1.0-pre.4', 'patch', false, undefined, '2.1.0'], + // prepatch -> normal + ['2.0.1-pre.1', 'major', false, undefined, '3.0.0'], + ['2.0.1-pre.5', 'minor', false, undefined, '2.1.0'], + ['2.0.1-pre.4', 'patch', false, undefined, '2.0.1'], + // Prereleases + ['2.0.0', 'major', true, 'pre', '3.0.0-pre.0'], + ['2.0.0', 'minor', true, 'pre', '2.1.0-pre.0'], + ['2.0.0', 'patch', true, 'pre', '2.0.1-pre.0'], + // premajor - prereleases + ['2.0.0-pre.1', 'major', true, undefined, '2.0.0-pre.2'], + ['2.0.0-pre.1', 'minor', true, undefined, '2.0.0-pre.2'], + ['2.0.0-pre.1', 'patch', true, undefined, '2.0.0-pre.2'], + // preminor - prereleases + ['2.1.0-pre.1', 'major', true, undefined, '3.0.0-pre.0'], + ['2.1.0-pre.1', 'minor', true, undefined, '2.1.0-pre.2'], + ['2.1.0-pre.1', 'patch', true, undefined, '2.1.0-pre.2'], + // prepatch - prereleases + ['2.0.1-pre.1', 'major', true, undefined, '3.0.0-pre.0'], + ['2.0.1-pre.1', 'minor', true, undefined, '2.1.0-pre.0'], + ['2.0.1-pre.1', 'patch', true, undefined, '2.0.1-pre.2'], + // different prerelease identifiers + ['2.0.0-beta.1', 'major', true, undefined, '2.0.0-beta.2'], + ['2.0.0-alpha.1', 'major', true, undefined, '2.0.0-alpha.2'], + ['2.0.0-rc.1', 'major', true, undefined, '2.0.0-rc.2'], + ['2.0.0-0', 'major', true, undefined, '2.0.0-1'], + // leaves prerelease + ['2.0.0-beta.1', 'major', false, undefined, '2.0.0'], + ['2.0.0-alpha.1', 'major', false, undefined, '2.0.0'], + ['2.0.0-rc.1', 'major', false, undefined, '2.0.0'], + ['2.0.0-0', 'major', false, undefined, '2.0.0'], + ['xxxx', 'major', false, undefined, throws], +]; + +t.test('SemverVersioningStrategy', async (t) => { + for (const [version, commits, prerelease, prereleaseType, expected] of checks) { + const name = [version, commits, prerelease, prereleaseType, expected]; + const id = name.map((v) => (typeof v === 'undefined' ? 'undefined' : v)).join(','); + const instance = new SemverVersioningStrategy({ prerelease, prereleaseType }) + + if (expected === throws) { + t.throws(() => instance.bump(Version.parse(version), COMMITS[commits]), id); + continue; + } + + const bump = instance.bump( + Version.parse(version), + COMMITS[commits] + ); + + const determine = instance.determineReleaseType( + Version.parse(version), + COMMITS[commits] + ); + + t.equal(bump.toString(), expected, id); + t.equal(determine.bump().toString(), expected, id); + } +}); From 4dc08d61bc2698f0039251fa4447f75e82d8f372 Mon Sep 17 00:00:00 2001 From: reggi Date: Wed, 11 Dec 2024 12:17:49 -0500 Subject: [PATCH 2/4] chore: fix linitng --- lib/release/semver-versioning-strategy.js | 67 +++++++++++------------ test/release/version.js | 44 +++++++-------- 2 files changed, 51 insertions(+), 60 deletions(-) diff --git a/lib/release/semver-versioning-strategy.js b/lib/release/semver-versioning-strategy.js index 0bae00f9..ae6e4498 100644 --- a/lib/release/semver-versioning-strategy.js +++ b/lib/release/semver-versioning-strategy.js @@ -1,66 +1,63 @@ -const semver = require('semver'); -const { Version } = require('release-please/build/src/version.js'); +const semver = require('semver') +const { Version } = require('release-please/build/src/version.js') const inc = (version, release, _preid) => { - const parsed = new semver.SemVer(version); - const implicitPreid = - parsed.prerelease.length > 1 ? parsed.prerelease[0]?.toString() : undefined; - const preid = _preid || implicitPreid; - const next = new semver.SemVer(version).inc(release, preid); - const isFreshMajor = parsed.minor === 0 && parsed.patch === 0; - const isFreshMinor = parsed.patch === 0; + const parsed = new semver.SemVer(version) + const implicitPreid = parsed.prerelease.length > 1 ? parsed.prerelease[0]?.toString() : undefined + const preid = _preid || implicitPreid + const next = new semver.SemVer(version).inc(release, preid) + const isFreshMajor = parsed.minor === 0 && parsed.patch === 0 + const isFreshMinor = parsed.patch === 0 if ( parsed.prerelease.length && next.prerelease.length && - ((release === 'premajor' && isFreshMajor) || - (release === 'preminor' && isFreshMinor) || - release === 'prepatch') + ((release === 'premajor' && isFreshMajor) || (release === 'preminor' && isFreshMinor) || release === 'prepatch') ) { - return semver.inc(version, 'prerelease', preid); + return semver.inc(version, 'prerelease', preid) } - return semver.inc(version, release, preid); -}; + return semver.inc(version, release, preid) +} -const parseCommits = (commits) => { - let release = 'patch'; +const parseCommits = commits => { + let release = 'patch' for (const commit of commits) { if (commit.breaking) { - release = 'major'; - break; + release = 'major' + break } else if (['feat', 'feature'].includes(commit.type)) { - release = 'minor'; + release = 'minor' } } - return release; -}; + return release +} class SemverVersioningStrategy { constructor(options) { - this.options = options; + this.options = options } determineReleaseType(_version, _commits) { - const options = this.options; + const options = this.options class Shell { bump() { - return new SemverVersioningStrategy(options).bump(_version, _commits); + return new SemverVersioningStrategy(options).bump(_version, _commits) } } - return new Shell(); + return new Shell() } bump(currentVersion, commits) { - const prerelease = this.options.prerelease; - const tag = this.options.prereleaseType; - const releaseType = parseCommits(commits); - const addPreIfNeeded = prerelease ? `pre${releaseType}` : releaseType; - const version = inc(currentVersion.toString(), addPreIfNeeded, tag); + const prerelease = this.options.prerelease + const tag = this.options.prereleaseType + const releaseType = parseCommits(commits) + const addPreIfNeeded = prerelease ? `pre${releaseType}` : releaseType + const version = inc(currentVersion.toString(), addPreIfNeeded, tag) /* istanbul ignore next */ - if (!version) { - throw new Error('Could not bump version'); + if (!version) { + throw new Error('Could not bump version') } - return Version.parse(version); + return Version.parse(version) } } -module.exports = { SemverVersioningStrategy }; +module.exports = { SemverVersioningStrategy } diff --git a/test/release/version.js b/test/release/version.js index 880cd6db..8c4d42e9 100644 --- a/test/release/version.js +++ b/test/release/version.js @@ -1,6 +1,6 @@ -const t = require('tap'); -const { Version } = require('release-please/build/src/version.js'); -const { SemverVersioningStrategy } = require('../../lib/release/semver-versioning-strategy'); +const t = require('tap') +const { Version } = require('release-please/build/src/version.js') +const { SemverVersioningStrategy } = require('../../lib/release/semver-versioning-strategy') const commit = { type: 'chore', @@ -11,17 +11,17 @@ const commit = { bareMessage: '', sha: '', message: '', -}; +} -const g = (v) => ({ ...commit, ...v }); +const g = v => ({ ...commit, ...v }) const COMMITS = { major: [{ type: 'feat' }, {}, {}, { breaking: true }].map(g), minor: [{}, {}, { type: 'feat' }].map(g), patch: [{}, { type: 'chore' }, { type: 'fix' }].map(g), -}; +} -const throws = "THROWS" +const throws = 'THROWS' const checks = [ // Normal releases @@ -67,30 +67,24 @@ const checks = [ ['2.0.0-rc.1', 'major', false, undefined, '2.0.0'], ['2.0.0-0', 'major', false, undefined, '2.0.0'], ['xxxx', 'major', false, undefined, throws], -]; +] -t.test('SemverVersioningStrategy', async (t) => { +t.test('SemverVersioningStrategy', async t => { for (const [version, commits, prerelease, prereleaseType, expected] of checks) { - const name = [version, commits, prerelease, prereleaseType, expected]; - const id = name.map((v) => (typeof v === 'undefined' ? 'undefined' : v)).join(','); + const name = [version, commits, prerelease, prereleaseType, expected] + const id = name.map(v => (typeof v === 'undefined' ? 'undefined' : v)).join(',') const instance = new SemverVersioningStrategy({ prerelease, prereleaseType }) - + if (expected === throws) { - t.throws(() => instance.bump(Version.parse(version), COMMITS[commits]), id); - continue; + t.throws(() => instance.bump(Version.parse(version), COMMITS[commits]), id) + continue } - const bump = instance.bump( - Version.parse(version), - COMMITS[commits] - ); + const bump = instance.bump(Version.parse(version), COMMITS[commits]) - const determine = instance.determineReleaseType( - Version.parse(version), - COMMITS[commits] - ); + const determine = instance.determineReleaseType(Version.parse(version), COMMITS[commits]) - t.equal(bump.toString(), expected, id); - t.equal(determine.bump().toString(), expected, id); + t.equal(bump.toString(), expected, id) + t.equal(determine.bump().toString(), expected, id) } -}); +}) From 832baaa09e459b6c150b67444974aa5083442a07 Mon Sep 17 00:00:00 2001 From: reggi Date: Wed, 11 Dec 2024 13:21:15 -0500 Subject: [PATCH 3/4] chore: return early --- lib/release/semver-versioning-strategy.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/release/semver-versioning-strategy.js b/lib/release/semver-versioning-strategy.js index ae6e4498..b02f8322 100644 --- a/lib/release/semver-versioning-strategy.js +++ b/lib/release/semver-versioning-strategy.js @@ -6,16 +6,16 @@ const inc = (version, release, _preid) => { const implicitPreid = parsed.prerelease.length > 1 ? parsed.prerelease[0]?.toString() : undefined const preid = _preid || implicitPreid const next = new semver.SemVer(version).inc(release, preid) + if (!parsed.prerelease.length) { + return next.format() + } const isFreshMajor = parsed.minor === 0 && parsed.patch === 0 const isFreshMinor = parsed.patch === 0 - if ( - parsed.prerelease.length && - next.prerelease.length && - ((release === 'premajor' && isFreshMajor) || (release === 'preminor' && isFreshMinor) || release === 'prepatch') - ) { + const shouldPrerelease = (release === 'premajor' && isFreshMajor) || (release === 'preminor' && isFreshMinor) || release === 'prepatch' + if (shouldPrerelease) { return semver.inc(version, 'prerelease', preid) } - return semver.inc(version, release, preid) + return next.format() } const parseCommits = commits => { From d0163e7a4cc4c8f46a6c741630f43b7c2fc2f4c8 Mon Sep 17 00:00:00 2001 From: reggi Date: Wed, 11 Dec 2024 13:42:52 -0500 Subject: [PATCH 4/4] chore: lint and improve nested class --- lib/release/semver-versioning-strategy.js | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/release/semver-versioning-strategy.js b/lib/release/semver-versioning-strategy.js index b02f8322..6844deb3 100644 --- a/lib/release/semver-versioning-strategy.js +++ b/lib/release/semver-versioning-strategy.js @@ -11,7 +11,8 @@ const inc = (version, release, _preid) => { } const isFreshMajor = parsed.minor === 0 && parsed.patch === 0 const isFreshMinor = parsed.patch === 0 - const shouldPrerelease = (release === 'premajor' && isFreshMajor) || (release === 'preminor' && isFreshMinor) || release === 'prepatch' + const shouldPrerelease = + (release === 'premajor' && isFreshMajor) || (release === 'preminor' && isFreshMinor) || release === 'prepatch' if (shouldPrerelease) { return semver.inc(version, 'prerelease', preid) } @@ -31,19 +32,25 @@ const parseCommits = commits => { return release } +class SemverVersioningStrategyNested { + constructor(options, version, commits) { + this.options = options + this.commits = commits + this.version = version + } + + bump() { + return new SemverVersioningStrategy(this.options).bump(this.version, this.commits) + } +} + class SemverVersioningStrategy { constructor(options) { this.options = options } - determineReleaseType(_version, _commits) { - const options = this.options - class Shell { - bump() { - return new SemverVersioningStrategy(options).bump(_version, _commits) - } - } - return new Shell() + determineReleaseType(version, commits) { + return new SemverVersioningStrategyNested(this.options, version, commits) } bump(currentVersion, commits) {