mirror of
https://github.com/PaulHatch/semantic-version.git
synced 2026-02-11 08:49:21 +00:00
Merge pull request #186 from PaulHatch/commit-patterns
This commit is contained in:
commit
9c93fcdb24
7 changed files with 173 additions and 3 deletions
|
|
@ -36,6 +36,10 @@ inputs:
|
||||||
description: "A string which indicates the flags used by the `minor_pattern` regular expression. Supported flags: idgs"
|
description: "A string which indicates the flags used by the `minor_pattern` regular expression. Supported flags: idgs"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
|
ignore_commits_pattern:
|
||||||
|
description: "A pattern to match commits that should be ignored when calculating the version. Commits matching this pattern will not trigger any version bump. Wrap with '/' to use a regular expression."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
version_format:
|
version_format:
|
||||||
description: "Pattern to use when formatting output version"
|
description: "Pattern to use when formatting output version"
|
||||||
required: true
|
required: true
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ export class ActionConfig {
|
||||||
public enablePrereleaseMode: boolean = false;
|
public enablePrereleaseMode: boolean = false;
|
||||||
/** If bump_each_commit is also set to true, setting this value will cause the version to increment only if the pattern specified is matched. */
|
/** If bump_each_commit is also set to true, setting this value will cause the version to increment only if the pattern specified is matched. */
|
||||||
public bumpEachCommitPatchPattern: string = "";
|
public bumpEachCommitPatchPattern: string = "";
|
||||||
|
/** A pattern to match commits that should be ignored when calculating the version. Commits matching this pattern will not trigger any version bump. Wrap with '/' to use a regular expression. */
|
||||||
|
public ignoreCommitsPattern: string = "";
|
||||||
/** If enabled, diagnostic information will be added to the action output. */
|
/** If enabled, diagnostic information will be added to the action output. */
|
||||||
public debug: boolean = false;
|
public debug: boolean = false;
|
||||||
/** Diagnostics to replay */
|
/** Diagnostics to replay */
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,11 @@ program
|
||||||
"/feat:/",
|
"/feat:/",
|
||||||
)
|
)
|
||||||
.option("--minor-flags <flags>", "Flags for minor pattern regex", "")
|
.option("--minor-flags <flags>", "Flags for minor pattern regex", "")
|
||||||
|
.option(
|
||||||
|
"-i, --ignore-commits-pattern <pattern>",
|
||||||
|
"Pattern to match commits that should be ignored when calculating version",
|
||||||
|
"",
|
||||||
|
)
|
||||||
.option(
|
.option(
|
||||||
"--version-format <format>",
|
"--version-format <format>",
|
||||||
"Version format template",
|
"Version format template",
|
||||||
|
|
@ -81,6 +86,7 @@ program
|
||||||
config.majorFlags = options.majorFlags || "";
|
config.majorFlags = options.majorFlags || "";
|
||||||
config.minorPattern = options.minorPattern;
|
config.minorPattern = options.minorPattern;
|
||||||
config.minorFlags = options.minorFlags || "";
|
config.minorFlags = options.minorFlags || "";
|
||||||
|
config.ignoreCommitsPattern = options.ignoreCommitsPattern || "";
|
||||||
config.bumpEachCommit = options.bumpEachCommit;
|
config.bumpEachCommit = options.bumpEachCommit;
|
||||||
config.bumpEachCommitPatchPattern =
|
config.bumpEachCommitPatchPattern =
|
||||||
options.bumpEachCommitPatchPattern || "";
|
options.bumpEachCommitPatchPattern || "";
|
||||||
|
|
|
||||||
135
src/main.test.ts
135
src/main.test.ts
|
|
@ -1431,5 +1431,140 @@ testInterfaces.forEach((testInterface) => {
|
||||||
},
|
},
|
||||||
timeout,
|
timeout,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Empty major pattern disables major version bumps",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
majorPattern: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("fix!: breaking fix");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.0.1+0");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Empty major pattern still allows normal behavior to verify test",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("fix!: breaking fix");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("2.0.0+0");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Empty minor pattern disables minor version bumps",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
minorPattern: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("feat: Feature Commit");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.0.1+0");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Empty major and minor patterns result in only patch bumps",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
majorPattern: "",
|
||||||
|
minorPattern: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("feat!: Breaking Change");
|
||||||
|
repo.makeCommit("feat: New Feature");
|
||||||
|
repo.makeCommit("Regular Commit");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.0.1+2");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Commits matching ignore pattern are excluded from version calculation",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
ignoreCommitsPattern: "/^chore:|^docs:|^style:/",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("chore: update dependencies");
|
||||||
|
repo.makeCommit("docs: update readme");
|
||||||
|
repo.makeCommit("style: fix formatting");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.0.0+0");
|
||||||
|
expect(result.changed).toBe(false);
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Non-ignored commits still trigger version bumps",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
ignoreCommitsPattern: "/^chore:|^docs:/",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("chore: update dependencies");
|
||||||
|
repo.makeCommit("fix: bug fix");
|
||||||
|
repo.makeCommit("docs: update readme");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.0.1+0");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
"Ignored commits do not affect major/minor detection",
|
||||||
|
async () => {
|
||||||
|
const repo = createTestRepo(testRunner, {
|
||||||
|
tagPrefix: "",
|
||||||
|
ignoreCommitsPattern: "/^chore:/",
|
||||||
|
});
|
||||||
|
|
||||||
|
repo.makeCommit("Initial Commit");
|
||||||
|
repo.exec("git tag 1.0.0");
|
||||||
|
repo.makeCommit("chore: update dependencies");
|
||||||
|
repo.makeCommit("feat: new feature");
|
||||||
|
repo.makeCommit("chore: cleanup");
|
||||||
|
const result = await repo.runAction();
|
||||||
|
|
||||||
|
expect(result.formattedVersion).toBe("1.1.0+0");
|
||||||
|
},
|
||||||
|
timeout,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ export async function run() {
|
||||||
userFormatType: core.getInput("user_format_type"),
|
userFormatType: core.getInput("user_format_type"),
|
||||||
enablePrereleaseMode: toBool(core.getInput("enable_prerelease_mode")),
|
enablePrereleaseMode: toBool(core.getInput("enable_prerelease_mode")),
|
||||||
bumpEachCommitPatchPattern: core.getInput("bump_each_commit_patch_pattern"),
|
bumpEachCommitPatchPattern: core.getInput("bump_each_commit_patch_pattern"),
|
||||||
|
ignoreCommitsPattern: core.getInput("ignore_commits_pattern"),
|
||||||
debug: toBool(core.getInput("debug")),
|
debug: toBool(core.getInput("debug")),
|
||||||
replay: "",
|
replay: "",
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -38,15 +38,17 @@ export class BumpAlwaysVersionClassifier extends DefaultVersionClassifier {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const filteredCommitSet = this.filterIgnoredCommits(commitSet);
|
||||||
|
|
||||||
let { major, minor, patch } = lastRelease;
|
let { major, minor, patch } = lastRelease;
|
||||||
let type = VersionType.None;
|
let type = VersionType.None;
|
||||||
let increment = 0;
|
let increment = 0;
|
||||||
|
|
||||||
if (commitSet.commits.length === 0) {
|
if (filteredCommitSet.commits.length === 0) {
|
||||||
return new VersionClassification(type, 0, false, major, minor, patch);
|
return new VersionClassification(type, 0, false, major, minor, patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let commit of commitSet.commits.reverse()) {
|
for (let commit of filteredCommitSet.commits.reverse()) {
|
||||||
if (this.majorPattern(commit)) {
|
if (this.majorPattern(commit)) {
|
||||||
type = VersionType.Major;
|
type = VersionType.Major;
|
||||||
} else if (this.minorPattern(commit)) {
|
} else if (this.minorPattern(commit)) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { VersionType } from "./VersionType";
|
||||||
export class DefaultVersionClassifier implements VersionClassifier {
|
export class DefaultVersionClassifier implements VersionClassifier {
|
||||||
protected majorPattern: (commit: CommitInfo) => boolean;
|
protected majorPattern: (commit: CommitInfo) => boolean;
|
||||||
protected minorPattern: (commit: CommitInfo) => boolean;
|
protected minorPattern: (commit: CommitInfo) => boolean;
|
||||||
|
protected ignorePattern: ((commit: CommitInfo) => boolean) | null;
|
||||||
protected enablePrereleaseMode: boolean;
|
protected enablePrereleaseMode: boolean;
|
||||||
|
|
||||||
constructor(config: ActionConfig) {
|
constructor(config: ActionConfig) {
|
||||||
|
|
@ -23,14 +24,31 @@ export class DefaultVersionClassifier implements VersionClassifier {
|
||||||
config.minorFlags,
|
config.minorFlags,
|
||||||
searchBody,
|
searchBody,
|
||||||
);
|
);
|
||||||
|
this.ignorePattern = config.ignoreCommitsPattern
|
||||||
|
? this.parsePattern(config.ignoreCommitsPattern, "", searchBody)
|
||||||
|
: null;
|
||||||
this.enablePrereleaseMode = config.enablePrereleaseMode;
|
this.enablePrereleaseMode = config.enablePrereleaseMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected filterIgnoredCommits(commitSet: CommitInfoSet): CommitInfoSet {
|
||||||
|
if (!this.ignorePattern) {
|
||||||
|
return commitSet;
|
||||||
|
}
|
||||||
|
const filteredCommits = commitSet.commits.filter(
|
||||||
|
(commit) => !this.ignorePattern!(commit),
|
||||||
|
);
|
||||||
|
const changed = filteredCommits.length > 0 ? commitSet.changed : false;
|
||||||
|
return new CommitInfoSet(changed, filteredCommits);
|
||||||
|
}
|
||||||
|
|
||||||
protected parsePattern(
|
protected parsePattern(
|
||||||
pattern: string,
|
pattern: string,
|
||||||
flags: string,
|
flags: string,
|
||||||
searchBody: boolean,
|
searchBody: boolean,
|
||||||
): (pattern: CommitInfo) => boolean {
|
): (pattern: CommitInfo) => boolean {
|
||||||
|
if (pattern === "") {
|
||||||
|
return (_commit: CommitInfo) => false;
|
||||||
|
}
|
||||||
if (/^\/.+\/[i]*$/.test(pattern)) {
|
if (/^\/.+\/[i]*$/.test(pattern)) {
|
||||||
const regexEnd = pattern.lastIndexOf("/");
|
const regexEnd = pattern.lastIndexOf("/");
|
||||||
const parsedFlags = pattern.slice(pattern.lastIndexOf("/") + 1);
|
const parsedFlags = pattern.slice(pattern.lastIndexOf("/") + 1);
|
||||||
|
|
@ -149,7 +167,9 @@ export class DefaultVersionClassifier implements VersionClassifier {
|
||||||
lastRelease: ReleaseInformation,
|
lastRelease: ReleaseInformation,
|
||||||
commitSet: CommitInfoSet,
|
commitSet: CommitInfoSet,
|
||||||
): Promise<VersionClassification> {
|
): Promise<VersionClassification> {
|
||||||
const { type, increment, changed } = this.resolveCommitType(commitSet);
|
const filteredCommitSet = this.filterIgnoredCommits(commitSet);
|
||||||
|
const { type, increment, changed } =
|
||||||
|
this.resolveCommitType(filteredCommitSet);
|
||||||
|
|
||||||
const { major, minor, patch } = this.getNextVersion(lastRelease, type);
|
const { major, minor, patch } = this.getNextVersion(lastRelease, type);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue