Skip to content

Commit

Permalink
Minimize semver logic + tests (#331)
Browse files Browse the repository at this point in the history
* Minimize semver logic + tests

* Add docs
  • Loading branch information
JAForbes authored May 13, 2022
1 parent 50c6691 commit c373104
Show file tree
Hide file tree
Showing 8 changed files with 848 additions and 15 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Test

on:
push:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
- run: npm ci
- run: npm test
10 changes: 10 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@
],
"args": ["actions-yml"],
"program": "${workspaceFolder}/bin.js"
},
{
"type": "pwa-node",
"request": "launch",
"name": "test",
"skipFiles": [
"<node_internals>/**"
],
"args": [],
"program": "${workspaceFolder}/test/test.js"
}
]
}
10 changes: 10 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ The semver increment level defaults to `patch` but can be set to `major`, `minor

**pr-release** infers the current version by checking multiple sources (package.json, release PR header, git tag, prior release) and always takes the highest found version as the authoritative one. So if you want to override the generated version, just manually update the release PR title to be a normal non-semver prerelease and merge. From then on, versions will not be prerelease incremented.

### The auto generated versions looks weird...

By default pr-release tries to capture the full scope/risk of a release by including every patch/minor/major increment. This is especially useful for versioning applications and gauging the scope/risk of a release at a glance.

For libraries you may want to use the `--minimize-semver-changes` option which makes the minimum necessary change to the semver to still satisfy semver range comparisons.

For example, if the previous version was `2.0.4` and the release candidate has 6 patches, 3 minors and 0 majors. The next version would be calculated as `2.1.0` as there was at least 1 minor. 1 minor constitutes a minor version update and no other version information is required, that is why the patch section is zeroed out. Additionally a minor jump from `2.1.0` or `2.6.0` is still a minor update, so it is not necessary to include the amount of minor jumps.

With the default settings the version would be calculated as `2.6.7` as it is a simple addition of semver increment levels to the base version.

### How do I configure my Github actions to use `pr-release`?

Run `npx pr-release actions-yml`. It will generate all the yml files you need to use all the features of `pr-release`.
Expand Down
79 changes: 66 additions & 13 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,17 +207,33 @@ async function updateDocs(){
await fs.writeFile('README.md', newReadme, 'utf8')
}

function getNextVersion({ recentBranches, version }){
export function getNextVersion({
recentBranches, version, minimizeSemverChange=false
}){
let severityIdx = groupBy( x => prSeverity(x), recentBranches )

// todo-james
let noPrereleaseBase; {
const parsed = semver.parse(version)
parsed.prerelease = []
noPrereleaseBase = parsed.format()
}

let nextVersion; {
nextVersion = semver.clean(version)
nextVersion = semver.clean(noPrereleaseBase)

for(let level of ['major', 'minor', 'patch', 'prerelease']){

let levelOrder = ['major', 'minor', 'patch', 'prerelease']
if( minimizeSemverChange ) {
levelOrder = ['patch', 'minor', 'major', 'prerelease']
}
outer: for(let level of levelOrder){
let n = severityIdx[level] ? severityIdx[level].length : 0
for( let i = 0; i < n; i++ ) {
nextVersion = semver.inc(nextVersion, level)

if ( minimizeSemverChange ) {
continue outer;
}
}
}

Expand All @@ -237,7 +253,7 @@ function getNextVersion({ recentBranches, version }){
return nextVersion
}

async function inferVersionExternal(){
async function inferVersionExternal({ minimizeSemverChange=false }={}){
let [owner,repo] = process.env.GITHUB_REPOSITORY.split('/')

const lastRelease = await getLastRelease({ owner, repo })
Expand All @@ -248,7 +264,9 @@ async function inferVersionExternal(){

const version =
await getNextVersion({
recentBranches, version: currentVersion.version
recentBranches
, version: currentVersion.version
, minimizeSemverChange
})

console.log('v'+version)
Expand Down Expand Up @@ -372,7 +390,11 @@ async function extractChangelog(options){
// this version is the published version + branch inferred versions
// it ignores the version set in the PR description
let nextVersion =
await getNextVersion({ recentBranches, version })
await getNextVersion({
recentBranches
, version
, minimizeSemverChange: options.minimizeSemverChange
})

let contributors =
[...new Set(recentBranches.map( x => x.user.login))]
Expand Down Expand Up @@ -589,10 +611,10 @@ async function merge(options){
// There may not be one, and if there is, there may already be a tag for that release

const changelogOut = typeof options.changelog == 'string' ? { out: options.changelog } : {}

const { minimizeSemverChange } = options
let out = options.changelog
? await externalChangelog({ ...changelogOut })
: await extractChangelog({})
? await externalChangelog({ ...changelogOut, minimizeSemverChange })
: await extractChangelog({ minimizeSemverChange })

let { nextVersion } = out
let [owner,repo] = process.env.GITHUB_REPOSITORY.split('/')
Expand Down Expand Up @@ -1040,7 +1062,11 @@ async function pr(options){

let severityIdx = groupBy( x => prSeverity(x), recentBranches )

let nextVersion = getNextVersion({ recentBranches, version })
let nextVersion = getNextVersion({
recentBranches
, version
, minimizeSemverChange: options.minimizeSemverChange
})

let defaultTitle = `Release - v${nextVersion}`

Expand Down Expand Up @@ -1658,6 +1684,7 @@ async function inferPreReleaseExternal({
, 'package-path': packagePath='package.json'
, publish=false
, apply=publish
, minimizeSemverChange
}={}){

let [owner,repo] = process.env.GITHUB_REPOSITORY.split('/')
Expand Down Expand Up @@ -1693,9 +1720,13 @@ async function inferPreReleaseExternal({
await inferVersion({ owner, repo, lastRelease })

let nextVersion =
await getNextVersion({ recentBranches: openBranches, version })
await getNextVersion({
recentBranches: openBranches
, version
, minimizeSemverChange
})


// todo-james move into getNextVersion
if(!semver.prerelease(nextVersion)){
x = nextVersion+'-'+preid+'.0'
} else {
Expand Down Expand Up @@ -1761,6 +1792,11 @@ subcommands:
Do not thank contributors in changelog or release PR
${blue(`--minimize-semver-changes`)}
Make the minimum change to the semver instead of including an increment
per pr merged.
${magenta(`merge`)}
Commits updated changelog and creates new npm/github/etc release.
Expand All @@ -1784,6 +1820,11 @@ subcommands:
${magenta('filepath')} defaults to changelog.md
${blue(`--minimize-semver-changes`)}
Make the minimum change to the semver instead of including an increment
per pr merged.
${magenta(`actions-yml`)}
Scaffold Github actions yml files
Expand Down Expand Up @@ -1830,6 +1871,11 @@ subcommands:
This is the same function used to generate the versions in the title
header and body but as a standalone command.
${blue(`--minimize-semver-changes`)}
Make the minimum change to the semver instead of including an increment
per pr merged.
${red(`Advanced Commands`)}
${red(`-----------------`)}
Expand Down Expand Up @@ -1876,6 +1922,12 @@ ${magenta(`infer-prerelease`)}
${blue(`--publish`)} will publish to the same release channel as preid (defaults to next)
${blue(`--publish=alpha`)} will publish to the specified channel, in this case alpha
${blue(`--minimize-semver-changes`)}
Make the minimum change to the semver instead of including an increment
per pr merged.
`

let [subcommand] = argv._
Expand Down Expand Up @@ -1928,6 +1980,7 @@ export async function main(){
if( preflights[subcommand] ) await preflight(argv._, argv)

argv = { contributors: true, thanks: true, ...argv }
argv = { 'minimize-semver-change': argv.minimizeSemverChange, ...argv }

await f(argv, ...argv._)
} catch (e) {
Expand Down
Loading

0 comments on commit c373104

Please sign in to comment.