feat!: tag released

This commit is contained in:
marcin 2023-03-28 16:34:14 +01:00
parent f9d3daa396
commit 0735972145
No known key found for this signature in database
GPG key ID: 524860A885021BCB
33 changed files with 216 additions and 42 deletions

12
.github/workflows/lint_commits.yml vendored Normal file
View file

@ -0,0 +1,12 @@
name: Lint Commit Messages
on: [pull_request]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: wagoid/commitlint-github-action@v5.0.2

25
.github/workflows/pr_title_lint.yml vendored Normal file
View file

@ -0,0 +1,25 @@
# https://github.com/marketplace/actions/conventional-pr-title
# This is a Github Action that ensures that your PR title matches the Conventional Commits spec.
#
# This is helpful when you're using semantic-release with the Conventional Commits preset.
# When using the Squash and merge strategy, Github will suggest to use the PR title as the commit message.
# With this action you can validate that the PR title will lead to a correct commit message.
name: PR Title Lint
on:
pull_request:
types:
- opened
- reopened
- edited
- synchronize
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: aslafy-z/conventional-pr-title-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

11
.github/workflows/pre_commit.yml vendored Normal file
View file

@ -0,0 +1,11 @@
name: pre-commit
on: [push]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.0

57
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,57 @@
name: Release
on:
push:
branches:
- 'master'
jobs:
create:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Calculate semantic version
id: version
uses: 3h4x/semantic-version@master
with:
tag_prefix: "v"
major_pattern: "/^(feat|refactor)!:/"
minor_pattern: "/^(feat|refactor):/"
version_format: "${major}.${minor}.${patch}"
bump_each_commit: false
search_commit_body: false
user_format_type: "json"
- name: Push tag
run: |
git tag v${{ steps.version.outputs.version }}
git push origin v${{ steps.version.outputs.version }}
- name: Update CHANGELOG
id: changelog
uses: requarks/changelog-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: v${{ steps.version.outputs.version }}
- name: Create Release
uses: ncipollo/release-action@v1
with:
allowUpdates: true
draft: false
name: v${{ steps.version.outputs.version }}
tag: v${{ steps.version.outputs.version }}
body: ${{ steps.changelog.outputs.changes }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Commit CHANGELOG.md
uses: stefanzweifel/git-auto-commit-action@v4
with:
branch: master
commit_message: "build(release): update CHANGELOG.md for v${{ steps.version.outputs.version }} [skip ci]"
file_pattern: CHANGELOG.md

8
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,8 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v1.1.1
hooks:
- id: trailing-whitespace
exclude: ^version.json

View file

@ -83,7 +83,7 @@ outputs:
description: "Indicates the previous version"
current_commit:
description: "The current commit hash"
runs:
using: "node16"
main: "dist/index.js"
main: "dist/index.js"

View file

@ -40,7 +40,7 @@ test('Name of test goes here', async () => {
// an optional second parameter can be used to specify the path of the file to commit,
// which will be created if it does not exist already
repo.makeCommit('Initial Commit', 'subdir');
// the exec method runs an arbitrary command in the repository
repo.exec('git tag 0.0.1')
@ -77,4 +77,4 @@ Additionally a few formatter provide modular behavior to these step:
- A version formatter is used to format output version string
- A user formatter is used to format the user information in the output (JSON and CSV are provided in the default implementation)
Each one includes at least one default, but can be replaced by a custom provider by implementing the appropriate interface and updating the `ConfigurationProvider` to return your action instead. This should allow you to continue to merge in changes from the main project as needed with minimal conflict.
Each one includes at least one default, but can be replaced by a custom provider by implementing the appropriate interface and updating the `ConfigurationProvider` to return your action instead. This should allow you to continue to merge in changes from the main project as needed with minimal conflict.

30
dist/index.js vendored
View file

@ -254,6 +254,7 @@ class DefaultTagFormatter {
this.namespace = config.namespace;
this.tagPrefix = config.tagPrefix;
this.namespaceSeperator = '-'; // maybe make configurable in the future
this.prereleaseName = config.prereleaseName;
}
Format(versionInfo) {
const result = `${this.tagPrefix}${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`;
@ -266,6 +267,9 @@ class DefaultTagFormatter {
if (!!this.namespace) {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
}
if (!!this.prereleaseName) {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]-${this.prereleaseName}.*[0-9]`;
}
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
}
Parse(tag) {
@ -297,6 +301,9 @@ class DefaultTagFormatter {
if (!!this.namespace) {
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
}
if (!!this.prereleaseName) {
return new RegExp(`^${this.tagPrefix}[0-9]+\.[0-9]+\.[0-9]+-${this.prereleaseName}\.[0-9]+$`).test(tag);
}
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
}
}
@ -434,6 +441,7 @@ function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true',
prereleaseName: core.getInput('prerelease_name'),
};
if (config.versionFormat === '' && core.getInput('format') !== '') {
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);
@ -1624,8 +1632,8 @@ class OidcClient {
const res = yield httpclient
.getJson(id_token_url)
.catch(error => {
throw new Error(`Failed to get ID Token. \n
Error Code : ${error.statusCode}\n
throw new Error(`Failed to get ID Token. \n
Error Code : ${error.statusCode}\n
Error Message: ${error.result.message}`);
});
const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value;
@ -5139,7 +5147,7 @@ module.exports = require("util");
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/
/******/ // The require function
/******/ function __nccwpck_require__(moduleId) {
/******/ // Check if module is in cache
@ -5153,7 +5161,7 @@ module.exports = require("util");
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/
/******/ // Execute the module function
/******/ var threw = true;
/******/ try {
@ -5162,24 +5170,24 @@ module.exports = require("util");
/******/ } finally {
/******/ if(threw) delete __webpack_module_cache__[moduleId];
/******/ }
/******/
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat */
/******/
/******/
/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
/******/
/******/
/************************************************************************/
/******/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module is referenced by other modules so it can't be inlined
/******/ var __webpack_exports__ = __nccwpck_require__(3109);
/******/ module.exports = __webpack_exports__;
/******/
/******/
/******/ })()
;
//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -9,4 +9,4 @@ module.exports = {
modulePaths: [
"<rootDir>/src/"
],
}
}

