Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(terra-draw): improve bump script to be more accurate in the recommended version #465

Merged
merged 1 commit into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 47 additions & 32 deletions bump.mjs
Original file line number Diff line number Diff line change
@@ -1,59 +1,74 @@
/**
* We have to manually figure out how to bump the package version based on the commits
* as commit-and-tag-version doesn't seem to work with monorepos where scopes are used to
* differentiate packages. We build on the conventional-recommended-bump package to determine
* differentiate packages. We build on the @conventional-changelog/git-client package to determine
* the bump type based on the commits since the last release for the current package.
*/

import * as path from 'path';
import { Bumper } from 'conventional-recommended-bump';
import { readFileSync } from "fs";
import { ConventionalGitClient } from '@conventional-changelog/git-client'

const gitPath = path.resolve('./');
const bumper = new Bumper(gitPath);
const packageName = JSON.parse(readFileSync('./package.json', 'utf8')).name;
await getBumpType(bumper, packageName);
const packageJson = JSON.parse(readFileSync('./package.json', 'utf8'))
const currentVersion = packageJson.version
const packageName = packageJson.name;
await getBumpType(packageName);


async function getBumpType(bumper, packageName) {
async function getBumpType(packageName) {

const PATCH = 0;
const MINOR = 1;
const MAJOR = 2;

await bumper.loadPreset('conventionalcommits')
const gitClient = new ConventionalGitClient(gitPath);

const recommendation = await bumper.bump(
(commits) => {
let recommendation = PATCH;
const commits = gitClient.getCommits({
format: '%B%n-hash-%n%H',
from: `${packageName}@${currentVersion}`,
})

// The last commit is the one that bumped the version for the current package
// It appears it is included in the list of commits, so we need to exclude it
const allCommitsAfterLastRelease = commits.slice(0, -1)
let recommendation = PATCH;

for (let commit of allCommitsAfterLastRelease) {
if (commit.scope === packageName) {
if (recommendation < MAJOR && commit.type === 'feat') {
recommendation = MINOR;
}
for await (const commit of commits) {

if (commit.notes.some((note) => note.title === 'BREAKING CHANGE')) {
recommendation = MAJOR;
}
}
}
// ConventionalGitClient does not seem to populate the scope for breaking changes with a !
if (!commit.scope) {
const hasScope = commit.header.includes(`(${packageName})!:`)

return {
level: recommendation,
reason: 'Based on conventional commits',
releaseType: ['patch', 'minor', 'major'][recommendation]
if (hasScope) {
commit.scope = packageName;
}
}

)
if (commit.scope === packageName) {

// If the commit type is a fix, we recommend a patch release:
// From https://www.conventionalcommits.org/en/v1.0.0:
// "fix: a commit of the type fix patches a bug in your codebase (this correlates with PATCH in Semantic Versioning)."
if (recommendation < MAJOR && commit.type === 'feat') {
recommendation = MINOR;
}

// There are two ways to trigger a major release either by having a footer
// with BREAKING CHANGE or by having a ! in the header after the scope
// From https://www.conventionalcommits.org/en/v1.0.0:
// "BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a ! after the type/scope, introduces a breaking API change (correlating with MAJOR in Semantic Versioning). A BREAKING CHANGE can be part of commits of any type."
const majorBecauseHeaderHasBang = commit.header.includes(`(${packageName})!:`)
const majorBecauseFooterHasBreaking = commit.footer?.includes('BREAKING CHANGE')

if (majorBecauseHeaderHasBang || majorBecauseFooterHasBreaking) {
recommendation = MAJOR;

// We can break out of the loop as we have found a major release and the release type will not change
// for the rest of the commits once a major release is recommended
break;
}
}
}

console.log(recommendation.releaseType)
const releaseType = ['patch', 'minor', 'major'][recommendation]

// TODO: Process doesn't seem to exit - probably something async still running
process.exit(0)
// This is the logged out to be used as the release type for the package
console.log(releaseType)
}
3 changes: 3 additions & 0 deletions guides/7.DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ In conventional commits, you can use the `!` to indicate a breaking change like

This will increment the major version on release, i.e. v1.0.0 -> v2.0.0. `feat` will trigger a minor release update, i.e. v1.0.0 -> v1.1.0 and `fix` will trigger a patch, i.e. v1.0.0 -> v1.0.1.

>[!NOTE]
> The library used to write the CHANGELOG and bump the appropriate package on release, `commit-and-tag-version` is not able to correctly recommend the correct version when bumping the package. Instead we work around this using the `bump.mjs` script which accurately determines what the new package version should be, based on the conventional commit specification. It then passes this `commit-and-tag-version` via the `--release-as` flag which allows you to specifically set the release version.

## Technologies Used

- [TypeScript](https://www.typescriptlang.org/) - provides strong compile time typing for JavaScript
Expand Down
Loading
Loading