Semantic versioning doesn't set the Patch field if the latest Tag includes 3 fields (like 'v0.0.0')
Bug description
Semantic versioning patch field does not behave as expected when the most recent tag has major, minor, and tag in it.
How to reproduce
- Create workflow with
uses: game-ci/unity-builder@v4to build a project - Add a tag
v0.1a couple CLs back (or submit a couple CLs after) - Build with GitHub Actions
- See that the built project
outputs.buildVersionis something like0.1.14as expected - Delete the tag and instead add a tag
v0.1.0a couple CLs back - Build with GitHub Actions
- See that the built project
outputs.buildVersionis0.1.0which is NOT as expected
Expected behavior
Based on the docs for Semantic versioning here I would expect the major and minor versions to be dictated by the most recent tag, but for the patch to always be the number of CLs since the tag.
Additional details
I would like to have GitHub Actions automatically create a tag for each release with the version. So for example, if commit 8db7eea is built to create 0.1.14, I would like to have it create the tag v0.1.14 on that commit. This behavior prevents me from doing that.
I believe that the cause is in the src/model/versioning.ts file. With 3 field in the version, getVersionDescription would something like v0.12.15-24-gd2198ab (If the tag was v.0.12.15). parseSemanticVersion would then match on the first descriptionRegexes to generate [match=..., tag='0.12.15', commits='24', hash='d2198ab'] . Then inside generateSemanticVersion we would combine tag and commits with const [major, minor, patch] = ${tag}.${commits}.split('.'); to create '0.12.15.24', but we only look at the first 3 so it would resolve to '0.12.15'.
My recommendation for fixing this (I do not have the time to get this all running locally to test and create a PR), would be to modify src/model/versioning.ts so that generateSemanticVersion() does this instead:
const { tag, commits, hash } = versionDescriptor;
// Use major and minor from tag, with patch from commit count
let [major, minor] = tag.split('.');
minor = minor ?? '0';
const threeDigitVersion = `${major}.${minor}.${commits}`;
core.info(`Found semantic version ${threeDigitVersion} for ${this.branch}@${hash}`);
return `${threeDigitVersion}`;
And then the generateSemanticVersion unit tests would have some additional cases for this:
it('tag with 3 fields uses commits', async () => {
...
jest.spyOn(Versioning, 'parseSemanticVersion').mockResolvedValue({
match: '0.1.0-2-g1b345678',
tag: '0.1.0',
commits: '2',
hash: '1b345678',
});
await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual('0.1.2');
});
it('tag with 1 field skips minor', async () => {
...
jest.spyOn(Versioning, 'parseSemanticVersion').mockResolvedValue({
match: '1-2-g1b345678',
tag: '1',
commits: '2',
hash: '1b345678',
});
await expect(Versioning.generateSemanticVersion()).resolves.toStrictEqual('1.0.2');
});