View file

@ -32,6 +32,8 @@ class ActionConfig {
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;
/** Prerelease name. If set, the version will be suffixed with the prerelease name and increment. */
this.prereleaseName = "";
}
}
exports.ActionConfig = ActionConfig;

View file

@ -7,6 +7,7 @@ class DefaultTagFormatter {
this.namespace = config.namespace;
this.tagPrefix = config.tagPrefix;
this.namespaceSeperator = '-'; // maybe make configurable in the future
this.prereleaseName = config.prereleaseName;
}
Format(versionInfo) {
const result = `${this.tagPrefix}${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`;
@ -19,6 +20,9 @@ class DefaultTagFormatter {
if (!!this.namespace) {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
}
if (!!this.prereleaseName) {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]-${this.prereleaseName}.*[0-9]`;
}
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
}
Parse(tag) {
@ -50,6 +54,9 @@ class DefaultTagFormatter {
if (!!this.namespace) {
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
}
if (!!this.prereleaseName) {
return new RegExp(`^${this.tagPrefix}[0-9]+\.[0-9]+\.[0-9]+-${this.prereleaseName}\.[0-9]+$`).test(tag);
}
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
}
}

View file

@ -78,6 +78,7 @@ function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true',
prereleaseName: core.getInput('prerelease_name'),
};
if (config.versionFormat === '' && core.getInput('format') !== '') {
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);

View file

@ -127,4 +127,4 @@
</text>
</a>
</switch>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -58,7 +58,7 @@ instead of the default patch level. As with normal commits the implied version
will only increment by one value since the last tag regardless of how many major
or minor commits are encountered. Major commits override minor commits, so a set
of commits containing both major and minor tags will result in a major version
increment.
increment.
![Commits Graph](minor.drawio.svg?raw=true)
@ -104,12 +104,15 @@ it will be given the new version if the build were to be retriggered, for exampl
# 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 version_type output is unchanged.
enable_prerelease_mode: true
# Fetches tags not from releases like v1.4.0 but from prereleases with suffix specified like v1.4.0-rc.0
# For example "rc."
prerelease_name: ""
```
## Outputs
## Outputs
- *major*, *minor*, and *patch* provide the version numbers that have been determined for this commit
- *increment* is an additional value indicating the number of commits for the current version, starting at zero. This can be used as part of a pre-release label.
- *increment* is an additional value indicating the number of commits for the current version, starting at zero. This can be used as part of a pre-release label.
- *version_type* is the type of version change the new version represents, e.g. `major`, `minor`, `patch`, or `none`.
- *version* is a formatted version string created using the format input. This is a convenience value to provide a preformatted representation of the data generated by this action.
- *version_tag* is a string identifier that would be used to tag the current commit as the "released" version. Typically this would only be used to generate a Git tag name.

View file

@ -28,4 +28,6 @@ export class ActionConfig {
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;
}
/** Prerelease name. If set, the version will be suffixed with the prerelease name and increment. */
public prereleaseName: string = "";
}

View file

@ -25,4 +25,4 @@ export const cmd = async (command: string, ...args: any): Promise<string> => {
}
return output;
};
};

