mirror of
https://github.com/PaulHatch/semantic-version.git
synced 2026-02-11 00:39:21 +00:00
feat(cli): add standalone CLI tool for semantic versioning
This commit is contained in:
parent
bdf7908364
commit
726912b971
55 changed files with 2305 additions and 2850 deletions
|
|
@ -1,18 +1,21 @@
|
|||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["jest"],
|
||||
"env": {
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"jest/globals": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"extends": ["eslint:recommended"],
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {}
|
||||
"rules": {
|
||||
"no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
128
.github/workflows/ci.yml
vendored
Normal file
128
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "feature/*"
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Format check
|
||||
run: npm run format-check
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
- name: Package
|
||||
run: npm run package
|
||||
|
||||
- name: Run Action
|
||||
uses: ./
|
||||
id: run
|
||||
with:
|
||||
debug: true
|
||||
|
||||
- name: Print version
|
||||
run: echo "Version ${{ steps.run.outputs.version }}"
|
||||
env:
|
||||
DEBUG_OUTPUT: ${{ steps.run.outputs.debug_output }}
|
||||
|
||||
release:
|
||||
name: Publish Release
|
||||
needs: build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
cache: "npm"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Package
|
||||
run: npm run package
|
||||
|
||||
- name: Extract version from tag
|
||||
id: tag
|
||||
run: |
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "major=$(echo $VERSION | cut -d. -f1)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update package.json version
|
||||
run: npm version ${{ steps.tag.outputs.version }} --no-git-tag-version
|
||||
|
||||
- name: Publish to npm
|
||||
run: npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
- name: Update major version tag
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git tag -fa v${{ steps.tag.outputs.major }} -m "Update v${{ steps.tag.outputs.major }} to ${{ steps.tag.outputs.version }}"
|
||||
git push origin v${{ steps.tag.outputs.major }} --force
|
||||
|
||||
- name: Create GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
generate_release_notes: true
|
||||
body: |
|
||||
## Installation
|
||||
|
||||
### GitHub Action
|
||||
```yaml
|
||||
- uses: paulhatch/semantic-version@v${{ steps.tag.outputs.major }}
|
||||
```
|
||||
|
||||
### CLI
|
||||
```bash
|
||||
npm install -g @semanticversion/cli
|
||||
```
|
||||
35
.github/workflows/test.yml
vendored
35
.github/workflows/test.yml
vendored
|
|
@ -1,35 +0,0 @@
|
|||
name: "Build"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "feature/*"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "Test and Run"
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: NPM Install
|
||||
run: npm ci
|
||||
- name: Test
|
||||
run: npm test
|
||||
- name: Package
|
||||
run: npm run package
|
||||
- name: Run Action
|
||||
uses: ./
|
||||
id: run
|
||||
with:
|
||||
debug: true
|
||||
- name: Print Diagnostic Output
|
||||
run: echo $DEBUG_OUTPUT
|
||||
env:
|
||||
DEBUG_OUTPUT: ${{ steps.run.outputs.debug_output }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
node_modules/
|
||||
lib/
|
||||
|
||||
# Editors
|
||||
.vscode
|
||||
|
|
|
|||
135
dist/index.js
vendored
135
dist/index.js
vendored
|
|
@ -48,15 +48,19 @@ const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* ()
|
|||
if (debugManager.isReplayMode()) {
|
||||
return debugManager.replayCommand(command, args);
|
||||
}
|
||||
let output = '', errors = '';
|
||||
let output = "", errors = "";
|
||||
const options = {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data) => { output += data.toString(); },
|
||||
stderr: (data) => { errors += data.toString(); },
|
||||
stdout: (data) => {
|
||||
output += data.toString();
|
||||
},
|
||||
stderr: (data) => {
|
||||
errors += data.toString();
|
||||
},
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}
|
||||
silent: true,
|
||||
},
|
||||
};
|
||||
let caughtError = null;
|
||||
try {
|
||||
|
|
@ -96,16 +100,24 @@ class ConfigurationProvider {
|
|||
this.config = config;
|
||||
DebugManager_1.DebugManager.getInstance().initializeConfig(config);
|
||||
}
|
||||
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
|
||||
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
|
||||
GetCommitsProvider() { return new DefaultCommitsProvider_1.DefaultCommitsProvider(this.config); }
|
||||
GetCurrentCommitResolver() {
|
||||
return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config);
|
||||
}
|
||||
GetLastReleaseResolver() {
|
||||
return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config);
|
||||
}
|
||||
GetCommitsProvider() {
|
||||
return new DefaultCommitsProvider_1.DefaultCommitsProvider(this.config);
|
||||
}
|
||||
GetVersionClassifier() {
|
||||
if (this.config.bumpEachCommit) {
|
||||
return new BumpAlwaysVersionClassifier_1.BumpAlwaysVersionClassifier(this.config);
|
||||
}
|
||||
return new DefaultVersionClassifier_1.DefaultVersionClassifier(this.config);
|
||||
}
|
||||
GetVersionFormatter() { return new DefaultVersionFormatter_1.DefaultVersionFormatter(this.config); }
|
||||
GetVersionFormatter() {
|
||||
return new DefaultVersionFormatter_1.DefaultVersionFormatter(this.config);
|
||||
}
|
||||
GetTagFormatter(branchName) {
|
||||
if (this.config.versionFromBranch) {
|
||||
return new BranchVersioningTagFormatter_1.BranchVersioningTagFormatter(this.config, branchName);
|
||||
|
|
@ -114,8 +126,10 @@ class ConfigurationProvider {
|
|||
}
|
||||
GetUserFormatter() {
|
||||
switch (this.config.userFormatType) {
|
||||
case 'json': return new JsonUserFormatter_1.JsonUserFormatter(this.config);
|
||||
case 'csv': return new CsvUserFormatter_1.CsvUserFormatter(this.config);
|
||||
case "json":
|
||||
return new JsonUserFormatter_1.JsonUserFormatter(this.config);
|
||||
case "csv":
|
||||
return new CsvUserFormatter_1.CsvUserFormatter(this.config);
|
||||
default:
|
||||
throw new Error(`Unknown user format type: ${this.config.userFormatType}, supported types: json, csv`);
|
||||
}
|
||||
|
|
@ -173,7 +187,6 @@ class DebugManager {
|
|||
this.replayMode = false;
|
||||
this.diagnosticInfo = new DiagnosticInfo();
|
||||
}
|
||||
;
|
||||
/** Enables replay mode and loads the diagnostic information from the specified string */
|
||||
replayFromDiagnostics(diagnostics) {
|
||||
this.debugEnabled = false;
|
||||
|
|
@ -182,7 +195,7 @@ class DebugManager {
|
|||
}
|
||||
/** Returns a JSON string containing the diagnostic information for this run */
|
||||
getDebugOutput(emptyRepo = false) {
|
||||
return this.isDebugEnabled() ? JSON.stringify(this.diagnosticInfo) : '';
|
||||
return this.isDebugEnabled() ? JSON.stringify(this.diagnosticInfo) : "";
|
||||
}
|
||||
/** Records a command and its output for diagnostic purposes */
|
||||
recordCommand(command, args, output, stderr, error) {
|
||||
|
|
@ -194,9 +207,10 @@ class DebugManager {
|
|||
/** Replays the specified command and returns the output */
|
||||
replayCommand(command, args) {
|
||||
if (this.diagnosticInfo === null) {
|
||||
throw new Error('No diagnostic information available for replay');
|
||||
throw new Error("No diagnostic information available for replay");
|
||||
}
|
||||
const commandResult = this.diagnosticInfo.commands.find(c => c.command === command && JSON.stringify(c.args) === JSON.stringify(args));
|
||||
const commandResult = this.diagnosticInfo.commands.find((c) => c.command === command &&
|
||||
JSON.stringify(c.args) === JSON.stringify(args));
|
||||
if (!commandResult) {
|
||||
throw new Error(`No result found in diagnostic for command "${command}"`);
|
||||
}
|
||||
|
|
@ -315,7 +329,7 @@ function runAction(configurationProvider) {
|
|||
const debugManager = DebugManager_1.DebugManager.getInstance();
|
||||
if (yield currentCommitResolver.IsEmptyRepoAsync()) {
|
||||
const versionInfo = new VersionInformation_1.VersionInformation(0, 0, 0, 0, VersionType_1.VersionType.None, [], false, false);
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', []), '', '', tagFormatter.Parse(tagFormatter.Format(versionInfo)).join('.'), debugManager.getDebugOutput(true));
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format("author", []), "", "", tagFormatter.Parse(tagFormatter.Format(versionInfo)).join("."), debugManager.getDebugOutput(true));
|
||||
}
|
||||
const currentCommit = yield currentCommitResolver.ResolveAsync();
|
||||
const lastRelease = yield lastReleaseResolver.ResolveAsync(currentCommit, tagFormatter);
|
||||
|
|
@ -327,8 +341,7 @@ function runAction(configurationProvider) {
|
|||
// version information to be used by the formatters
|
||||
let versionInfo = new VersionInformation_1.VersionInformation(major, minor, patch, increment, type, commitSet.commits, changed, isTagged);
|
||||
// Group all the authors together, count the number of commits per author
|
||||
const allAuthors = versionInfo.commits
|
||||
.reduce((acc, commit) => {
|
||||
const allAuthors = versionInfo.commits.reduce((acc, commit) => {
|
||||
const key = `${commit.author} <${commit.authorEmail}>`;
|
||||
acc[key] = acc[key] || { n: commit.author, e: commit.authorEmail, c: 0 };
|
||||
acc[key].c++;
|
||||
|
|
@ -337,7 +350,7 @@ function runAction(configurationProvider) {
|
|||
const authors = Object.values(allAuthors)
|
||||
.map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c))
|
||||
.sort((a, b) => b.commits - a.commits);
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', authors), currentCommit, lastRelease.hash, `${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`, debugManager.getDebugOutput());
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format("author", authors), currentCommit, lastRelease.hash, `${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`, debugManager.getDebugOutput());
|
||||
});
|
||||
}
|
||||
exports.runAction = runAction;
|
||||
|
|
@ -472,18 +485,18 @@ class DefaultTagFormatter {
|
|||
constructor(config) {
|
||||
this.namespace = config.namespace;
|
||||
this.tagPrefix = config.tagPrefix;
|
||||
this.namespaceSeperator = '-'; // maybe make configurable in the future
|
||||
this.namespaceSeparator = '-'; // maybe make configurable in the future
|
||||
}
|
||||
Format(versionInfo) {
|
||||
const result = `${this.tagPrefix}${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`;
|
||||
if (!!this.namespace) {
|
||||
return `${result}${this.namespaceSeperator}${this.namespace}`;
|
||||
if (this.namespace) {
|
||||
return `${result}${this.namespaceSeparator}${this.namespace}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
GetPattern() {
|
||||
if (!!this.namespace) {
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
|
||||
if (this.namespace) {
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeparator}${this.namespace}`;
|
||||
}
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
|
||||
}
|
||||
|
|
@ -510,16 +523,15 @@ class DefaultTagFormatter {
|
|||
}
|
||||
return [major, minor, patch];
|
||||
}
|
||||
;
|
||||
IsValid(tag) {
|
||||
const regexEscape = (literal) => literal.replace(/\W/g, '\\$&');
|
||||
const tagPrefix = regexEscape(this.tagPrefix);
|
||||
const namespaceSeperator = regexEscape(this.namespaceSeperator);
|
||||
const namespaceSeparator = regexEscape(this.namespaceSeparator);
|
||||
const namespace = regexEscape(this.namespace);
|
||||
if (!!this.namespace) {
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
|
||||
if (this.namespace) {
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\\.[0-9]+\\.[0-9]+${namespaceSeparator}${namespace}$`).test(tag);
|
||||
}
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\\.[0-9]+\\.[0-9]+$`).test(tag);
|
||||
}
|
||||
}
|
||||
exports.DefaultTagFormatter = DefaultTagFormatter;
|
||||
|
|
@ -616,15 +628,12 @@ const ConfigurationProvider_1 = __nccwpck_require__(2614);
|
|||
const core = __importStar(__nccwpck_require__(2186));
|
||||
const VersionType_1 = __nccwpck_require__(895);
|
||||
function setOutput(versionResult) {
|
||||
const { major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput } = versionResult;
|
||||
const { major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput, } = versionResult;
|
||||
const repository = process.env.GITHUB_REPOSITORY;
|
||||
if (!changed) {
|
||||
core.info('No changes detected for this commit');
|
||||
core.info("No changes detected for this commit");
|
||||
}
|
||||
core.info(`Version is ${formattedVersion}`);
|
||||
if (repository !== undefined) {
|
||||
core.info(`To create a release for this version, go to https://github.com/${repository}/releases/new?tag=${versionTag}&target=${currentCommit.split('/').slice(-1)[0]}`);
|
||||
}
|
||||
core.setOutput("version", formattedVersion);
|
||||
core.setOutput("major", major.toString());
|
||||
core.setOutput("minor", minor.toString());
|
||||
|
|
@ -643,51 +652,51 @@ function setOutput(versionResult) {
|
|||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
function toBool(value) {
|
||||
if (!value || value.toLowerCase() === 'false') {
|
||||
if (!value || value.toLowerCase() === "false") {
|
||||
return false;
|
||||
}
|
||||
else if (value.toLowerCase() === 'true') {
|
||||
else if (value.toLowerCase() === "true") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function toStringOrBool(value) {
|
||||
if (!value || value === 'false') {
|
||||
if (!value || value === "false") {
|
||||
return false;
|
||||
}
|
||||
if (value === 'true') {
|
||||
if (value === "true") {
|
||||
return true;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const config = {
|
||||
branch: core.getInput('branch'),
|
||||
tagPrefix: core.getInput('tag_prefix'),
|
||||
useBranches: toBool(core.getInput('use_branches')),
|
||||
versionFromBranch: toStringOrBool(core.getInput('version_from_branch')),
|
||||
majorPattern: core.getInput('major_pattern'),
|
||||
minorPattern: core.getInput('minor_pattern'),
|
||||
majorFlags: core.getInput('major_regexp_flags'),
|
||||
minorFlags: core.getInput('minor_regexp_flags'),
|
||||
versionFormat: core.getInput('version_format'),
|
||||
changePath: core.getInput('change_path'),
|
||||
namespace: core.getInput('namespace'),
|
||||
bumpEachCommit: toBool(core.getInput('bump_each_commit')),
|
||||
searchCommitBody: toBool(core.getInput('search_commit_body')),
|
||||
userFormatType: core.getInput('user_format_type'),
|
||||
enablePrereleaseMode: toBool(core.getInput('enable_prerelease_mode')),
|
||||
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern'),
|
||||
debug: toBool(core.getInput('debug')),
|
||||
replay: ''
|
||||
branch: core.getInput("branch"),
|
||||
tagPrefix: core.getInput("tag_prefix"),
|
||||
useBranches: toBool(core.getInput("use_branches")),
|
||||
versionFromBranch: toStringOrBool(core.getInput("version_from_branch")),
|
||||
majorPattern: core.getInput("major_pattern"),
|
||||
minorPattern: core.getInput("minor_pattern"),
|
||||
majorFlags: core.getInput("major_regexp_flags"),
|
||||
minorFlags: core.getInput("minor_regexp_flags"),
|
||||
versionFormat: core.getInput("version_format"),
|
||||
changePath: core.getInput("change_path"),
|
||||
namespace: core.getInput("namespace"),
|
||||
bumpEachCommit: toBool(core.getInput("bump_each_commit")),
|
||||
searchCommitBody: toBool(core.getInput("search_commit_body")),
|
||||
userFormatType: core.getInput("user_format_type"),
|
||||
enablePrereleaseMode: toBool(core.getInput("enable_prerelease_mode")),
|
||||
bumpEachCommitPatchPattern: core.getInput("bump_each_commit_patch_pattern"),
|
||||
debug: toBool(core.getInput("debug")),
|
||||
replay: "",
|
||||
};
|
||||
if (config.useBranches) {
|
||||
core.warning(`The 'use_branches' input option is deprecated, please see the documentation for more information on how to use branches`);
|
||||
}
|
||||
if (config.versionFormat === '' && core.getInput('format') !== '') {
|
||||
if (config.versionFormat === "" && core.getInput("format") !== "") {
|
||||
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);
|
||||
config.versionFormat = core.getInput('format');
|
||||
config.versionFormat = core.getInput("format");
|
||||
}
|
||||
if (core.getInput('short_tags') !== '') {
|
||||
if (core.getInput("short_tags") !== "") {
|
||||
core.warning(`The 'short_tags' input option is no longer supported`);
|
||||
}
|
||||
const configurationProvider = new ConfigurationProvider_1.ConfigurationProvider(config);
|
||||
|
|
@ -1047,12 +1056,12 @@ class DefaultLastReleaseResolver {
|
|||
let currentTag = (yield (0, CommandRunner_1.cmd)(`git tag --points-at ${current} ${releasePattern}`)).trim();
|
||||
currentTag = tagFormatter.IsValid(currentTag) ? currentTag : '';
|
||||
const isTagged = currentTag !== '';
|
||||
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
const [currentMajor, currentMinor, currentPatch] = currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
let tagsCount = 0;
|
||||
let tag = '';
|
||||
try {
|
||||
const refPrefixPattern = this.useBranches ? 'refs/heads/' : 'refs/tags/';
|
||||
if (!!currentTag) {
|
||||
if (currentTag) {
|
||||
// If we already have the current branch tagged, we are checking for the previous one
|
||||
// so that we will have an accurate increment (assuming the new tag is the expected one)
|
||||
const command = `git for-each-ref --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
|
||||
|
|
@ -1079,10 +1088,10 @@ class DefaultLastReleaseResolver {
|
|||
// practice this isn't likely to happen, but it keeps the test output from being
|
||||
// polluted with a bunch of warnings.
|
||||
if (tagsCount > 0) {
|
||||
core.warning(`None of the ${tagsCount} tags(s) found were valid version tags for the present configuration. If this is unexpected, check to ensure that the configuration is correct and matches the tag format you are using.`);
|
||||
core.warning(`None of the ${tagsCount} tags(s) found were valid version tags for the present configuration. If this is unexpected, check to ensure that the configuration is correct and matches the tag format you are using. If you have not yet tagged this repo with a version tag, this can be ignored.`);
|
||||
}
|
||||
else {
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote.');
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote. If you have not yet tagged this repo with a version tag, this can be ignored.');
|
||||
}
|
||||
}
|
||||
const [major, minor, patch] = tagFormatter.Parse('');
|
||||
|
|
|
|||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,45 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ActionConfig = void 0;
|
||||
/** Represents the input configuration for the semantic-version action */
|
||||
class ActionConfig {
|
||||
constructor() {
|
||||
/** Set to specify a specific branch, default is the current HEAD */
|
||||
this.branch = "HEAD";
|
||||
/** The prefix to use to identify tags */
|
||||
this.tagPrefix = "v";
|
||||
/** (Deprecated) Use branches instead of tags */
|
||||
this.useBranches = false;
|
||||
/** If true, the branch will be used to select the maximum version. */
|
||||
this.versionFromBranch = false;
|
||||
/** A string which, if present in a git commit, indicates that a change represents a major (breaking) change. Wrap with '/' to match using a regular expression. */
|
||||
this.majorPattern = "/!:|BREAKING CHANGE:/";
|
||||
/** A string which indicates the flags used by the `majorPattern` regular expression. */
|
||||
this.majorFlags = "";
|
||||
/** A string which, if present in a git commit, indicates that a change represents a minor (feature) change. Wrap with '/' to match using a regular expression. */
|
||||
this.minorPattern = "/feat:/";
|
||||
/** A string which indicates the flags used by the `minorPattern` regular expression. */
|
||||
this.minorFlags = "";
|
||||
/** Pattern to use when formatting output version */
|
||||
this.versionFormat = '${major}.${minor}.${patch}';
|
||||
/** Path to check for changes. If any changes are detected in the path the 'changed' output will true. Enter multiple paths separated by spaces. */
|
||||
this.changePath = '';
|
||||
/** Use to create a named sub-version. This value will be appended to tags created for this version. */
|
||||
this.namespace = "";
|
||||
/** If true, every commit will be treated as a bump to the version. */
|
||||
this.bumpEachCommit = false;
|
||||
/** If true, the body of commits will also be searched for major/minor patterns to determine the version type */
|
||||
this.searchCommitBody = false;
|
||||
/** The output method used to generate list of users, 'csv' or 'json'. Default is 'csv'. */
|
||||
this.userFormatType = "csv";
|
||||
/** Prevents pre-v1.0.0 version from automatically incrementing the major version. If enabled, when the major version is 0, major releases will be treated as minor and minor as patch. Note that the versionType output is unchanged. */
|
||||
this.enablePrereleaseMode = 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. */
|
||||
this.bumpEachCommitPatchPattern = "";
|
||||
/** If enabled, diagnostic information will be added to the action output. */
|
||||
this.debug = false;
|
||||
/** Diagnostics to replay */
|
||||
this.replay = '';
|
||||
}
|
||||
}
|
||||
exports.ActionConfig = ActionConfig;
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.cmd = void 0;
|
||||
// Using require instead of import to support integration testing
|
||||
const exec = __importStar(require("@actions/exec"));
|
||||
const DebugManager_1 = require("./DebugManager");
|
||||
const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const debugManager = DebugManager_1.DebugManager.getInstance();
|
||||
if (debugManager.isReplayMode()) {
|
||||
return debugManager.replayCommand(command, args);
|
||||
}
|
||||
let output = '', errors = '';
|
||||
const options = {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data) => { output += data.toString(); },
|
||||
stderr: (data) => { errors += data.toString(); },
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}
|
||||
};
|
||||
let caughtError = null;
|
||||
try {
|
||||
yield exec.exec(command, args, options);
|
||||
}
|
||||
catch (err) {
|
||||
caughtError = err;
|
||||
}
|
||||
debugManager.recordCommand(command, args, output, errors, caughtError);
|
||||
return output;
|
||||
});
|
||||
exports.cmd = cmd;
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ConfigurationProvider = void 0;
|
||||
const CsvUserFormatter_1 = require("./formatting/CsvUserFormatter");
|
||||
const BranchVersioningTagFormatter_1 = require("./formatting/BranchVersioningTagFormatter");
|
||||
const DefaultTagFormatter_1 = require("./formatting/DefaultTagFormatter");
|
||||
const DefaultVersionFormatter_1 = require("./formatting/DefaultVersionFormatter");
|
||||
const JsonUserFormatter_1 = require("./formatting/JsonUserFormatter");
|
||||
const DefaultCommitsProvider_1 = require("./providers/DefaultCommitsProvider");
|
||||
const DefaultCurrentCommitResolver_1 = require("./providers/DefaultCurrentCommitResolver");
|
||||
const DefaultVersionClassifier_1 = require("./providers/DefaultVersionClassifier");
|
||||
const DefaultLastReleaseResolver_1 = require("./providers/DefaultLastReleaseResolver");
|
||||
const BumpAlwaysVersionClassifier_1 = require("./providers/BumpAlwaysVersionClassifier");
|
||||
const DebugManager_1 = require("./DebugManager");
|
||||
class ConfigurationProvider {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
DebugManager_1.DebugManager.getInstance().initializeConfig(config);
|
||||
}
|
||||
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
|
||||
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
|
||||
GetCommitsProvider() { return new DefaultCommitsProvider_1.DefaultCommitsProvider(this.config); }
|
||||
GetVersionClassifier() {
|
||||
if (this.config.bumpEachCommit) {
|
||||
return new BumpAlwaysVersionClassifier_1.BumpAlwaysVersionClassifier(this.config);
|
||||
}
|
||||
return new DefaultVersionClassifier_1.DefaultVersionClassifier(this.config);
|
||||
}
|
||||
GetVersionFormatter() { return new DefaultVersionFormatter_1.DefaultVersionFormatter(this.config); }
|
||||
GetTagFormatter(branchName) {
|
||||
if (this.config.versionFromBranch) {
|
||||
return new BranchVersioningTagFormatter_1.BranchVersioningTagFormatter(this.config, branchName);
|
||||
}
|
||||
return new DefaultTagFormatter_1.DefaultTagFormatter(this.config);
|
||||
}
|
||||
GetUserFormatter() {
|
||||
switch (this.config.userFormatType) {
|
||||
case 'json': return new JsonUserFormatter_1.JsonUserFormatter(this.config);
|
||||
case 'csv': return new CsvUserFormatter_1.CsvUserFormatter(this.config);
|
||||
default:
|
||||
throw new Error(`Unknown user format type: ${this.config.userFormatType}, supported types: json, csv`);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ConfigurationProvider = ConfigurationProvider;
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DebugManager = void 0;
|
||||
/** Utility class for managing debug mode and diagnostic information */
|
||||
class DebugManager {
|
||||
constructor() {
|
||||
this.debugEnabled = false;
|
||||
this.replayMode = false;
|
||||
this.diagnosticInfo = null;
|
||||
}
|
||||
/** Returns the singleton instance of the DebugManager */
|
||||
static getInstance() {
|
||||
if (!DebugManager.instance) {
|
||||
DebugManager.instance = new DebugManager();
|
||||
}
|
||||
return DebugManager.instance;
|
||||
}
|
||||
/** Clears the singleton instance of the DebugManager (used for testing) */
|
||||
static clearState() {
|
||||
DebugManager.instance = new DebugManager();
|
||||
}
|
||||
/** Returns true if debug mode is enabled */
|
||||
isDebugEnabled() {
|
||||
return this.debugEnabled;
|
||||
}
|
||||
/** Returns true if replay mode is enabled */
|
||||
isReplayMode() {
|
||||
return this.replayMode;
|
||||
}
|
||||
initializeConfig(config) {
|
||||
if (config.debug) {
|
||||
this.setDebugEnabled(true);
|
||||
}
|
||||
else if (config.replay.length > 0) {
|
||||
this.replayFromDiagnostics(config.replay);
|
||||
}
|
||||
}
|
||||
/** Enables or disables debug mode, also clears any existing diagnostics info */
|
||||
setDebugEnabled(enableDebug = true) {
|
||||
this.debugEnabled = enableDebug;
|
||||
this.replayMode = false;
|
||||
this.diagnosticInfo = new DiagnosticInfo();
|
||||
}
|
||||
;
|
||||
/** Enables replay mode and loads the diagnostic information from the specified string */
|
||||
replayFromDiagnostics(diagnostics) {
|
||||
this.debugEnabled = false;
|
||||
this.replayMode = true;
|
||||
this.diagnosticInfo = JSON.parse(diagnostics);
|
||||
}
|
||||
/** Returns a JSON string containing the diagnostic information for this run */
|
||||
getDebugOutput(emptyRepo = false) {
|
||||
return this.isDebugEnabled() ? JSON.stringify(this.diagnosticInfo) : '';
|
||||
}
|
||||
/** Records a command and its output for diagnostic purposes */
|
||||
recordCommand(command, args, output, stderr, error) {
|
||||
var _a;
|
||||
if (this.isDebugEnabled()) {
|
||||
(_a = this.diagnosticInfo) === null || _a === void 0 ? void 0 : _a.recordCommand(command, args, output, stderr, error);
|
||||
}
|
||||
}
|
||||
/** Replays the specified command and returns the output */
|
||||
replayCommand(command, args) {
|
||||
if (this.diagnosticInfo === null) {
|
||||
throw new Error('No diagnostic information available for replay');
|
||||
}
|
||||
const commandResult = this.diagnosticInfo.commands.find(c => c.command === command && JSON.stringify(c.args) === JSON.stringify(args));
|
||||
if (!commandResult) {
|
||||
throw new Error(`No result found in diagnostic for command "${command}"`);
|
||||
}
|
||||
if (commandResult.error) {
|
||||
throw commandResult.error;
|
||||
}
|
||||
if (commandResult.stderr) {
|
||||
console.error(commandResult.stderr);
|
||||
}
|
||||
return commandResult.output;
|
||||
}
|
||||
}
|
||||
exports.DebugManager = DebugManager;
|
||||
/** Represents a CLI command result */
|
||||
class CommandResult {
|
||||
constructor(command, args, output, stderr, error) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
this.output = output;
|
||||
this.stderr = stderr;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
/** Represents the result of the commands executed for a run */
|
||||
class DiagnosticInfo {
|
||||
constructor() {
|
||||
this.commands = [];
|
||||
this.empty = false;
|
||||
}
|
||||
recordCommand(command, args, output, stderr, error) {
|
||||
this.commands.push(new CommandResult(command, args, output, stderr, error));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VersionResult = void 0;
|
||||
/** Represents the total output for the action */
|
||||
class VersionResult {
|
||||
/**
|
||||
* Creates a new result instance
|
||||
* @param major - The major version number
|
||||
* @param minor - The minor version number
|
||||
* @param patch - The patch version number
|
||||
* @param increment - The number of commits for this version (usually used to create version suffix)
|
||||
* @param versionType - The type of version, e.g. major, minor, patch
|
||||
* @param formattedVersion - The formatted semantic version
|
||||
* @param versionTag - The string to be used as a Git tag
|
||||
* @param changed - True if the version was changed, otherwise false
|
||||
* @param isTagged - True if the commit had a tag that matched the `versionTag` format
|
||||
* @param authors - Authors formatted according to the format mode (e.g. JSON, CSV, YAML, etc.)
|
||||
* @param currentCommit - The current commit hash
|
||||
* @param previousCommit - The previous commit hash
|
||||
* @param previousVersion - The previous version
|
||||
* @param debugOutput - Diagnostic information, if debug is enabled
|
||||
*/
|
||||
constructor(major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.increment = increment;
|
||||
this.versionType = versionType;
|
||||
this.formattedVersion = formattedVersion;
|
||||
this.versionTag = versionTag;
|
||||
this.changed = changed;
|
||||
this.isTagged = isTagged;
|
||||
this.authors = authors;
|
||||
this.currentCommit = currentCommit;
|
||||
this.previousCommit = previousCommit;
|
||||
this.previousVersion = previousVersion;
|
||||
this.debugOutput = debugOutput;
|
||||
}
|
||||
}
|
||||
exports.VersionResult = VersionResult;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.runAction = void 0;
|
||||
const VersionResult_1 = require("./VersionResult");
|
||||
const VersionType_1 = require("./providers/VersionType");
|
||||
const UserInfo_1 = require("./providers/UserInfo");
|
||||
const VersionInformation_1 = require("./providers/VersionInformation");
|
||||
const DebugManager_1 = require("./DebugManager");
|
||||
function runAction(configurationProvider) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const currentCommitResolver = configurationProvider.GetCurrentCommitResolver();
|
||||
const lastReleaseResolver = configurationProvider.GetLastReleaseResolver();
|
||||
const commitsProvider = configurationProvider.GetCommitsProvider();
|
||||
const versionClassifier = configurationProvider.GetVersionClassifier();
|
||||
const versionFormatter = configurationProvider.GetVersionFormatter();
|
||||
const tagFormatter = configurationProvider.GetTagFormatter(yield currentCommitResolver.ResolveBranchNameAsync());
|
||||
const userFormatter = configurationProvider.GetUserFormatter();
|
||||
const debugManager = DebugManager_1.DebugManager.getInstance();
|
||||
if (yield currentCommitResolver.IsEmptyRepoAsync()) {
|
||||
const versionInfo = new VersionInformation_1.VersionInformation(0, 0, 0, 0, VersionType_1.VersionType.None, [], false, false);
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', []), '', '', tagFormatter.Parse(tagFormatter.Format(versionInfo)).join('.'), debugManager.getDebugOutput(true));
|
||||
}
|
||||
const currentCommit = yield currentCommitResolver.ResolveAsync();
|
||||
const lastRelease = yield lastReleaseResolver.ResolveAsync(currentCommit, tagFormatter);
|
||||
const commitSet = yield commitsProvider.GetCommitsAsync(lastRelease.hash, currentCommit);
|
||||
const classification = yield versionClassifier.ClassifyAsync(lastRelease, commitSet);
|
||||
const { isTagged } = lastRelease;
|
||||
const { major, minor, patch, increment, type, changed } = classification;
|
||||
// At this point all necessary data has been pulled from the database, create
|
||||
// version information to be used by the formatters
|
||||
let versionInfo = new VersionInformation_1.VersionInformation(major, minor, patch, increment, type, commitSet.commits, changed, isTagged);
|
||||
// Group all the authors together, count the number of commits per author
|
||||
const allAuthors = versionInfo.commits
|
||||
.reduce((acc, commit) => {
|
||||
const key = `${commit.author} <${commit.authorEmail}>`;
|
||||
acc[key] = acc[key] || { n: commit.author, e: commit.authorEmail, c: 0 };
|
||||
acc[key].c++;
|
||||
return acc;
|
||||
}, {});
|
||||
const authors = Object.values(allAuthors)
|
||||
.map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c))
|
||||
.sort((a, b) => b.commits - a.commits);
|
||||
return new VersionResult_1.VersionResult(versionInfo.major, versionInfo.minor, versionInfo.patch, versionInfo.increment, versionInfo.type, versionFormatter.Format(versionInfo), tagFormatter.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', authors), currentCommit, lastRelease.hash, `${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`, debugManager.getDebugOutput());
|
||||
});
|
||||
}
|
||||
exports.runAction = runAction;
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BranchVersioningTagFormatter = void 0;
|
||||
const DefaultTagFormatter_1 = require("./DefaultTagFormatter");
|
||||
/** Default tag formatter which allows a prefix to be specified */
|
||||
class BranchVersioningTagFormatter extends DefaultTagFormatter_1.DefaultTagFormatter {
|
||||
getRegex(pattern) {
|
||||
if (/^\/.+\/[i]*$/.test(pattern)) {
|
||||
const regexEnd = pattern.lastIndexOf('/');
|
||||
const parsedFlags = pattern.slice(pattern.lastIndexOf('/') + 1);
|
||||
return new RegExp(pattern.slice(1, regexEnd), parsedFlags);
|
||||
}
|
||||
return new RegExp(pattern);
|
||||
}
|
||||
constructor(config, branchName) {
|
||||
super(config);
|
||||
const pattern = config.versionFromBranch === true ?
|
||||
new RegExp("[0-9]+.[0-9]+$|[0-9]+$") :
|
||||
this.getRegex(config.versionFromBranch);
|
||||
const result = pattern.exec(branchName);
|
||||
if (result === null) {
|
||||
this.major = NaN;
|
||||
this.onVersionBranch = false;
|
||||
return;
|
||||
}
|
||||
let branchVersion;
|
||||
switch (result === null || result === void 0 ? void 0 : result.length) {
|
||||
case 1:
|
||||
branchVersion = result[0];
|
||||
break;
|
||||
case 2:
|
||||
branchVersion = result[1];
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unable to parse version from branch named '${branchName}' using pattern '${pattern}'`);
|
||||
}
|
||||
this.onVersionBranch = true;
|
||||
const versionValues = branchVersion.split('.');
|
||||
if (versionValues.length > 2) {
|
||||
throw new Error(`The version string '${branchVersion}' parsed from branch '${branchName}' is invalid. It must be in the format 'major.minor' or 'major'`);
|
||||
}
|
||||
this.major = parseInt(versionValues[0]);
|
||||
if (isNaN(this.major)) {
|
||||
throw new Error(`The major version '${versionValues[0]}' parsed from branch '${branchName}' is invalid. It must be a number.`);
|
||||
}
|
||||
if (versionValues.length > 1) {
|
||||
this.minor = parseInt(versionValues[1]);
|
||||
if (isNaN(this.minor)) {
|
||||
throw new Error(`The minor version '${versionValues[1]}' parsed from branch '${branchName}' is invalid. It must be a number.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
GetPattern() {
|
||||
let pattern = super.GetPattern();
|
||||
if (!this.onVersionBranch) {
|
||||
return pattern;
|
||||
}
|
||||
if (this.minor === undefined) {
|
||||
return pattern.replace('*[0-9].*[0-9].*[0-9]', `${this.major}.*[0-9].*[0-9]`);
|
||||
}
|
||||
return pattern.replace('*[0-9].*[0-9].*[0-9]', `${this.major}.${this.minor}.*[0-9]`);
|
||||
}
|
||||
IsValid(tag) {
|
||||
if (!this.onVersionBranch) {
|
||||
return super.IsValid(tag);
|
||||
}
|
||||
if (!super.IsValid(tag)) {
|
||||
return false;
|
||||
}
|
||||
const parsed = super.Parse(tag);
|
||||
if (parsed[0] !== this.major) {
|
||||
return false;
|
||||
}
|
||||
if (this.minor !== undefined && parsed[1] !== this.minor) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Parse(tag) {
|
||||
if (!this.onVersionBranch) {
|
||||
return super.Parse(tag);
|
||||
}
|
||||
const parsed = super.Parse(tag);
|
||||
return [this.major, this.minor || parsed[1], parsed[2]];
|
||||
}
|
||||
}
|
||||
exports.BranchVersioningTagFormatter = BranchVersioningTagFormatter;
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CsvUserFormatter = void 0;
|
||||
class CsvUserFormatter {
|
||||
constructor(config) {
|
||||
// placeholder for consistency with other formatters
|
||||
}
|
||||
Format(type, users) {
|
||||
return users.map(user => `${user.name} <${user.email}>`).join(', ');
|
||||
}
|
||||
}
|
||||
exports.CsvUserFormatter = CsvUserFormatter;
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultTagFormatter = void 0;
|
||||
/** Default tag formatter which allows a prefix to be specified */
|
||||
class DefaultTagFormatter {
|
||||
constructor(config) {
|
||||
this.namespace = config.namespace;
|
||||
this.tagPrefix = config.tagPrefix;
|
||||
this.namespaceSeperator = '-'; // maybe make configurable in the future
|
||||
}
|
||||
Format(versionInfo) {
|
||||
const result = `${this.tagPrefix}${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`;
|
||||
if (!!this.namespace) {
|
||||
return `${result}${this.namespaceSeperator}${this.namespace}`;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
GetPattern() {
|
||||
if (!!this.namespace) {
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
|
||||
}
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
|
||||
}
|
||||
Parse(tag) {
|
||||
if (tag === '') {
|
||||
return [0, 0, 0];
|
||||
}
|
||||
let tagParts = tag
|
||||
.replace(this.tagPrefix, '<--!PREFIX!-->')
|
||||
.replace(this.namespace, '<--!NAMESPACE!-->')
|
||||
.split('/');
|
||||
const stripedTag = tagParts[tagParts.length - 1]
|
||||
.replace('<--!PREFIX!-->', this.tagPrefix)
|
||||
.replace('<--!NAMESPACE!-->', this.namespace);
|
||||
let versionValues = stripedTag
|
||||
.substring(this.tagPrefix.length)
|
||||
.slice(0, this.namespace === '' ? 999 : -(this.namespace.length + 1))
|
||||
.split('.');
|
||||
let major = parseInt(versionValues[0]);
|
||||
let minor = versionValues.length > 1 ? parseInt(versionValues[1]) : 0;
|
||||
let patch = versionValues.length > 2 ? parseInt(versionValues[2]) : 0;
|
||||
if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
|
||||
throw `Invalid tag ${tag} (${versionValues})`;
|
||||
}
|
||||
return [major, minor, patch];
|
||||
}
|
||||
;
|
||||
IsValid(tag) {
|
||||
const regexEscape = (literal) => literal.replace(/\W/g, '\\$&');
|
||||
const tagPrefix = regexEscape(this.tagPrefix);
|
||||
const namespaceSeperator = regexEscape(this.namespaceSeperator);
|
||||
const namespace = regexEscape(this.namespace);
|
||||
if (!!this.namespace) {
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
|
||||
}
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
|
||||
}
|
||||
}
|
||||
exports.DefaultTagFormatter = DefaultTagFormatter;
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultVersionFormatter = void 0;
|
||||
class DefaultVersionFormatter {
|
||||
constructor(config) {
|
||||
this.formatString = config.versionFormat;
|
||||
}
|
||||
Format(versionInfo) {
|
||||
return this.formatString
|
||||
.replace('${major}', versionInfo.major.toString())
|
||||
.replace('${minor}', versionInfo.minor.toString())
|
||||
.replace('${patch}', versionInfo.patch.toString())
|
||||
.replace('${increment}', versionInfo.increment.toString());
|
||||
}
|
||||
}
|
||||
exports.DefaultVersionFormatter = DefaultVersionFormatter;
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.JsonUserFormatter = void 0;
|
||||
class JsonUserFormatter {
|
||||
constructor(config) {
|
||||
// placeholder for consistency with other formatters
|
||||
}
|
||||
Format(type, users) {
|
||||
let result = users.map(u => ({ name: u.name, email: u.email }));
|
||||
return JSON.stringify(result).replace('\n', '');
|
||||
}
|
||||
}
|
||||
exports.JsonUserFormatter = JsonUserFormatter;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.YamlUserFormatter = void 0;
|
||||
class YamlUserFormatter {
|
||||
constructor(config) {
|
||||
this.lineBreak = config.userFormatLineBreak || '\n';
|
||||
this.includeType = config.includeType || false;
|
||||
}
|
||||
Format(type, users) {
|
||||
const result = users.flatMap(u => [`- name: "${u.name}"`, ` email: "${u.email}"`]).join(this.lineBreak);
|
||||
return this.includeType ?
|
||||
`${type}:${this.lineBreak}${result}` :
|
||||
result;
|
||||
}
|
||||
}
|
||||
exports.YamlUserFormatter = YamlUserFormatter;
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.JsonUserFormatter = exports.DefaultVersionFormatter = exports.DefaultTagFormatter = exports.CsvUserFormatter = void 0;
|
||||
var CsvUserFormatter_1 = require("./CsvUserFormatter");
|
||||
Object.defineProperty(exports, "CsvUserFormatter", { enumerable: true, get: function () { return CsvUserFormatter_1.CsvUserFormatter; } });
|
||||
var DefaultTagFormatter_1 = require("./DefaultTagFormatter");
|
||||
Object.defineProperty(exports, "DefaultTagFormatter", { enumerable: true, get: function () { return DefaultTagFormatter_1.DefaultTagFormatter; } });
|
||||
var DefaultVersionFormatter_1 = require("./DefaultVersionFormatter");
|
||||
Object.defineProperty(exports, "DefaultVersionFormatter", { enumerable: true, get: function () { return DefaultVersionFormatter_1.DefaultVersionFormatter; } });
|
||||
var JsonUserFormatter_1 = require("./JsonUserFormatter");
|
||||
Object.defineProperty(exports, "JsonUserFormatter", { enumerable: true, get: function () { return JsonUserFormatter_1.JsonUserFormatter; } });
|
||||
121
lib/main.js
121
lib/main.js
|
|
@ -1,121 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.run = void 0;
|
||||
const action_1 = require("./action");
|
||||
const ConfigurationProvider_1 = require("./ConfigurationProvider");
|
||||
const core = __importStar(require("@actions/core"));
|
||||
const VersionType_1 = require("./providers/VersionType");
|
||||
function setOutput(versionResult) {
|
||||
const { major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput } = versionResult;
|
||||
const repository = process.env.GITHUB_REPOSITORY;
|
||||
if (!changed) {
|
||||
core.info('No changes detected for this commit');
|
||||
}
|
||||
core.info(`Version is ${formattedVersion}`);
|
||||
if (repository !== undefined) {
|
||||
core.info(`To create a release for this version, go to https://github.com/${repository}/releases/new?tag=${versionTag}&target=${currentCommit.split('/').slice(-1)[0]}`);
|
||||
}
|
||||
core.setOutput("version", formattedVersion);
|
||||
core.setOutput("major", major.toString());
|
||||
core.setOutput("minor", minor.toString());
|
||||
core.setOutput("patch", patch.toString());
|
||||
core.setOutput("increment", increment.toString());
|
||||
core.setOutput("version_type", VersionType_1.VersionType[versionType].toLowerCase());
|
||||
core.setOutput("changed", changed.toString());
|
||||
core.setOutput("is_tagged", isTagged.toString());
|
||||
core.setOutput("version_tag", versionTag);
|
||||
core.setOutput("authors", authors);
|
||||
core.setOutput("previous_commit", previousCommit);
|
||||
core.setOutput("previous_version", previousVersion);
|
||||
core.setOutput("current_commit", currentCommit);
|
||||
core.setOutput("debug_output", debugOutput);
|
||||
}
|
||||
function run() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
function toBool(value) {
|
||||
if (!value || value.toLowerCase() === 'false') {
|
||||
return false;
|
||||
}
|
||||
else if (value.toLowerCase() === 'true') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function toStringOrBool(value) {
|
||||
if (!value || value === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (value === 'true') {
|
||||
return true;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const config = {
|
||||
branch: core.getInput('branch'),
|
||||
tagPrefix: core.getInput('tag_prefix'),
|
||||
useBranches: toBool(core.getInput('use_branches')),
|
||||
versionFromBranch: toStringOrBool(core.getInput('version_from_branch')),
|
||||
majorPattern: core.getInput('major_pattern'),
|
||||
minorPattern: core.getInput('minor_pattern'),
|
||||
majorFlags: core.getInput('major_regexp_flags'),
|
||||
minorFlags: core.getInput('minor_regexp_flags'),
|
||||
versionFormat: core.getInput('version_format'),
|
||||
changePath: core.getInput('change_path'),
|
||||
namespace: core.getInput('namespace'),
|
||||
bumpEachCommit: toBool(core.getInput('bump_each_commit')),
|
||||
searchCommitBody: toBool(core.getInput('search_commit_body')),
|
||||
userFormatType: core.getInput('user_format_type'),
|
||||
enablePrereleaseMode: toBool(core.getInput('enable_prerelease_mode')),
|
||||
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern'),
|
||||
debug: toBool(core.getInput('debug')),
|
||||
replay: ''
|
||||
};
|
||||
if (config.useBranches) {
|
||||
core.warning(`The 'use_branches' input option is deprecated, please see the documentation for more information on how to use branches`);
|
||||
}
|
||||
if (config.versionFormat === '' && core.getInput('format') !== '') {
|
||||
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);
|
||||
config.versionFormat = core.getInput('format');
|
||||
}
|
||||
if (core.getInput('short_tags') !== '') {
|
||||
core.warning(`The 'short_tags' input option is no longer supported`);
|
||||
}
|
||||
const configurationProvider = new ConfigurationProvider_1.ConfigurationProvider(config);
|
||||
const result = yield (0, action_1.runAction)(configurationProvider);
|
||||
setOutput(result);
|
||||
});
|
||||
}
|
||||
exports.run = run;
|
||||
run();
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BumpAlwaysVersionClassifier = void 0;
|
||||
const DefaultVersionClassifier_1 = require("./DefaultVersionClassifier");
|
||||
const VersionClassification_1 = require("./VersionClassification");
|
||||
const VersionType_1 = require("./VersionType");
|
||||
class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVersionClassifier {
|
||||
constructor(config) {
|
||||
super(config);
|
||||
this.enablePrereleaseMode = config.enablePrereleaseMode;
|
||||
this.patchPattern = !config.bumpEachCommitPatchPattern ?
|
||||
_ => true :
|
||||
this.parsePattern(config.bumpEachCommitPatchPattern, "", config.searchCommitBody);
|
||||
}
|
||||
ClassifyAsync(lastRelease, commitSet) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (lastRelease.currentPatch !== null) {
|
||||
return new VersionClassification_1.VersionClassification(VersionType_1.VersionType.None, 0, false, lastRelease.currentMajor, lastRelease.currentMinor, lastRelease.currentPatch);
|
||||
}
|
||||
let { major, minor, patch } = lastRelease;
|
||||
let type = VersionType_1.VersionType.None;
|
||||
let increment = 0;
|
||||
if (commitSet.commits.length === 0) {
|
||||
return new VersionClassification_1.VersionClassification(type, 0, false, major, minor, patch);
|
||||
}
|
||||
for (let commit of commitSet.commits.reverse()) {
|
||||
if (this.majorPattern(commit)) {
|
||||
type = VersionType_1.VersionType.Major;
|
||||
}
|
||||
else if (this.minorPattern(commit)) {
|
||||
type = VersionType_1.VersionType.Minor;
|
||||
}
|
||||
else if (this.patchPattern(commit) ||
|
||||
(major === 0 && minor === 0 && patch === 0 && commitSet.commits.length > 0)) {
|
||||
type = VersionType_1.VersionType.Patch;
|
||||
}
|
||||
else {
|
||||
type = VersionType_1.VersionType.None;
|
||||
}
|
||||
if (this.enablePrereleaseMode && major === 0) {
|
||||
switch (type) {
|
||||
case VersionType_1.VersionType.Major:
|
||||
case VersionType_1.VersionType.Minor:
|
||||
minor += 1;
|
||||
patch = 0;
|
||||
increment = 0;
|
||||
break;
|
||||
case VersionType_1.VersionType.Patch:
|
||||
patch += 1;
|
||||
increment = 0;
|
||||
break;
|
||||
default:
|
||||
increment++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (type) {
|
||||
case VersionType_1.VersionType.Major:
|
||||
major += 1;
|
||||
minor = 0;
|
||||
patch = 0;
|
||||
increment = 0;
|
||||
break;
|
||||
case VersionType_1.VersionType.Minor:
|
||||
minor += 1;
|
||||
patch = 0;
|
||||
break;
|
||||
case VersionType_1.VersionType.Patch:
|
||||
patch += 1;
|
||||
increment = 0;
|
||||
break;
|
||||
default:
|
||||
increment++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new VersionClassification_1.VersionClassification(type, increment, true, major, minor, patch);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.BumpAlwaysVersionClassifier = BumpAlwaysVersionClassifier;
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CommitInfo = void 0;
|
||||
/** Represents information about a commit */
|
||||
class CommitInfo {
|
||||
/**
|
||||
* Creates a new commit information instance
|
||||
* @param hash - The hash of the commit
|
||||
* @param subject - The subject of the commit message
|
||||
* @param body - The body of the commit message
|
||||
* @param author - The author's name
|
||||
* @param authorEmail - The author's email
|
||||
* @param authorDate - The date the commit was authored
|
||||
* @param committer - The committer's name
|
||||
* @param committerEmail - The committer's email
|
||||
* @param committerDate - The date the commit was committed
|
||||
* @param tags - List of any tags associated with this commit
|
||||
*/
|
||||
constructor(hash, subject, body, author, authorEmail, authorDate, committer, committerEmail, committerDate, tags) {
|
||||
this.hash = hash;
|
||||
this.subject = subject;
|
||||
this.body = body;
|
||||
this.author = author;
|
||||
this.authorEmail = authorEmail;
|
||||
this.authorDate = authorDate;
|
||||
this.committer = committer;
|
||||
this.committerEmail = committerEmail;
|
||||
this.committerDate = committerDate;
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
exports.CommitInfo = CommitInfo;
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CommitInfoSet = void 0;
|
||||
/** Represents information about a set of commits */
|
||||
class CommitInfoSet {
|
||||
constructor(changed, commits) {
|
||||
this.changed = changed;
|
||||
this.commits = commits;
|
||||
}
|
||||
}
|
||||
exports.CommitInfoSet = CommitInfoSet;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultCommitsProvider = void 0;
|
||||
const CommandRunner_1 = require("../CommandRunner");
|
||||
const CommitInfo_1 = require("./CommitInfo");
|
||||
const CommitInfoSet_1 = require("./CommitInfoSet");
|
||||
class DefaultCommitsProvider {
|
||||
constructor(config) {
|
||||
this.changePath = config.changePath;
|
||||
}
|
||||
GetCommitsAsync(startHash, endHash) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const logSplitter = `@@@START_RECORD`;
|
||||
const formatPlaceholders = Object.entries({
|
||||
hash: '%H',
|
||||
subject: '%s',
|
||||
body: '%b',
|
||||
author: '%an',
|
||||
authorEmail: '%ae',
|
||||
authorDate: '%aI',
|
||||
committer: '%cn',
|
||||
committerEmail: '%ce',
|
||||
committerDate: '%cI',
|
||||
tags: '%d'
|
||||
});
|
||||
const pretty = logSplitter + '%n' + formatPlaceholders
|
||||
.map(x => `@@@${x[0]}%n${x[1]}`)
|
||||
.join('%n');
|
||||
var logCommand = `git log --pretty="${pretty}" --author-date-order ${(startHash === '' ? endHash : `${startHash}..${endHash}`)}`;
|
||||
if (this.changePath !== '') {
|
||||
logCommand += ` -- ${this.changePath}`;
|
||||
}
|
||||
const log = yield (0, CommandRunner_1.cmd)(logCommand);
|
||||
const entries = log
|
||||
.split(logSplitter)
|
||||
.slice(1);
|
||||
const commits = entries.map(entry => {
|
||||
const fields = entry
|
||||
.split(`@@@`)
|
||||
.slice(1)
|
||||
.reduce((acc, value) => {
|
||||
const firstLine = value.indexOf('\n');
|
||||
const key = value.substring(0, firstLine);
|
||||
acc[key] = value.substring(firstLine + 1).trim();
|
||||
return acc;
|
||||
}, {});
|
||||
const tags = fields.tags
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.startsWith('tags: '))
|
||||
.map((v) => v.substring(5).trim());
|
||||
return new CommitInfo_1.CommitInfo(fields.hash, fields.subject, fields.body, fields.author, fields.authorEmail, new Date(fields.authorDate), fields.committer, fields.committerEmail, new Date(fields.committerDate), tags);
|
||||
});
|
||||
// check for changes
|
||||
let changed = true;
|
||||
if (this.changePath !== '') {
|
||||
if (startHash === '') {
|
||||
const changedFiles = yield (0, CommandRunner_1.cmd)(`git log --name-only --oneline ${endHash} -- ${this.changePath}`);
|
||||
changed = changedFiles.length > 0;
|
||||
}
|
||||
else {
|
||||
const changedFiles = yield (0, CommandRunner_1.cmd)(`git diff --name-only ${startHash}..${endHash} -- ${this.changePath}`);
|
||||
changed = changedFiles.length > 0;
|
||||
}
|
||||
}
|
||||
return new CommitInfoSet_1.CommitInfoSet(changed, commits);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultCommitsProvider = DefaultCommitsProvider;
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultCurrentCommitResolver = void 0;
|
||||
const CommandRunner_1 = require("../CommandRunner");
|
||||
class DefaultCurrentCommitResolver {
|
||||
constructor(config) {
|
||||
this.branch = config.branch;
|
||||
}
|
||||
ResolveAsync() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (this.branch === 'HEAD') {
|
||||
return (yield (0, CommandRunner_1.cmd)('git', 'rev-parse', 'HEAD')).trim();
|
||||
}
|
||||
return this.branch;
|
||||
});
|
||||
}
|
||||
IsEmptyRepoAsync() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let lastCommitAll = (yield (0, CommandRunner_1.cmd)('git', 'rev-list', '-n1', '--all')).trim();
|
||||
return lastCommitAll === '';
|
||||
});
|
||||
}
|
||||
ResolveBranchNameAsync() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const branchName = this.branch == 'HEAD' ? yield (0, CommandRunner_1.cmd)('git', 'rev-parse', '--abbrev-ref', 'HEAD') : this.branch;
|
||||
return branchName.trim();
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultCurrentCommitResolver = DefaultCurrentCommitResolver;
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultLastReleaseResolver = void 0;
|
||||
const CommandRunner_1 = require("../CommandRunner");
|
||||
const ReleaseInformation_1 = require("./ReleaseInformation");
|
||||
const core = __importStar(require("@actions/core"));
|
||||
class DefaultLastReleaseResolver {
|
||||
constructor(config) {
|
||||
this.changePath = config.changePath;
|
||||
this.useBranches = config.useBranches;
|
||||
}
|
||||
ResolveAsync(current, tagFormatter) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const releasePattern = tagFormatter.GetPattern();
|
||||
let currentTag = (yield (0, CommandRunner_1.cmd)(`git tag --points-at ${current} ${releasePattern}`)).trim();
|
||||
currentTag = tagFormatter.IsValid(currentTag) ? currentTag : '';
|
||||
const isTagged = currentTag !== '';
|
||||
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
let tagsCount = 0;
|
||||
let tag = '';
|
||||
try {
|
||||
const refPrefixPattern = this.useBranches ? 'refs/heads/' : 'refs/tags/';
|
||||
if (!!currentTag) {
|
||||
// If we already have the current branch tagged, we are checking for the previous one
|
||||
// so that we will have an accurate increment (assuming the new tag is the expected one)
|
||||
const command = `git for-each-ref --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
|
||||
const tags = (yield (0, CommandRunner_1.cmd)(command)).split('\n');
|
||||
tagsCount = tags.length;
|
||||
tag = tags
|
||||
.find(t => tagFormatter.IsValid(t) && t !== currentTag) || '';
|
||||
}
|
||||
else {
|
||||
const command = `git for-each-ref --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
|
||||
const tags = (yield (0, CommandRunner_1.cmd)(command)).split('\n');
|
||||
tagsCount = tags.length;
|
||||
tag = tags
|
||||
.find(t => tagFormatter.IsValid(t)) || '';
|
||||
}
|
||||
tag = tag.trim();
|
||||
}
|
||||
catch (err) {
|
||||
tag = '';
|
||||
}
|
||||
if (tag === '') {
|
||||
if ((yield (0, CommandRunner_1.cmd)('git', 'remote')) !== '') {
|
||||
// Since there is no remote, we assume that there are no other tags to pull. In
|
||||
// practice this isn't likely to happen, but it keeps the test output from being
|
||||
// polluted with a bunch of warnings.
|
||||
if (tagsCount > 0) {
|
||||
core.warning(`None of the ${tagsCount} tags(s) found were valid version tags for the present configuration. If this is unexpected, check to ensure that the configuration is correct and matches the tag format you are using.`);
|
||||
}
|
||||
else {
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote.');
|
||||
}
|
||||
}
|
||||
const [major, minor, patch] = tagFormatter.Parse('');
|
||||
// no release tags yet, use the initial commit as the root
|
||||
return new ReleaseInformation_1.ReleaseInformation(major, minor, patch, '', currentMajor, currentMinor, currentPatch, isTagged);
|
||||
}
|
||||
// parse the version tag
|
||||
const [major, minor, patch] = tagFormatter.Parse(tag);
|
||||
const root = yield (0, CommandRunner_1.cmd)('git', `merge-base`, tag, current);
|
||||
return new ReleaseInformation_1.ReleaseInformation(major, minor, patch, root.trim(), currentMajor, currentMinor, currentPatch, isTagged);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultLastReleaseResolver = DefaultLastReleaseResolver;
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultVersionClassifier = void 0;
|
||||
const VersionClassification_1 = require("./VersionClassification");
|
||||
const VersionType_1 = require("./VersionType");
|
||||
class DefaultVersionClassifier {
|
||||
constructor(config) {
|
||||
const searchBody = config.searchCommitBody;
|
||||
this.majorPattern = this.parsePattern(config.majorPattern, config.majorFlags, searchBody);
|
||||
this.minorPattern = this.parsePattern(config.minorPattern, config.minorFlags, searchBody);
|
||||
this.enablePrereleaseMode = config.enablePrereleaseMode;
|
||||
}
|
||||
parsePattern(pattern, flags, searchBody) {
|
||||
if (/^\/.+\/[i]*$/.test(pattern)) {
|
||||
const regexEnd = pattern.lastIndexOf('/');
|
||||
const parsedFlags = pattern.slice(pattern.lastIndexOf('/') + 1);
|
||||
const regex = new RegExp(pattern.slice(1, regexEnd), parsedFlags || flags);
|
||||
return searchBody ?
|
||||
(commit) => regex.test(commit.subject) || regex.test(commit.body) :
|
||||
(commit) => regex.test(commit.subject);
|
||||
}
|
||||
else {
|
||||
const matchString = pattern;
|
||||
return searchBody ?
|
||||
(commit) => commit.subject.includes(matchString) || commit.body.includes(matchString) :
|
||||
(commit) => commit.subject.includes(matchString);
|
||||
}
|
||||
}
|
||||
getNextVersion(current, type) {
|
||||
if (this.enablePrereleaseMode && current.major === 0) {
|
||||
switch (type) {
|
||||
case VersionType_1.VersionType.Major:
|
||||
return { major: current.major, minor: current.minor + 1, patch: 0 };
|
||||
case VersionType_1.VersionType.Minor:
|
||||
case VersionType_1.VersionType.Patch:
|
||||
return { major: current.major, minor: current.minor, patch: current.patch + 1 };
|
||||
case VersionType_1.VersionType.None:
|
||||
return { major: current.major, minor: current.minor, patch: current.patch };
|
||||
default:
|
||||
throw new Error(`Unknown change type: ${type}`);
|
||||
}
|
||||
}
|
||||
switch (type) {
|
||||
case VersionType_1.VersionType.Major:
|
||||
return { major: current.major + 1, minor: 0, patch: 0 };
|
||||
case VersionType_1.VersionType.Minor:
|
||||
return { major: current.major, minor: current.minor + 1, patch: 0 };
|
||||
case VersionType_1.VersionType.Patch:
|
||||
return { major: current.major, minor: current.minor, patch: current.patch + 1 };
|
||||
case VersionType_1.VersionType.None:
|
||||
return { major: current.major, minor: current.minor, patch: current.patch };
|
||||
default:
|
||||
throw new Error(`Unknown change type: ${type}`);
|
||||
}
|
||||
}
|
||||
resolveCommitType(commitsSet) {
|
||||
if (commitsSet.commits.length === 0) {
|
||||
return { type: VersionType_1.VersionType.None, increment: 0, changed: commitsSet.changed };
|
||||
}
|
||||
const commits = commitsSet.commits.reverse();
|
||||
let index = 1;
|
||||
for (let commit of commits) {
|
||||
if (this.majorPattern(commit)) {
|
||||
return { type: VersionType_1.VersionType.Major, increment: commits.length - index, changed: commitsSet.changed };
|
||||
}
|
||||
index++;
|
||||
}
|
||||
index = 1;
|
||||
for (let commit of commits) {
|
||||
if (this.minorPattern(commit)) {
|
||||
return { type: VersionType_1.VersionType.Minor, increment: commits.length - index, changed: commitsSet.changed };
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return { type: VersionType_1.VersionType.Patch, increment: commitsSet.commits.length - 1, changed: true };
|
||||
}
|
||||
ClassifyAsync(lastRelease, commitSet) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const { type, increment, changed } = this.resolveCommitType(commitSet);
|
||||
const { major, minor, patch } = this.getNextVersion(lastRelease, type);
|
||||
if (lastRelease.currentPatch !== null) {
|
||||
// If the current commit is tagged, we must use that version. Here we check if the version we have resolved from the
|
||||
// previous commits is the same as the current version. If it is, we will use the increment value, otherwise we reset
|
||||
// to zero. For example:
|
||||
// - commit 1 - v1.0.0+0
|
||||
// - commit 2 - v1.0.0+1
|
||||
// - commit 3 was tagged v2.0.0 - v2.0.0+0
|
||||
// - commit 4 - v2.0.1+0
|
||||
const versionsMatch = lastRelease.currentMajor === major && lastRelease.currentMinor === minor && lastRelease.currentPatch === patch;
|
||||
const currentIncrement = versionsMatch ? increment : 0;
|
||||
return new VersionClassification_1.VersionClassification(VersionType_1.VersionType.None, currentIncrement, false, lastRelease.currentMajor, lastRelease.currentMinor, lastRelease.currentPatch);
|
||||
}
|
||||
return new VersionClassification_1.VersionClassification(type, increment, changed, major, minor, patch);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.DefaultVersionClassifier = DefaultVersionClassifier;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ReleaseInformation = void 0;
|
||||
// Finds the hash of the last commit
|
||||
class ReleaseInformation {
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param major - the major version number
|
||||
* @param minor - the minor version number
|
||||
* @param patch - the patch version number
|
||||
* @param hash - the hash of commit of the last release
|
||||
* @param currentMajor - the major version number from the current commit
|
||||
* @param currentMinor - the minor version number from the current commit
|
||||
* @param currentPatch - the patch version number from the current commit
|
||||
* @param isTagged - whether the current commit is tagged with a version
|
||||
*/
|
||||
constructor(major, minor, patch, hash, currentMajor, currentMinor, currentPatch, isTagged) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.hash = hash;
|
||||
this.currentMajor = currentMajor;
|
||||
this.currentMinor = currentMinor;
|
||||
this.currentPatch = currentPatch;
|
||||
this.isTagged = isTagged;
|
||||
}
|
||||
}
|
||||
exports.ReleaseInformation = ReleaseInformation;
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TagLastReleaseResolver = void 0;
|
||||
const CommandRunner_1 = require("../CommandRunner");
|
||||
const ReleaseInformation_1 = require("./ReleaseInformation");
|
||||
const core = __importStar(require("@actions/core"));
|
||||
class TagLastReleaseResolver {
|
||||
constructor(config) {
|
||||
this.changePath = config.changePath;
|
||||
}
|
||||
ResolveAsync(current, tagFormatter) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const releasePattern = tagFormatter.GetPattern();
|
||||
let currentTag = (yield (0, CommandRunner_1.cmd)(`git tag --points-at ${current} ${releasePattern}`)).trim();
|
||||
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
let tag = '';
|
||||
try {
|
||||
if (!!currentTag) {
|
||||
// If we already have the current branch tagged, we are checking for the previous one
|
||||
// so that we will have an accurate increment (assuming the new tag is the expected one)
|
||||
const command = `git for-each-ref --count=2 --sort=-v:*refname --format=%(refname:short) --merged=${current} refs/tags/${releasePattern}`;
|
||||
tag = yield (0, CommandRunner_1.cmd)(command);
|
||||
tag = tag.split('\n').at(-1) || '';
|
||||
}
|
||||
else {
|
||||
const command = `git for-each-ref --count=1 --sort=-v:*refname --format=%(refname:short) --merged=${current} refs/tags/${releasePattern}`;
|
||||
tag = yield (0, CommandRunner_1.cmd)(command);
|
||||
}
|
||||
tag = tag.trim();
|
||||
}
|
||||
catch (err) {
|
||||
tag = '';
|
||||
}
|
||||
if (tag === '') {
|
||||
if ((yield (0, CommandRunner_1.cmd)('git', 'remote')) !== '') {
|
||||
// Since there is no remote, we assume that there are no other tags to pull. In
|
||||
// practice this isn't likely to happen, but it keeps the test output from being
|
||||
// polluted with a bunch of warnings.
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote.');
|
||||
}
|
||||
// no release tags yet, use the initial commit as the root
|
||||
return new ReleaseInformation_1.ReleaseInformation(0, 0, 0, '', currentMajor, currentMinor, currentPatch);
|
||||
}
|
||||
// parse the version tag
|
||||
const [major, minor, patch] = tagFormatter.Parse(tag);
|
||||
const root = yield (0, CommandRunner_1.cmd)('git', `merge-base`, tag, current);
|
||||
return new ReleaseInformation_1.ReleaseInformation(major, minor, patch, root.trim(), currentMajor, currentMinor, currentPatch);
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.TagLastReleaseResolver = TagLastReleaseResolver;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.UserInfo = void 0;
|
||||
/** Represents information about a user (e.g. committer, author, tagger) */
|
||||
class UserInfo {
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param name - User's name
|
||||
* @param email - User's email
|
||||
* @param commits - Number of commits in the scope evaluated
|
||||
*/
|
||||
constructor(name, email, commits) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
this.commits = commits;
|
||||
}
|
||||
}
|
||||
exports.UserInfo = UserInfo;
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VersionClassification = void 0;
|
||||
/** The result of a version classification */
|
||||
class VersionClassification {
|
||||
/**
|
||||
* Creates a new version classification result instance
|
||||
* @param type - The type of change the current range represents
|
||||
* @param increment - The number of commits which have this version, usually zero-based
|
||||
* @param changed - True if the version has changed, false otherwise
|
||||
* @param major - The major version number
|
||||
* @param minor - The minor version number
|
||||
* @param patch - The patch version number
|
||||
*/
|
||||
constructor(type, increment, changed, major, minor, patch) {
|
||||
this.type = type;
|
||||
this.increment = increment;
|
||||
this.changed = changed;
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
}
|
||||
}
|
||||
exports.VersionClassification = VersionClassification;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VersionInformation = void 0;
|
||||
/**
|
||||
* Represents the "resolved" information about a version change, serves
|
||||
* as the input to formatters that produce the final output
|
||||
*/
|
||||
class VersionInformation {
|
||||
/**
|
||||
* Creates a new version information instance
|
||||
* @param major - The major version number
|
||||
* @param minor - The minor version number
|
||||
* @param patch - The patch version number
|
||||
* @param increment - The number of commits for this version
|
||||
* @param type - The type of change the current range represents
|
||||
* @param commits - The list of commits for this version
|
||||
* @param changed - True if the version has changed, false otherwise
|
||||
* @param isTagged - True if the current commit is a version-tagged commit
|
||||
*/
|
||||
constructor(major, minor, patch, increment, type, commits, changed, isTagged) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
this.increment = increment;
|
||||
this.type = type;
|
||||
this.commits = commits;
|
||||
this.changed = changed;
|
||||
this.isTagged = isTagged;
|
||||
}
|
||||
}
|
||||
exports.VersionInformation = VersionInformation;
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VersionType = void 0;
|
||||
/** Indicates the type of change a particular version change represents */
|
||||
var VersionType;
|
||||
(function (VersionType) {
|
||||
/** Indicates a major version change */
|
||||
VersionType["Major"] = "Major";
|
||||
/** Indicates a minor version change */
|
||||
VersionType["Minor"] = "Minor";
|
||||
/** Indicates a patch version change */
|
||||
VersionType["Patch"] = "Patch";
|
||||
/** Indicates no change--generally this means that the current commit is already tagged with a version */
|
||||
VersionType["None"] = "None";
|
||||
})(VersionType || (exports.VersionType = VersionType = {}));
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.VersionType = exports.VersionInformation = exports.VersionClassification = exports.UserInfo = exports.TagLastReleaseResolver = exports.ReleaseInformation = exports.DefaultVersionClassifier = exports.DefaultCurrentCommitResolver = exports.DefaultCommitsProvider = exports.CommitInfo = void 0;
|
||||
var CommitInfo_1 = require("./CommitInfo");
|
||||
Object.defineProperty(exports, "CommitInfo", { enumerable: true, get: function () { return CommitInfo_1.CommitInfo; } });
|
||||
var DefaultCommitsProvider_1 = require("./DefaultCommitsProvider");
|
||||
Object.defineProperty(exports, "DefaultCommitsProvider", { enumerable: true, get: function () { return DefaultCommitsProvider_1.DefaultCommitsProvider; } });
|
||||
var DefaultCurrentCommitResolver_1 = require("./DefaultCurrentCommitResolver");
|
||||
Object.defineProperty(exports, "DefaultCurrentCommitResolver", { enumerable: true, get: function () { return DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver; } });
|
||||
var DefaultVersionClassifier_1 = require("./DefaultVersionClassifier");
|
||||
Object.defineProperty(exports, "DefaultVersionClassifier", { enumerable: true, get: function () { return DefaultVersionClassifier_1.DefaultVersionClassifier; } });
|
||||
var ReleaseInformation_1 = require("./ReleaseInformation");
|
||||
Object.defineProperty(exports, "ReleaseInformation", { enumerable: true, get: function () { return ReleaseInformation_1.ReleaseInformation; } });
|
||||
var TagLastReleaseResolver_1 = require("./TagLastReleaseResolver");
|
||||
Object.defineProperty(exports, "TagLastReleaseResolver", { enumerable: true, get: function () { return TagLastReleaseResolver_1.TagLastReleaseResolver; } });
|
||||
var UserInfo_1 = require("./UserInfo");
|
||||
Object.defineProperty(exports, "UserInfo", { enumerable: true, get: function () { return UserInfo_1.UserInfo; } });
|
||||
var VersionClassification_1 = require("./VersionClassification");
|
||||
Object.defineProperty(exports, "VersionClassification", { enumerable: true, get: function () { return VersionClassification_1.VersionClassification; } });
|
||||
var VersionInformation_1 = require("./VersionInformation");
|
||||
Object.defineProperty(exports, "VersionInformation", { enumerable: true, get: function () { return VersionInformation_1.VersionInformation; } });
|
||||
var VersionType_1 = require("./VersionType");
|
||||
Object.defineProperty(exports, "VersionType", { enumerable: true, get: function () { return VersionType_1.VersionType; } });
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -10,7 +10,8 @@
|
|||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1"
|
||||
"@actions/exec": "^1.1.1",
|
||||
"commander": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.13",
|
||||
|
|
@ -2518,6 +2519,15 @@
|
|||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz",
|
||||
"integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
|
|
|||
28
package.json
28
package.json
|
|
@ -1,9 +1,21 @@
|
|||
{
|
||||
"name": "semantic-version",
|
||||
"name": "@semanticversion/cli",
|
||||
"version": "0.0.0",
|
||||
"description": "Semantic Version GitHub Action",
|
||||
"description": "Automated semantic versioning based on git history - CLI tool and GitHub Action",
|
||||
"main": "lib/main.js",
|
||||
"bin": {
|
||||
"git-sv": "lib/cli.js",
|
||||
"git-semantic-version": "lib/cli.js"
|
||||
},
|
||||
"files": [
|
||||
"lib/**/*.js",
|
||||
"lib/**/*.d.ts"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "tsc",
|
||||
"format": "prettier --write **/**.ts",
|
||||
"format-check": "prettier --check **/**.ts",
|
||||
|
|
@ -17,9 +29,12 @@
|
|||
"url": "git+https://github.com/paulhatch/semantic-version.git"
|
||||
},
|
||||
"keywords": [
|
||||
"GitHub",
|
||||
"Actions",
|
||||
"JavaScript"
|
||||
"semantic-version",
|
||||
"semver",
|
||||
"git",
|
||||
"versioning",
|
||||
"cli",
|
||||
"github-actions"
|
||||
],
|
||||
"author": "Paul Hatcherian",
|
||||
"license": "MIT",
|
||||
|
|
@ -29,7 +44,8 @@
|
|||
"homepage": "https://github.com/paulhatch/semantic-version#readme",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.1",
|
||||
"@actions/exec": "^1.1.1"
|
||||
"@actions/exec": "^1.1.1",
|
||||
"commander": "^14.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.13",
|
||||
|
|
|
|||
|
|
@ -1,39 +1,39 @@
|
|||
/** Represents the input configuration for the semantic-version action */
|
||||
export class ActionConfig {
|
||||
/** Set to specify a specific branch, default is the current HEAD */
|
||||
public branch: string = "HEAD";
|
||||
/** The prefix to use to identify tags */
|
||||
public tagPrefix: string = "v";
|
||||
/** (Deprecated) Use branches instead of tags */
|
||||
public useBranches: boolean = false;
|
||||
/** If true, the branch will be used to select the maximum version. */
|
||||
public versionFromBranch: string | boolean = false;
|
||||
/** A string which, if present in a git commit, indicates that a change represents a major (breaking) change. Wrap with '/' to match using a regular expression. */
|
||||
public majorPattern: string = "/!:|BREAKING CHANGE:/";
|
||||
/** A string which indicates the flags used by the `majorPattern` regular expression. */
|
||||
public majorFlags: string = "";
|
||||
/** A string which, if present in a git commit, indicates that a change represents a minor (feature) change. Wrap with '/' to match using a regular expression. */
|
||||
public minorPattern: string = "/feat:/";
|
||||
/** A string which indicates the flags used by the `minorPattern` regular expression. */
|
||||
public minorFlags: string = "";
|
||||
/** Pattern to use when formatting output version */
|
||||
public versionFormat: string = '${major}.${minor}.${patch}';
|
||||
/** Path to check for changes. If any changes are detected in the path the 'changed' output will true. Enter multiple paths separated by spaces. */
|
||||
public changePath: string = '';
|
||||
/** Use to create a named sub-version. This value will be appended to tags created for this version. */
|
||||
public namespace: string = "";
|
||||
/** If true, every commit will be treated as a bump to the version. */
|
||||
public bumpEachCommit: boolean = false;
|
||||
/** If true, the body of commits will also be searched for major/minor patterns to determine the version type */
|
||||
public searchCommitBody: boolean = false;
|
||||
/** The output method used to generate list of users, 'csv' or 'json'. Default is 'csv'. */
|
||||
public userFormatType: string = "csv";
|
||||
/** Prevents pre-v1.0.0 version from automatically incrementing the major version. If enabled, when the major version is 0, major releases will be treated as minor and minor as patch. Note that the versionType output is unchanged. */
|
||||
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. */
|
||||
public bumpEachCommitPatchPattern: string = "";
|
||||
/** If enabled, diagnostic information will be added to the action output. */
|
||||
public debug: boolean = false;
|
||||
/** Diagnostics to replay */
|
||||
public replay: string = '';
|
||||
}
|
||||
/** Set to specify a specific branch, default is the current HEAD */
|
||||
public branch: string = "HEAD";
|
||||
/** The prefix to use to identify tags */
|
||||
public tagPrefix: string = "v";
|
||||
/** (Deprecated) Use branches instead of tags */
|
||||
public useBranches: boolean = false;
|
||||
/** If true, the branch will be used to select the maximum version. */
|
||||
public versionFromBranch: string | boolean = false;
|
||||
/** A string which, if present in a git commit, indicates that a change represents a major (breaking) change. Wrap with '/' to match using a regular expression. */
|
||||
public majorPattern: string = "/!:|BREAKING CHANGE:/";
|
||||
/** A string which indicates the flags used by the `majorPattern` regular expression. */
|
||||
public majorFlags: string = "";
|
||||
/** A string which, if present in a git commit, indicates that a change represents a minor (feature) change. Wrap with '/' to match using a regular expression. */
|
||||
public minorPattern: string = "/feat:/";
|
||||
/** A string which indicates the flags used by the `minorPattern` regular expression. */
|
||||
public minorFlags: string = "";
|
||||
/** Pattern to use when formatting output version */
|
||||
public versionFormat: string = "${major}.${minor}.${patch}";
|
||||
/** Path to check for changes. If any changes are detected in the path the 'changed' output will true. Enter multiple paths separated by spaces. */
|
||||
public changePath: string = "";
|
||||
/** Use to create a named sub-version. This value will be appended to tags created for this version. */
|
||||
public namespace: string = "";
|
||||
/** If true, every commit will be treated as a bump to the version. */
|
||||
public bumpEachCommit: boolean = false;
|
||||
/** If true, the body of commits will also be searched for major/minor patterns to determine the version type */
|
||||
public searchCommitBody: boolean = false;
|
||||
/** The output method used to generate list of users, 'csv' or 'json'. Default is 'csv'. */
|
||||
public userFormatType: string = "csv";
|
||||
/** Prevents pre-v1.0.0 version from automatically incrementing the major version. If enabled, when the major version is 0, major releases will be treated as minor and minor as patch. Note that the versionType output is unchanged. */
|
||||
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. */
|
||||
public bumpEachCommitPatchPattern: string = "";
|
||||
/** If enabled, diagnostic information will be added to the action output. */
|
||||
public debug: boolean = false;
|
||||
/** Diagnostics to replay */
|
||||
public replay: string = "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,38 @@
|
|||
// Using require instead of import to support integration testing
|
||||
import * as exec from '@actions/exec';
|
||||
import { DebugManager } from './DebugManager';
|
||||
import * as exec from "@actions/exec";
|
||||
import { DebugManager } from "./DebugManager";
|
||||
|
||||
export const cmd = async (command: string, ...args: any): Promise<string> => {
|
||||
const debugManager = DebugManager.getInstance();
|
||||
|
||||
const debugManager = DebugManager.getInstance();
|
||||
if (debugManager.isReplayMode()) {
|
||||
return debugManager.replayCommand(command, args);
|
||||
}
|
||||
|
||||
if (debugManager.isReplayMode()) {
|
||||
return debugManager.replayCommand(command, args);
|
||||
}
|
||||
let output = "",
|
||||
errors = "";
|
||||
const options = {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data: any) => {
|
||||
output += data.toString();
|
||||
},
|
||||
stderr: (data: any) => {
|
||||
errors += data.toString();
|
||||
},
|
||||
ignoreReturnCode: true,
|
||||
silent: true,
|
||||
},
|
||||
};
|
||||
|
||||
let output = '', errors = '';
|
||||
const options = {
|
||||
silent: true,
|
||||
listeners: {
|
||||
stdout: (data: any) => { output += data.toString(); },
|
||||
stderr: (data: any) => { errors += data.toString(); },
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}
|
||||
};
|
||||
let caughtError: any = null;
|
||||
try {
|
||||
await exec.exec(command, args, options);
|
||||
} catch (err) {
|
||||
caughtError = err;
|
||||
}
|
||||
|
||||
let caughtError: any = null;
|
||||
try {
|
||||
await exec.exec(command, args, options);
|
||||
} catch (err) {
|
||||
caughtError = err;
|
||||
}
|
||||
debugManager.recordCommand(command, args, output, errors, caughtError);
|
||||
|
||||
debugManager.recordCommand(command, args, output, errors, caughtError);
|
||||
|
||||
return output;
|
||||
};
|
||||
return output;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import { CsvUserFormatter } from './formatting/CsvUserFormatter'
|
||||
import { BranchVersioningTagFormatter } from './formatting/BranchVersioningTagFormatter'
|
||||
import { DefaultTagFormatter } from './formatting/DefaultTagFormatter'
|
||||
import { DefaultVersionFormatter } from './formatting/DefaultVersionFormatter'
|
||||
import { JsonUserFormatter } from './formatting/JsonUserFormatter'
|
||||
import { TagFormatter } from './formatting/TagFormatter'
|
||||
import { UserFormatter } from './formatting/UserFormatter'
|
||||
import { VersionFormatter } from './formatting/VersionFormatter'
|
||||
import { CommitsProvider } from './providers/CommitsProvider'
|
||||
import { CurrentCommitResolver } from './providers/CurrentCommitResolver'
|
||||
import { DefaultCommitsProvider } from './providers/DefaultCommitsProvider'
|
||||
import { DefaultCurrentCommitResolver } from './providers/DefaultCurrentCommitResolver'
|
||||
import { DefaultVersionClassifier } from './providers/DefaultVersionClassifier'
|
||||
import { LastReleaseResolver } from './providers/LastReleaseResolver'
|
||||
import { DefaultLastReleaseResolver } from './providers/DefaultLastReleaseResolver'
|
||||
import { VersionClassifier } from './providers/VersionClassifier'
|
||||
import { BumpAlwaysVersionClassifier } from './providers/BumpAlwaysVersionClassifier'
|
||||
import { ActionConfig } from './ActionConfig';
|
||||
import { DebugManager } from './DebugManager';
|
||||
import { CsvUserFormatter } from "./formatting/CsvUserFormatter";
|
||||
import { BranchVersioningTagFormatter } from "./formatting/BranchVersioningTagFormatter";
|
||||
import { DefaultTagFormatter } from "./formatting/DefaultTagFormatter";
|
||||
import { DefaultVersionFormatter } from "./formatting/DefaultVersionFormatter";
|
||||
import { JsonUserFormatter } from "./formatting/JsonUserFormatter";
|
||||
import { TagFormatter } from "./formatting/TagFormatter";
|
||||
import { UserFormatter } from "./formatting/UserFormatter";
|
||||
import { VersionFormatter } from "./formatting/VersionFormatter";
|
||||
import { CommitsProvider } from "./providers/CommitsProvider";
|
||||
import { CurrentCommitResolver } from "./providers/CurrentCommitResolver";
|
||||
import { DefaultCommitsProvider } from "./providers/DefaultCommitsProvider";
|
||||
import { DefaultCurrentCommitResolver } from "./providers/DefaultCurrentCommitResolver";
|
||||
import { DefaultVersionClassifier } from "./providers/DefaultVersionClassifier";
|
||||
import { LastReleaseResolver } from "./providers/LastReleaseResolver";
|
||||
import { DefaultLastReleaseResolver } from "./providers/DefaultLastReleaseResolver";
|
||||
import { VersionClassifier } from "./providers/VersionClassifier";
|
||||
import { BumpAlwaysVersionClassifier } from "./providers/BumpAlwaysVersionClassifier";
|
||||
import { ActionConfig } from "./ActionConfig";
|
||||
import { DebugManager } from "./DebugManager";
|
||||
|
||||
export class ConfigurationProvider {
|
||||
|
||||
constructor(config: ActionConfig) {
|
||||
this.config = config;
|
||||
DebugManager.getInstance().initializeConfig(config);
|
||||
|
|
@ -27,11 +26,17 @@ export class ConfigurationProvider {
|
|||
|
||||
private config: ActionConfig;
|
||||
|
||||
public GetCurrentCommitResolver(): CurrentCommitResolver { return new DefaultCurrentCommitResolver(this.config); }
|
||||
public GetCurrentCommitResolver(): CurrentCommitResolver {
|
||||
return new DefaultCurrentCommitResolver(this.config);
|
||||
}
|
||||
|
||||
public GetLastReleaseResolver(): LastReleaseResolver { return new DefaultLastReleaseResolver(this.config); }
|
||||
public GetLastReleaseResolver(): LastReleaseResolver {
|
||||
return new DefaultLastReleaseResolver(this.config);
|
||||
}
|
||||
|
||||
public GetCommitsProvider(): CommitsProvider { return new DefaultCommitsProvider(this.config); }
|
||||
public GetCommitsProvider(): CommitsProvider {
|
||||
return new DefaultCommitsProvider(this.config);
|
||||
}
|
||||
|
||||
public GetVersionClassifier(): VersionClassifier {
|
||||
if (this.config.bumpEachCommit) {
|
||||
|
|
@ -40,7 +45,9 @@ export class ConfigurationProvider {
|
|||
return new DefaultVersionClassifier(this.config);
|
||||
}
|
||||
|
||||
public GetVersionFormatter(): VersionFormatter { return new DefaultVersionFormatter(this.config); }
|
||||
public GetVersionFormatter(): VersionFormatter {
|
||||
return new DefaultVersionFormatter(this.config);
|
||||
}
|
||||
|
||||
public GetTagFormatter(branchName: string): TagFormatter {
|
||||
if (this.config.versionFromBranch) {
|
||||
|
|
@ -51,10 +58,14 @@ export class ConfigurationProvider {
|
|||
|
||||
public GetUserFormatter(): UserFormatter {
|
||||
switch (this.config.userFormatType) {
|
||||
case 'json': return new JsonUserFormatter(this.config);
|
||||
case 'csv': return new CsvUserFormatter(this.config);
|
||||
case "json":
|
||||
return new JsonUserFormatter(this.config);
|
||||
case "csv":
|
||||
return new CsvUserFormatter(this.config);
|
||||
default:
|
||||
throw new Error(`Unknown user format type: ${this.config.userFormatType}, supported types: json, csv`);
|
||||
throw new Error(
|
||||
`Unknown user format type: ${this.config.userFormatType}, supported types: json, csv`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,115 +1,134 @@
|
|||
import { ActionConfig } from "./ActionConfig";
|
||||
|
||||
|
||||
/** Utility class for managing debug mode and diagnostic information */
|
||||
export class DebugManager {
|
||||
private constructor() { }
|
||||
private constructor() {}
|
||||
|
||||
private static instance: DebugManager;
|
||||
/** Returns the singleton instance of the DebugManager */
|
||||
public static getInstance(): DebugManager {
|
||||
if (!DebugManager.instance) {
|
||||
DebugManager.instance = new DebugManager();
|
||||
}
|
||||
return DebugManager.instance;
|
||||
private static instance: DebugManager;
|
||||
/** Returns the singleton instance of the DebugManager */
|
||||
public static getInstance(): DebugManager {
|
||||
if (!DebugManager.instance) {
|
||||
DebugManager.instance = new DebugManager();
|
||||
}
|
||||
return DebugManager.instance;
|
||||
}
|
||||
|
||||
/** Clears the singleton instance of the DebugManager (used for testing) */
|
||||
public static clearState() {
|
||||
DebugManager.instance = new DebugManager();
|
||||
}
|
||||
|
||||
private debugEnabled: boolean = false;
|
||||
private replayMode: boolean = false;
|
||||
private diagnosticInfo: DiagnosticInfo | null = null;
|
||||
|
||||
/** Returns true if debug mode is enabled */
|
||||
public isDebugEnabled(): boolean {
|
||||
return this.debugEnabled;
|
||||
}
|
||||
|
||||
/** Returns true if replay mode is enabled */
|
||||
public isReplayMode(): boolean {
|
||||
return this.replayMode;
|
||||
}
|
||||
|
||||
initializeConfig(config: ActionConfig) {
|
||||
if (config.debug) {
|
||||
this.setDebugEnabled(true);
|
||||
} else if (config.replay.length > 0) {
|
||||
this.replayFromDiagnostics(config.replay);
|
||||
}
|
||||
}
|
||||
|
||||
/** Enables or disables debug mode, also clears any existing diagnostics info */
|
||||
public setDebugEnabled(enableDebug: boolean = true): void {
|
||||
this.debugEnabled = enableDebug;
|
||||
this.replayMode = false;
|
||||
this.diagnosticInfo = new DiagnosticInfo();
|
||||
}
|
||||
|
||||
/** Enables replay mode and loads the diagnostic information from the specified string */
|
||||
public replayFromDiagnostics(diagnostics: string): void {
|
||||
this.debugEnabled = false;
|
||||
this.replayMode = true;
|
||||
this.diagnosticInfo = JSON.parse(diagnostics);
|
||||
}
|
||||
|
||||
/** Returns a JSON string containing the diagnostic information for this run */
|
||||
public getDebugOutput(emptyRepo: boolean = false): string {
|
||||
return this.isDebugEnabled() ? JSON.stringify(this.diagnosticInfo) : "";
|
||||
}
|
||||
|
||||
/** Records a command and its output for diagnostic purposes */
|
||||
public recordCommand(
|
||||
command: string,
|
||||
args: any[],
|
||||
output: string,
|
||||
stderr: string,
|
||||
error: any,
|
||||
): void {
|
||||
if (this.isDebugEnabled()) {
|
||||
this.diagnosticInfo?.recordCommand(command, args, output, stderr, error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Replays the specified command and returns the output */
|
||||
public replayCommand(command: string, args: any[]): string {
|
||||
if (this.diagnosticInfo === null) {
|
||||
throw new Error("No diagnostic information available for replay");
|
||||
}
|
||||
|
||||
/** Clears the singleton instance of the DebugManager (used for testing) */
|
||||
public static clearState() {
|
||||
DebugManager.instance = new DebugManager();
|
||||
const commandResult = this.diagnosticInfo.commands.find(
|
||||
(c) =>
|
||||
c.command === command &&
|
||||
JSON.stringify(c.args) === JSON.stringify(args),
|
||||
);
|
||||
if (!commandResult) {
|
||||
throw new Error(`No result found in diagnostic for command "${command}"`);
|
||||
}
|
||||
|
||||
|
||||
private debugEnabled: boolean = false;
|
||||
private replayMode: boolean = false;
|
||||
private diagnosticInfo: DiagnosticInfo | null = null;
|
||||
|
||||
/** Returns true if debug mode is enabled */
|
||||
public isDebugEnabled(): boolean {
|
||||
return this.debugEnabled;
|
||||
if (commandResult.error) {
|
||||
throw commandResult.error;
|
||||
}
|
||||
|
||||
/** Returns true if replay mode is enabled */
|
||||
public isReplayMode(): boolean {
|
||||
return this.replayMode;
|
||||
}
|
||||
|
||||
initializeConfig(config: ActionConfig) {
|
||||
if (config.debug) {
|
||||
this.setDebugEnabled(true);
|
||||
} else if (config.replay.length > 0) {
|
||||
this.replayFromDiagnostics(config.replay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Enables or disables debug mode, also clears any existing diagnostics info */
|
||||
public setDebugEnabled(enableDebug: boolean = true): void {
|
||||
this.debugEnabled = enableDebug;
|
||||
this.replayMode = false;
|
||||
this.diagnosticInfo = new DiagnosticInfo();
|
||||
};
|
||||
|
||||
/** Enables replay mode and loads the diagnostic information from the specified string */
|
||||
public replayFromDiagnostics(diagnostics: string): void {
|
||||
this.debugEnabled = false
|
||||
this.replayMode = true;
|
||||
this.diagnosticInfo = JSON.parse(diagnostics);
|
||||
}
|
||||
|
||||
/** Returns a JSON string containing the diagnostic information for this run */
|
||||
public getDebugOutput(emptyRepo: boolean = false): string {
|
||||
return this.isDebugEnabled() ? JSON.stringify(this.diagnosticInfo) : '';
|
||||
}
|
||||
|
||||
/** Records a command and its output for diagnostic purposes */
|
||||
public recordCommand(command: string, args: any[], output: string, stderr: string, error: any): void {
|
||||
if (this.isDebugEnabled()) {
|
||||
this.diagnosticInfo?.recordCommand(command, args, output, stderr, error);
|
||||
}
|
||||
}
|
||||
|
||||
/** Replays the specified command and returns the output */
|
||||
public replayCommand(command: string, args: any[]): string {
|
||||
if (this.diagnosticInfo === null) {
|
||||
throw new Error('No diagnostic information available for replay');
|
||||
}
|
||||
|
||||
const commandResult = this.diagnosticInfo.commands.find(c => c.command === command && JSON.stringify(c.args) === JSON.stringify(args));
|
||||
if (!commandResult) {
|
||||
throw new Error(`No result found in diagnostic for command "${command}"`);
|
||||
}
|
||||
if (commandResult.error) {
|
||||
throw commandResult.error;
|
||||
}
|
||||
if (commandResult.stderr) {
|
||||
console.error(commandResult.stderr);
|
||||
}
|
||||
return commandResult.output;
|
||||
if (commandResult.stderr) {
|
||||
console.error(commandResult.stderr);
|
||||
}
|
||||
return commandResult.output;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents a CLI command result */
|
||||
class CommandResult {
|
||||
public command: string;
|
||||
public args: any[];
|
||||
public output: string;
|
||||
public stderr: string;
|
||||
public error: any;
|
||||
public constructor(command: string, args: any[], output: string, stderr: string, error: any) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
this.output = output;
|
||||
this.stderr = stderr;
|
||||
this.error = error;
|
||||
}
|
||||
public command: string;
|
||||
public args: any[];
|
||||
public output: string;
|
||||
public stderr: string;
|
||||
public error: any;
|
||||
public constructor(
|
||||
command: string,
|
||||
args: any[],
|
||||
output: string,
|
||||
stderr: string,
|
||||
error: any,
|
||||
) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
this.output = output;
|
||||
this.stderr = stderr;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
/** Represents the result of the commands executed for a run */
|
||||
class DiagnosticInfo {
|
||||
public commands: CommandResult[] = [];
|
||||
public empty: boolean = false;
|
||||
public recordCommand(command: string, args: any[], output: string, stderr: string, error: any): void {
|
||||
this.commands.push(new CommandResult(command, args, output, stderr, error));
|
||||
}
|
||||
}
|
||||
public commands: CommandResult[] = [];
|
||||
public empty: boolean = false;
|
||||
public recordCommand(
|
||||
command: string,
|
||||
args: any[],
|
||||
output: string,
|
||||
stderr: string,
|
||||
error: any,
|
||||
): void {
|
||||
this.commands.push(new CommandResult(command, args, output, stderr, error));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class VersionResult {
|
|||
* @param changed - True if the version was changed, otherwise false
|
||||
* @param isTagged - True if the commit had a tag that matched the `versionTag` format
|
||||
* @param authors - Authors formatted according to the format mode (e.g. JSON, CSV, YAML, etc.)
|
||||
* @param currentCommit - The current commit hash
|
||||
* @param currentCommit - The current commit hash
|
||||
* @param previousCommit - The previous commit hash
|
||||
* @param previousVersion - The previous version
|
||||
* @param debugOutput - Diagnostic information, if debug is enabled
|
||||
|
|
@ -34,5 +34,6 @@ export class VersionResult {
|
|||
public currentCommit: string,
|
||||
public previousCommit: string,
|
||||
public previousVersion: string,
|
||||
public debugOutput: string) { }
|
||||
public debugOutput: string,
|
||||
) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,37 @@
|
|||
import { ConfigurationProvider } from './ConfigurationProvider';
|
||||
import { VersionResult } from './VersionResult';
|
||||
import { VersionType } from './providers/VersionType';
|
||||
import { UserInfo } from './providers/UserInfo';
|
||||
import { VersionInformation } from './providers/VersionInformation';
|
||||
import { DebugManager } from './DebugManager';
|
||||
import { ConfigurationProvider } from "./ConfigurationProvider";
|
||||
import { VersionResult } from "./VersionResult";
|
||||
import { VersionType } from "./providers/VersionType";
|
||||
import { UserInfo } from "./providers/UserInfo";
|
||||
import { VersionInformation } from "./providers/VersionInformation";
|
||||
import { DebugManager } from "./DebugManager";
|
||||
|
||||
export async function runAction(configurationProvider: ConfigurationProvider): Promise<VersionResult> {
|
||||
|
||||
const currentCommitResolver = configurationProvider.GetCurrentCommitResolver();
|
||||
export async function runAction(
|
||||
configurationProvider: ConfigurationProvider,
|
||||
): Promise<VersionResult> {
|
||||
const currentCommitResolver =
|
||||
configurationProvider.GetCurrentCommitResolver();
|
||||
const lastReleaseResolver = configurationProvider.GetLastReleaseResolver();
|
||||
const commitsProvider = configurationProvider.GetCommitsProvider();
|
||||
const versionClassifier = configurationProvider.GetVersionClassifier();
|
||||
const versionFormatter = configurationProvider.GetVersionFormatter();
|
||||
const tagFormatter = configurationProvider.GetTagFormatter(await currentCommitResolver.ResolveBranchNameAsync());
|
||||
const tagFormatter = configurationProvider.GetTagFormatter(
|
||||
await currentCommitResolver.ResolveBranchNameAsync(),
|
||||
);
|
||||
const userFormatter = configurationProvider.GetUserFormatter();
|
||||
|
||||
const debugManager = DebugManager.getInstance();
|
||||
|
||||
if (await currentCommitResolver.IsEmptyRepoAsync()) {
|
||||
|
||||
const versionInfo = new VersionInformation(0, 0, 0, 0, VersionType.None, [], false, false);
|
||||
const versionInfo = new VersionInformation(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
VersionType.None,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
);
|
||||
return new VersionResult(
|
||||
versionInfo.major,
|
||||
versionInfo.minor,
|
||||
|
|
@ -30,34 +42,51 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
|
|||
tagFormatter.Format(versionInfo),
|
||||
versionInfo.changed,
|
||||
versionInfo.isTagged,
|
||||
userFormatter.Format('author', []),
|
||||
'',
|
||||
'',
|
||||
tagFormatter.Parse(tagFormatter.Format(versionInfo)).join('.'),
|
||||
debugManager.getDebugOutput(true)
|
||||
userFormatter.Format("author", []),
|
||||
"",
|
||||
"",
|
||||
tagFormatter.Parse(tagFormatter.Format(versionInfo)).join("."),
|
||||
debugManager.getDebugOutput(true),
|
||||
);
|
||||
}
|
||||
|
||||
const currentCommit = await currentCommitResolver.ResolveAsync();
|
||||
const lastRelease = await lastReleaseResolver.ResolveAsync(currentCommit, tagFormatter);
|
||||
const commitSet = await commitsProvider.GetCommitsAsync(lastRelease.hash, currentCommit);
|
||||
const classification = await versionClassifier.ClassifyAsync(lastRelease, commitSet);
|
||||
const lastRelease = await lastReleaseResolver.ResolveAsync(
|
||||
currentCommit,
|
||||
tagFormatter,
|
||||
);
|
||||
const commitSet = await commitsProvider.GetCommitsAsync(
|
||||
lastRelease.hash,
|
||||
currentCommit,
|
||||
);
|
||||
const classification = await versionClassifier.ClassifyAsync(
|
||||
lastRelease,
|
||||
commitSet,
|
||||
);
|
||||
|
||||
const { isTagged } = lastRelease;
|
||||
const { major, minor, patch, increment, type, changed } = classification;
|
||||
|
||||
// At this point all necessary data has been pulled from the database, create
|
||||
// version information to be used by the formatters
|
||||
let versionInfo = new VersionInformation(major, minor, patch, increment, type, commitSet.commits, changed, isTagged);
|
||||
let versionInfo = new VersionInformation(
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
increment,
|
||||
type,
|
||||
commitSet.commits,
|
||||
changed,
|
||||
isTagged,
|
||||
);
|
||||
|
||||
// Group all the authors together, count the number of commits per author
|
||||
const allAuthors = versionInfo.commits
|
||||
.reduce((acc: any, commit) => {
|
||||
const key = `${commit.author} <${commit.authorEmail}>`;
|
||||
acc[key] = acc[key] || { n: commit.author, e: commit.authorEmail, c: 0 };
|
||||
acc[key].c++;
|
||||
return acc;
|
||||
}, {});
|
||||
const allAuthors = versionInfo.commits.reduce((acc: any, commit) => {
|
||||
const key = `${commit.author} <${commit.authorEmail}>`;
|
||||
acc[key] = acc[key] || { n: commit.author, e: commit.authorEmail, c: 0 };
|
||||
acc[key].c++;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const authors = Object.values(allAuthors)
|
||||
.map((u: any) => new UserInfo(u.n, u.e, u.c))
|
||||
|
|
@ -73,10 +102,10 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
|
|||
tagFormatter.Format(versionInfo),
|
||||
versionInfo.changed,
|
||||
versionInfo.isTagged,
|
||||
userFormatter.Format('author', authors),
|
||||
userFormatter.Format("author", authors),
|
||||
currentCommit,
|
||||
lastRelease.hash,
|
||||
`${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`,
|
||||
debugManager.getDebugOutput()
|
||||
debugManager.getDebugOutput(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
146
src/cli.ts
Normal file
146
src/cli.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { Command } from "commander";
|
||||
import { runAction } from "./action";
|
||||
import { ConfigurationProvider } from "./ConfigurationProvider";
|
||||
import { ActionConfig } from "./ActionConfig";
|
||||
import * as process from "process";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.name("semantic-version")
|
||||
.description("Automated semantic versioning for Git repositories")
|
||||
.version(process.env.SEMANTIC_VERSION || "0.0.0-dev")
|
||||
.option(
|
||||
"-B, --branch <branch>",
|
||||
"Specific branch to analyze (default: current HEAD)",
|
||||
"HEAD",
|
||||
)
|
||||
.option("-t, --tag-prefix <prefix>", 'Version tag prefix (e.g., "v")', "v")
|
||||
.option(
|
||||
"-p, --path <path>",
|
||||
"Path to check for changes (multiple paths separated by spaces)",
|
||||
"",
|
||||
)
|
||||
.option("-n, --namespace <namespace>", "Namespace for version tags", "")
|
||||
.option(
|
||||
"-M, --major-pattern <pattern>",
|
||||
"Regex pattern for major version bumps",
|
||||
"/!:|BREAKING CHANGE:/",
|
||||
)
|
||||
.option("--major-flags <flags>", "Flags for major pattern regex", "")
|
||||
.option(
|
||||
"-m, --minor-pattern <pattern>",
|
||||
"Regex pattern for minor version bumps",
|
||||
"/feat:/",
|
||||
)
|
||||
.option("--minor-flags <flags>", "Flags for minor pattern regex", "")
|
||||
.option(
|
||||
"--version-format <format>",
|
||||
"Version format template",
|
||||
"${major}.${minor}.${patch}",
|
||||
)
|
||||
.option("-b, --bump-each-commit", "Bump version for each commit", false)
|
||||
.option(
|
||||
"--bump-each-commit-patch-pattern <pattern>",
|
||||
"Pattern for patch bumps when bump-each-commit is enabled",
|
||||
"",
|
||||
)
|
||||
.option(
|
||||
"-s, --search-commit-body",
|
||||
"Search commit body for version patterns",
|
||||
false,
|
||||
)
|
||||
.option("-d, --debug", "Enable debug output", false)
|
||||
.option(
|
||||
"--version-from-branch [pattern]",
|
||||
"Use branch name for version selection (optionally provide a regex pattern)",
|
||||
)
|
||||
.option(
|
||||
"--enable-prerelease-mode",
|
||||
"Prevents pre-v1.0.0 major version increments",
|
||||
false,
|
||||
)
|
||||
.option(
|
||||
"--user-format-type <type>",
|
||||
"Output format for users (csv, json)",
|
||||
"csv",
|
||||
)
|
||||
.option("--format <format>", "Output format (json, text)", "text")
|
||||
.action(async (options) => {
|
||||
try {
|
||||
// Create ActionConfig from CLI options
|
||||
const config = new ActionConfig();
|
||||
config.branch = options.branch;
|
||||
config.tagPrefix = options.tagPrefix;
|
||||
config.versionFormat = options.versionFormat;
|
||||
config.changePath = options.path;
|
||||
config.namespace = options.namespace;
|
||||
config.majorPattern = options.majorPattern;
|
||||
config.majorFlags = options.majorFlags || "";
|
||||
config.minorPattern = options.minorPattern;
|
||||
config.minorFlags = options.minorFlags || "";
|
||||
config.bumpEachCommit = options.bumpEachCommit;
|
||||
config.bumpEachCommitPatchPattern =
|
||||
options.bumpEachCommitPatchPattern || "";
|
||||
config.searchCommitBody = options.searchCommitBody;
|
||||
// versionFromBranch can be true (flag only) or a string (custom pattern)
|
||||
config.versionFromBranch = options.versionFromBranch ?? false;
|
||||
config.enablePrereleaseMode = options.enablePrereleaseMode;
|
||||
config.userFormatType = options.userFormatType;
|
||||
config.debug = options.debug;
|
||||
|
||||
// Create ConfigurationProvider with the config
|
||||
const configProvider = new ConfigurationProvider(config);
|
||||
|
||||
const result = await runAction(configProvider);
|
||||
|
||||
if (options.format === "json") {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
version: result.formattedVersion,
|
||||
versionTag: result.versionTag,
|
||||
major: result.major,
|
||||
minor: result.minor,
|
||||
patch: result.patch,
|
||||
increment: result.increment,
|
||||
versionType: result.versionType,
|
||||
changed: result.changed,
|
||||
isTagged: result.isTagged,
|
||||
authors: result.authors,
|
||||
currentCommit: result.currentCommit,
|
||||
previousCommit: result.previousCommit,
|
||||
previousVersion: result.previousVersion,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
console.log(result.formattedVersion);
|
||||
if (options.debug) {
|
||||
console.error("Version details:");
|
||||
console.error(` Major: ${result.major}`);
|
||||
console.error(` Minor: ${result.minor}`);
|
||||
console.error(` Patch: ${result.patch}`);
|
||||
console.error(` Type: ${result.versionType}`);
|
||||
console.error(` Changed: ${result.changed}`);
|
||||
if (result.previousVersion) {
|
||||
console.error(` Previous: ${result.previousVersion}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error("Error:", error instanceof Error ? error.message : error);
|
||||
if (options.debug && error instanceof Error && error.stack) {
|
||||
console.error(error.stack);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
|
|
@ -7,19 +7,19 @@ export class DefaultTagFormatter implements TagFormatter {
|
|||
|
||||
private tagPrefix: string;
|
||||
private namespace: string;
|
||||
private namespaceSeperator: string;
|
||||
private namespaceSeparator: string;
|
||||
|
||||
constructor(config: ActionConfig) {
|
||||
this.namespace = config.namespace;
|
||||
this.tagPrefix = config.tagPrefix;
|
||||
this.namespaceSeperator = '-'; // maybe make configurable in the future
|
||||
this.namespaceSeparator = '-'; // maybe make configurable in the future
|
||||
}
|
||||
|
||||
public Format(versionInfo: VersionInformation): string {
|
||||
const result = `${this.tagPrefix}${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`;
|
||||
|
||||
if (!!this.namespace) {
|
||||
return `${result}${this.namespaceSeperator}${this.namespace}`;
|
||||
if (this.namespace) {
|
||||
return `${result}${this.namespaceSeparator}${this.namespace}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -27,8 +27,8 @@ export class DefaultTagFormatter implements TagFormatter {
|
|||
|
||||
public GetPattern(): string {
|
||||
|
||||
if (!!this.namespace) {
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
|
||||
if (this.namespace) {
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeparator}${this.namespace}`;
|
||||
}
|
||||
|
||||
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
|
||||
|
|
@ -63,18 +63,18 @@ export class DefaultTagFormatter implements TagFormatter {
|
|||
}
|
||||
|
||||
return [major, minor, patch];
|
||||
};
|
||||
}
|
||||
|
||||
public IsValid(tag: string): boolean {
|
||||
const regexEscape = (literal: string) => literal.replace(/\W/g, '\\$&');
|
||||
const tagPrefix = regexEscape(this.tagPrefix);
|
||||
const namespaceSeperator = regexEscape(this.namespaceSeperator);
|
||||
const namespaceSeparator = regexEscape(this.namespaceSeparator);
|
||||
const namespace = regexEscape(this.namespace);
|
||||
|
||||
if (!!this.namespace) {
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
|
||||
if (this.namespace) {
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\\.[0-9]+\\.[0-9]+${namespaceSeparator}${namespace}$`).test(tag);
|
||||
}
|
||||
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
|
||||
return new RegExp(`^${tagPrefix}[0-9]+\\.[0-9]+\\.[0-9]+$`).test(tag);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
2461
src/main.test.ts
2461
src/main.test.ts
File diff suppressed because it is too large
Load diff
99
src/main.ts
99
src/main.ts
|
|
@ -1,23 +1,35 @@
|
|||
import { runAction } from './action';
|
||||
import { ActionConfig } from './ActionConfig';
|
||||
import { ConfigurationProvider } from './ConfigurationProvider';
|
||||
import { VersionResult } from './VersionResult';
|
||||
import * as core from '@actions/core';
|
||||
import { VersionType } from './providers/VersionType';
|
||||
import { runAction } from "./action";
|
||||
import { ActionConfig } from "./ActionConfig";
|
||||
import { ConfigurationProvider } from "./ConfigurationProvider";
|
||||
import { VersionResult } from "./VersionResult";
|
||||
import * as core from "@actions/core";
|
||||
import { VersionType } from "./providers/VersionType";
|
||||
|
||||
function setOutput(versionResult: VersionResult) {
|
||||
const { major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput } = versionResult;
|
||||
const {
|
||||
major,
|
||||
minor,
|
||||
patch,
|
||||
increment,
|
||||
versionType,
|
||||
formattedVersion,
|
||||
versionTag,
|
||||
changed,
|
||||
isTagged,
|
||||
authors,
|
||||
currentCommit,
|
||||
previousCommit,
|
||||
previousVersion,
|
||||
debugOutput,
|
||||
} = versionResult;
|
||||
|
||||
const repository = process.env.GITHUB_REPOSITORY;
|
||||
|
||||
if (!changed) {
|
||||
core.info('No changes detected for this commit');
|
||||
core.info("No changes detected for this commit");
|
||||
}
|
||||
|
||||
core.info(`Version is ${formattedVersion}`);
|
||||
if (repository !== undefined) {
|
||||
core.info(`To create a release for this version, go to https://github.com/${repository}/releases/new?tag=${versionTag}&target=${currentCommit.split('/').slice(-1)[0]}`);
|
||||
}
|
||||
|
||||
core.setOutput("version", formattedVersion);
|
||||
core.setOutput("major", major.toString());
|
||||
|
|
@ -36,56 +48,59 @@ function setOutput(versionResult: VersionResult) {
|
|||
}
|
||||
|
||||
export async function run() {
|
||||
|
||||
function toBool(value: string): boolean {
|
||||
if (!value || value.toLowerCase() === 'false') {
|
||||
if (!value || value.toLowerCase() === "false") {
|
||||
return false;
|
||||
} else if (value.toLowerCase() === 'true') {
|
||||
} else if (value.toLowerCase() === "true") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function toStringOrBool(value: string): string | boolean {
|
||||
if (!value || value === 'false') {
|
||||
if (!value || value === "false") {
|
||||
return false;
|
||||
}
|
||||
if (value === 'true') {
|
||||
if (value === "true") {
|
||||
return true;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const config: ActionConfig = {
|
||||
branch: core.getInput('branch'),
|
||||
tagPrefix: core.getInput('tag_prefix'),
|
||||
useBranches: toBool(core.getInput('use_branches')),
|
||||
versionFromBranch: toStringOrBool(core.getInput('version_from_branch')),
|
||||
majorPattern: core.getInput('major_pattern'),
|
||||
minorPattern: core.getInput('minor_pattern'),
|
||||
majorFlags: core.getInput('major_regexp_flags'),
|
||||
minorFlags: core.getInput('minor_regexp_flags'),
|
||||
versionFormat: core.getInput('version_format'),
|
||||
changePath: core.getInput('change_path'),
|
||||
namespace: core.getInput('namespace'),
|
||||
bumpEachCommit: toBool(core.getInput('bump_each_commit')),
|
||||
searchCommitBody: toBool(core.getInput('search_commit_body')),
|
||||
userFormatType: core.getInput('user_format_type'),
|
||||
enablePrereleaseMode: toBool(core.getInput('enable_prerelease_mode')),
|
||||
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern'),
|
||||
debug: toBool(core.getInput('debug')),
|
||||
replay: ''
|
||||
branch: core.getInput("branch"),
|
||||
tagPrefix: core.getInput("tag_prefix"),
|
||||
useBranches: toBool(core.getInput("use_branches")),
|
||||
versionFromBranch: toStringOrBool(core.getInput("version_from_branch")),
|
||||
majorPattern: core.getInput("major_pattern"),
|
||||
minorPattern: core.getInput("minor_pattern"),
|
||||
majorFlags: core.getInput("major_regexp_flags"),
|
||||
minorFlags: core.getInput("minor_regexp_flags"),
|
||||
versionFormat: core.getInput("version_format"),
|
||||
changePath: core.getInput("change_path"),
|
||||
namespace: core.getInput("namespace"),
|
||||
bumpEachCommit: toBool(core.getInput("bump_each_commit")),
|
||||
searchCommitBody: toBool(core.getInput("search_commit_body")),
|
||||
userFormatType: core.getInput("user_format_type"),
|
||||
enablePrereleaseMode: toBool(core.getInput("enable_prerelease_mode")),
|
||||
bumpEachCommitPatchPattern: core.getInput("bump_each_commit_patch_pattern"),
|
||||
debug: toBool(core.getInput("debug")),
|
||||
replay: "",
|
||||
};
|
||||
|
||||
if (config.useBranches) {
|
||||
core.warning(`The 'use_branches' input option is deprecated, please see the documentation for more information on how to use branches`);
|
||||
core.warning(
|
||||
`The 'use_branches' input option is deprecated, please see the documentation for more information on how to use branches`,
|
||||
);
|
||||
}
|
||||
|
||||
if (config.versionFormat === '' && core.getInput('format') !== '') {
|
||||
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);
|
||||
config.versionFormat = core.getInput('format');
|
||||
if (config.versionFormat === "" && core.getInput("format") !== "") {
|
||||
core.warning(
|
||||
`The 'format' input is deprecated, use 'versionFormat' instead`,
|
||||
);
|
||||
config.versionFormat = core.getInput("format");
|
||||
}
|
||||
if (core.getInput('short_tags') !== '') {
|
||||
if (core.getInput("short_tags") !== "") {
|
||||
core.warning(`The 'short_tags' input option is no longer supported`);
|
||||
}
|
||||
|
||||
|
|
@ -94,4 +109,4 @@ export async function run() {
|
|||
setOutput(result);
|
||||
}
|
||||
|
||||
run();
|
||||
run();
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ export class DefaultLastReleaseResolver implements LastReleaseResolver {
|
|||
currentTag = tagFormatter.IsValid(currentTag) ? currentTag : '';
|
||||
const isTagged = currentTag !== '';
|
||||
|
||||
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
const [currentMajor, currentMinor, currentPatch] = currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
|
||||
|
||||
let tagsCount = 0;
|
||||
|
||||
let tag = '';
|
||||
try {
|
||||
const refPrefixPattern = this.useBranches ? 'refs/heads/' : 'refs/tags/';
|
||||
if (!!currentTag) {
|
||||
if (currentTag) {
|
||||
// If we already have the current branch tagged, we are checking for the previous one
|
||||
// so that we will have an accurate increment (assuming the new tag is the expected one)
|
||||
const command = `git for-each-ref --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
|
||||
|
|
@ -63,9 +63,9 @@ export class DefaultLastReleaseResolver implements LastReleaseResolver {
|
|||
// polluted with a bunch of warnings.
|
||||
|
||||
if (tagsCount > 0) {
|
||||
core.warning(`None of the ${tagsCount} tags(s) found were valid version tags for the present configuration. If this is unexpected, check to ensure that the configuration is correct and matches the tag format you are using.`);
|
||||
core.warning(`None of the ${tagsCount} tags(s) found were valid version tags for the present configuration. If this is unexpected, check to ensure that the configuration is correct and matches the tag format you are using. If you have not yet tagged this repo with a version tag, this can be ignored.`);
|
||||
} else {
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote.');
|
||||
core.warning('No tags are present for this repository. If this is unexpected, check to ensure that tags have been pulled from the remote. If you have not yet tagged this repo with a version tag, this can be ignored.');
|
||||
}
|
||||
}
|
||||
const [major, minor, patch] = tagFormatter.Parse('');
|
||||
|
|
|
|||
187
src/test-abstraction.ts
Normal file
187
src/test-abstraction.ts
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
import * as cp from "child_process";
|
||||
import * as path from "path";
|
||||
import { runAction } from "./action";
|
||||
import { ConfigurationProvider } from "./ConfigurationProvider";
|
||||
import { ActionConfig } from "./ActionConfig";
|
||||
import { DebugManager } from "./DebugManager";
|
||||
import { VersionResult } from "./VersionResult";
|
||||
|
||||
export type TestInterface = "action" | "cli";
|
||||
|
||||
export interface TestRunner {
|
||||
runSemanticVersion(
|
||||
config: Partial<ActionConfig>,
|
||||
cwd: string,
|
||||
): Promise<VersionResult>;
|
||||
}
|
||||
|
||||
export class ActionTestRunner implements TestRunner {
|
||||
async runSemanticVersion(
|
||||
config: Partial<ActionConfig>,
|
||||
cwd: string,
|
||||
): Promise<VersionResult> {
|
||||
DebugManager.clearState();
|
||||
const fullConfig = new ActionConfig();
|
||||
Object.assign(fullConfig, config);
|
||||
|
||||
const originalCwd = process.cwd();
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
return await runAction(new ConfigurationProvider(fullConfig));
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class CLITestRunner implements TestRunner {
|
||||
private cliPath: string;
|
||||
|
||||
constructor() {
|
||||
this.cliPath = path.join(__dirname, "..", "lib", "cli.js");
|
||||
}
|
||||
|
||||
private toKebabCase(str: string): string {
|
||||
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
}
|
||||
|
||||
async runSemanticVersion(
|
||||
config: Partial<ActionConfig>,
|
||||
cwd: string,
|
||||
): Promise<VersionResult> {
|
||||
const args: string[] = ["node", this.cliPath, "--format", "json"];
|
||||
|
||||
// Special case: changePath maps to --path
|
||||
const cliOptionOverrides: Record<string, string> = {
|
||||
changePath: "path",
|
||||
};
|
||||
|
||||
// Dynamically build CLI arguments from config
|
||||
for (const [key, value] of Object.entries(config)) {
|
||||
if (value === undefined || value === null) continue;
|
||||
|
||||
// Get CLI option name
|
||||
const cliOptionName = cliOptionOverrides[key] || this.toKebabCase(key);
|
||||
|
||||
// Handle boolean flags
|
||||
if (typeof value === "boolean") {
|
||||
if (value) {
|
||||
args.push(`--${cliOptionName}`);
|
||||
}
|
||||
} else {
|
||||
// Handle string/number values
|
||||
args.push(`--${cliOptionName}`, String(value));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Use execFileSync instead of execSync to avoid shell escaping issues
|
||||
const output = cp.execFileSync("node", args.slice(1), {
|
||||
cwd,
|
||||
encoding: "utf8",
|
||||
stdio: ["pipe", "pipe", "pipe"], // Capture stdout and stderr
|
||||
});
|
||||
|
||||
// Parse JSON output - filter out warning lines that start with ::warning::
|
||||
const lines = output
|
||||
.split("\n")
|
||||
.filter((line) => !line.startsWith("::warning::"));
|
||||
|
||||
// Find the start and end of JSON block
|
||||
const startIndex = lines.findIndex((line) => line.trim().startsWith("{"));
|
||||
const endIndex = lines.findIndex((line) => line.trim() === "}");
|
||||
|
||||
if (startIndex === -1 || endIndex === -1) {
|
||||
throw new Error(`No JSON output found from CLI. Output was: ${output}`);
|
||||
}
|
||||
|
||||
// Extract JSON lines and join them
|
||||
const jsonLines = lines.slice(startIndex, endIndex + 1);
|
||||
const jsonString = jsonLines.join("\n");
|
||||
|
||||
let cliResult;
|
||||
try {
|
||||
cliResult = JSON.parse(jsonString);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to parse JSON. Output was: "${output}". JSON string extracted: "${jsonString}"`,
|
||||
);
|
||||
}
|
||||
|
||||
// Map CLI output to VersionResult format
|
||||
return new VersionResult(
|
||||
cliResult.major,
|
||||
cliResult.minor,
|
||||
cliResult.patch,
|
||||
cliResult.increment,
|
||||
cliResult.versionType,
|
||||
cliResult.version,
|
||||
cliResult.versionTag,
|
||||
cliResult.changed,
|
||||
cliResult.isTagged,
|
||||
cliResult.authors,
|
||||
cliResult.currentCommit,
|
||||
cliResult.previousCommit,
|
||||
cliResult.previousVersion,
|
||||
"", // debugOutput - not returned by CLI
|
||||
);
|
||||
} catch (error: any) {
|
||||
if (error.stdout) {
|
||||
// Try to parse error output - filter out warning lines
|
||||
const errorOutput = error.stdout.toString();
|
||||
const lines = errorOutput
|
||||
.split("\n")
|
||||
.filter((line: string) => !line.startsWith("::warning::"));
|
||||
|
||||
// Find the start and end of JSON block
|
||||
const startIndex = lines.findIndex((line: string) =>
|
||||
line.trim().startsWith("{"),
|
||||
);
|
||||
const endIndex = lines.findIndex((line: string) => line.trim() === "}");
|
||||
|
||||
if (startIndex !== -1 && endIndex !== -1) {
|
||||
// Extract JSON lines and join them
|
||||
const jsonLines = lines.slice(startIndex, endIndex + 1);
|
||||
const jsonString = jsonLines.join("\n");
|
||||
|
||||
let cliResult;
|
||||
try {
|
||||
cliResult = JSON.parse(jsonString);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to parse JSON from error output. Output was: "${errorOutput}". JSON string extracted: "${jsonString}"`,
|
||||
);
|
||||
}
|
||||
return new VersionResult(
|
||||
cliResult.major,
|
||||
cliResult.minor,
|
||||
cliResult.patch,
|
||||
cliResult.increment,
|
||||
cliResult.versionType,
|
||||
cliResult.version,
|
||||
cliResult.versionTag,
|
||||
cliResult.changed,
|
||||
cliResult.isTagged,
|
||||
cliResult.authors,
|
||||
cliResult.currentCommit,
|
||||
cliResult.previousCommit,
|
||||
cliResult.previousVersion,
|
||||
"", // debugOutput - not returned by CLI
|
||||
);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createTestRunner(type: TestInterface): TestRunner {
|
||||
switch (type) {
|
||||
case "action":
|
||||
return new ActionTestRunner();
|
||||
case "cli":
|
||||
return new CLITestRunner();
|
||||
default:
|
||||
throw new Error(`Unknown test interface: ${type}`);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue