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

feat: add understanding of more commit types #112

Merged
merged 4 commits into from
Oct 2, 2023
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
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

## Usage

This CLI script determines whether a semantic release should occur for a package based on Git history.
Specifically, it returns truthy only if a commit whose type _isn't_ `chore` or `docs` has come since the most recent release commit.
This function determines whether a semantic release should occur for a package based on Git history.
It returns true if a "meaningful" commit has come since the most recent release commit.

```shell
if npx should-semantic-release ; then npx release-it ; fi
Expand All @@ -53,6 +53,16 @@ Checking commit: chore: release v1.27.31
This is a release commit. Returning false.
```

### Commit Purposes

Based on a commit's conventional commit message type:

1. If the type is `feat` `fix`, or `perf`, it's considered "meaningful"
2. If the type is `docs`, `refactor`, `style`, or `test`, it's ignored
3. If the message looks like `v1.2.3`, `chore: release 1.2.3`, or similar, it's considered a "release"

See [`getCommitMeaning`](./src/getCommitMeaning.ts) for the exact logic used.

### Node API

Alternately, you can call this import asynchronous `shouldSemanticRelease` function into Node scripts:
Expand Down
46 changes: 46 additions & 0 deletions src/getCommitMeaning.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, expect, it } from "vitest";

import { getCommitMeaning } from "./getCommitMeaning.js";

describe("getCommitMeaning", () => {
it.each([
["chore", { type: undefined }],
["chore: deps", { type: "chore" }],
["chore(deps): bump", { type: "chore" }],
["docs", { type: undefined }],
["docs: bump", { type: "docs" }],
["docs: message", { type: "docs" }],
["feat", { type: undefined }],
["feat: bump", "meaningful"],
["feat: bump version to 1.2.3", "meaningful"],
["feat: message", "meaningful"],
["fix", { type: undefined }],
["fix: bump", "meaningful"],
["fix: bump version to 1.2.3", "meaningful"],
["fix: message", "meaningful"],
["perf", { type: undefined }],
["perf: bump", "meaningful"],
["perf: bump version to 1.2.3", "meaningful"],
["perf: message", "meaningful"],
["style", { type: undefined }],
["style: bump", { type: "style" }],
["style: bump version to 1.2.3", { type: "style" }],
["style: message", { type: "style" }],
["0.0.0", "release"],
["v0.0.0", "release"],
["1.2.3", "release"],
["v1.2.3", "release"],
["1.23.456", "release"],
["v1.23.456", "release"],
["12.345.6789", "release"],
["v12.345.6789", "release"],
["chore: release", "release"],
["chore: release 1.2.3", "release"],
["chore: release v1.2.3", "release"],
["chore(deps): release", "release"],
["chore(deps): release 1.2.3", "release"],
["chore(deps): release v1.2.3", "release"],
])("returns %j for %s", (input, expected) => {
expect(getCommitMeaning(input)).toEqual(expected);
});
});
28 changes: 28 additions & 0 deletions src/getCommitMeaning.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import conventionalCommitsParser from "conventional-commits-parser";

const alwaysMeaningfulTypes = new Set(["feat", "fix", "perf"]);

const alwaysIgnoredTypes = new Set(["docs", "refactor", "style", "test"]);

const releaseCommitTester =
/^(?:chore(?:\(.*\))?:?)?\s*release|v?\d+\.\d+\.\d+/;

export function getCommitMeaning(message: string) {
// Some types are always meaningful or ignored, regardless of potentially release-like messages
const { type } = conventionalCommitsParser.sync(message);
if (type) {
if (alwaysMeaningfulTypes.has(type)) {
return "meaningful";
}
if (alwaysIgnoredTypes.has(type)) {
return { type };
}
}

// If we've hit a release commit, we know we don't need to release
if (releaseCommitTester.test(message)) {
return "release";
}

return { type: type ?? undefined };
}
32 changes: 0 additions & 32 deletions src/isReleaseCommit.test.ts

This file was deleted.

5 changes: 0 additions & 5 deletions src/isReleaseCommit.ts

This file was deleted.

30 changes: 13 additions & 17 deletions src/shouldSemanticRelease.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import conventionalCommitsParser from "conventional-commits-parser";

import { isReleaseCommit } from "./isReleaseCommit.js";
import { getCommitMeaning } from "./getCommitMeaning.js";
import { ShouldSemanticReleaseOptions } from "./types.js";
import { execOrThrow } from "./utils.js";

const ignoredTypes = new Set(["chore", "docs"]);

export async function shouldSemanticRelease({
verbose,
}: ShouldSemanticReleaseOptions) {
Expand All @@ -21,20 +17,20 @@ export async function shouldSemanticRelease({

for (const message of history) {
log(`Checking commit: ${message}`);
// If we've hit a release commit, we know we don't need to release
if (isReleaseCommit(message)) {
log(`Found a release commit. Returning false.`);
return false;
}
const meaning = getCommitMeaning(message);

// Otherwise, we should release if a non-ignored commit type is found
const { type } = conventionalCommitsParser.sync(message);
if (type && !ignoredTypes.has(type)) {
log(`Found a meaningful commit. Returning true.`);
return true;
}
switch (meaning) {
case "release":
log(`Found a release commit. Returning false.`);
return false;

case "meaningful":
log(`Found a meaningful commit. Returning true.`);
return true;

log(`Found type ${type}. Continuing.`);
default:
log(`Found type ${meaning.type}. Continuing.`);
}
}

// If we've seen every commit in the history and none match, don't release
Expand Down