View file

@ -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
*/

View file

@ -8,11 +8,13 @@ export class DefaultTagFormatter implements TagFormatter {
private tagPrefix: string;
private namespace: string;
private namespaceSeperator: string;
private prereleaseName: string;
constructor(config: ActionConfig) {
this.namespace = config.namespace;
this.tagPrefix = config.tagPrefix;
this.namespaceSeperator = '-'; // maybe make configurable in the future
this.prereleaseName = config.prereleaseName;
}
public Format(versionInfo: VersionInformation): string {
@ -31,6 +33,10 @@ export class DefaultTagFormatter implements TagFormatter {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]${this.namespaceSeperator}${this.namespace}`;
}
if (!!this.prereleaseName) {
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]-${this.prereleaseName}.*[0-9]`;
}
return `${this.tagPrefix}*[0-9].*[0-9].*[0-9]`;
}
@ -71,6 +77,10 @@ export class DefaultTagFormatter implements TagFormatter {
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+${namespaceSeperator}${namespace}$`).test(tag);
}
if (!!this.prereleaseName) {
return new RegExp(`^${this.tagPrefix}[0-9]+\.[0-9]+\.[0-9]+-${this.prereleaseName}\.[0-9]+$`).test(tag);
}
return new RegExp(`^${tagPrefix}[0-9]+\.[0-9]+\.[0-9]+$`).test(tag);
}
}

View file

@ -60,6 +60,33 @@ const execute = (workingDirectory: string, command: string, env?: any) => {
}
};
test('Prerelease is detected and tagged correctly', async () => {
const repo = createTestRepo({ tagPrefix: 'v', versionFormat: "${major}.${minor}.${patch}-rc.${increment}", prereleaseName: "rc" });
repo.makeCommit('Initial Commit');
repo.exec('git tag v1.0.0-rc.0');
var result = await repo.runAction();
expect(result.formattedVersion).toBe('1.0.0-rc.0');
expect(result.isTagged).toBe(true);
repo.makeCommit('Second Commit');
result = await repo.runAction();
expect(result.formattedVersion).toBe('1.0.1-rc.0')
expect(result.isTagged).toBe(false);
repo.makeCommit('Third Commit (MINOR)');
result = await repo.runAction();
expect(result.formattedVersion).toBe('1.1.0-rc.0');
expect(result.isTagged).toBe(false);
repo.makeCommit('Fourth Commit (MINOR)');
repo.exec('git tag v1.1.0-rc.0')
result = await repo.runAction();
console.log(result.formattedVersion)
expect(result.formattedVersion).toBe('1.1.0-rc.1');
expect(result.isTagged).toBe(true);
}, timeout);
test('Empty repository version is correct', async () => {
const repo = createTestRepo(); // 0.0.0+0
var result = await repo.runAction();
@ -814,11 +841,11 @@ test('Prerelease tags are ignored on current commit', async () => {
repo.makeCommit(`Commit ${i++}`);
await validate('0.1.0+1');
repo.exec('git tag v1.0.0-rc2');
await validate('0.1.0+1');
await validate('0.1.0+1');
repo.makeCommit(`Commit ${i++}`);
await validate('0.1.0+2');
await validate('0.1.0+2');
repo.exec('git tag v1.0.0-rc3');
await validate('0.1.0+2');
await validate('0.1.0+2');
repo.makeCommit(`Commit ${i++}`);
await validate('0.1.0+3');
repo.exec('git tag v1.0.0');
@ -897,4 +924,4 @@ test('Tagged commit is flagged as release', async () => {
result = await repo.runAction();
expect(result.formattedVersion).toBe('1.1.0-prerelease.1');
expect(result.isTagged).toBe(true);
}, timeout);
}, timeout);

View file

@ -51,6 +51,7 @@ export async function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true',
prereleaseName: core.getInput('prerelease_name'),
};
if (config.versionFormat === '' && core.getInput('format') !== '') {
@ -66,4 +67,4 @@ export async function run() {
setOutput(result);
}
run();
run();

View file

@ -44,4 +44,4 @@ export class BumpAlwaysVersionClassifier extends DefaultVersionClassifier {
return new VersionClassification(type, 0, true, major, minor, patch);
}
}
}

View file

@ -6,4 +6,4 @@ export class CommitInfoSet {
public changed: boolean,
public commits: CommitInfo[]
){}
}
}

View file

@ -91,4 +91,4 @@ export class DefaultCommitsProvider implements CommitsProvider {
return new CommitInfoSet(changed, commits);
}
}
}

View file

@ -22,4 +22,4 @@ export class DefaultCurrentCommitResolver implements CurrentCommitResolver {
let lastCommitAll = (await cmd('git', 'rev-list', '-n1', '--all')).trim();
return lastCommitAll === '';
}
}
}

View file

@ -21,7 +21,7 @@ export class DefaultLastReleaseResolver implements LastReleaseResolver {
let currentTag = (await cmd(
`git tag --points-at ${current} ${releasePattern}`
)).trim();
currentTag = tagFormatter.IsValid(currentTag) ? currentTag : '';
const isTagged = currentTag !== '';
@ -73,4 +73,4 @@ export class DefaultLastReleaseResolver implements LastReleaseResolver {
return new ReleaseInformation(major, minor, patch, root.trim(), currentMajor, currentMinor, currentPatch, isTagged);
}
}
}

View file

@ -21,4 +21,4 @@ test('Regular expressions can be used as minor tag direct', async () => {
expect(result.minor).toBe(1);
expect(result.patch).toBe(0);
expect(result.increment).toBe(0);
});
});

View file

@ -112,4 +112,4 @@ export class DefaultVersionClassifier implements VersionClassifier {
return new VersionClassification(type, increment, changed, major, minor, patch);
}
}
}

View file

@ -2,7 +2,7 @@
/** Represents information about a user (e.g. committer, author, tagger) */
export class UserInfo {
/**
* Creates a new instance
* Creates a new instance
* @param name - User's name
* @param email - User's email
* @param commits - Number of commits in the scope evaluated

View file

@ -202,4 +202,4 @@
</text>
</a>
</switch>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -9,4 +9,4 @@
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
},
"exclude": ["node_modules", "**/*.test.ts"]
}
}

View file

@ -106,4 +106,4 @@
</text>
</a>
</switch>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB