Add support for using branches instead of tags

This commit is contained in:
Paul Hatcherian 2022-04-15 13:12:45 -04:00
parent cd16d71443
commit 3a712b126c
12 changed files with 235 additions and 101 deletions

View file

@ -12,6 +12,10 @@ inputs:
description: "The prefix to use to identify tags"
required: false
default: "v"
use_branches:
description: "Use branches instead of tags"
required: false
default: "false"
major_pattern:
description: "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."
required: true

189
dist/index.js vendored
View file

@ -83,14 +83,14 @@ const JsonUserFormatter_1 = __nccwpck_require__(892);
const DefaultCommitsProvider_1 = __nccwpck_require__(458);
const DefaultCurrentCommitResolver_1 = __nccwpck_require__(35);
const DefaultVersionClassifier_1 = __nccwpck_require__(527);
const TagLastReleaseResolver_1 = __nccwpck_require__(936);
const DefaultLastReleaseResolver_1 = __nccwpck_require__(337);
const BumpAlwaysVersionClassifier_1 = __nccwpck_require__(482);
class ConfigurationProvider {
constructor(config) {
this.config = config;
}
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new TagLastReleaseResolver_1.TagLastReleaseResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
GetCommitsProvider() { return new DefaultCommitsProvider_1.DefaultCommitsProvider(this.config); }
GetVersionClassifier() {
if (this.config.bumpEachCommit) {
@ -409,6 +409,7 @@ function run() {
const config = {
branch: core.getInput('branch'),
tagPrefix: core.getInput('tag_prefix'),
useBranches: core.getInput('use_branches') === 'true',
majorPattern: core.getInput('major_pattern'),
minorPattern: core.getInput('minor_pattern'),
versionFormat: core.getInput('version_format'),
@ -681,6 +682,99 @@ class DefaultCurrentCommitResolver {
exports.DefaultCurrentCommitResolver = DefaultCurrentCommitResolver;
/***/ }),
/***/ 337:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"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 = __nccwpck_require__(949);
const ReleaseInformation_1 = __nccwpck_require__(300);
const core = __importStar(__nccwpck_require__(186));
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();
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
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 --count=2 --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${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} ${refPrefixPattern}${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.DefaultLastReleaseResolver = DefaultLastReleaseResolver;
/***/ }),
/***/ 527:
@ -813,97 +907,6 @@ class ReleaseInformation {
exports.ReleaseInformation = ReleaseInformation;
/***/ }),
/***/ 936:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
"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 = __nccwpck_require__(949);
const ReleaseInformation_1 = __nccwpck_require__(300);
const core = __importStar(__nccwpck_require__(186));
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;
/***/ }),
/***/ 907:

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -8,6 +8,8 @@ class ActionConfig {
this.branch = "HEAD";
/** The prefix to use to identify tags */
this.tagPrefix = "v";
/** Use branches instead of tags */
this.useBranches = 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 = "(MAJOR)";
/** 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. */

View file

@ -8,14 +8,14 @@ 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 TagLastReleaseResolver_1 = require("./providers/TagLastReleaseResolver");
const DefaultLastReleaseResolver_1 = require("./providers/DefaultLastReleaseResolver");
const BumpAlwaysVersionClassifier_1 = require("./providers/BumpAlwaysVersionClassifier");
class ConfigurationProvider {
constructor(config) {
this.config = config;
}
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new TagLastReleaseResolver_1.TagLastReleaseResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
GetCommitsProvider() { return new DefaultCommitsProvider_1.DefaultCommitsProvider(this.config); }
GetVersionClassifier() {
if (this.config.bumpEachCommit) {

View file

@ -63,6 +63,7 @@ function run() {
const config = {
branch: core.getInput('branch'),
tagPrefix: core.getInput('tag_prefix'),
useBranches: core.getInput('use_branches') === 'true',
majorPattern: core.getInput('major_pattern'),
minorPattern: core.getInput('minor_pattern'),
versionFormat: core.getInput('version_format'),

View file

@ -0,0 +1,85 @@
"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();
const [currentMajor, currentMinor, currentPatch] = !!currentTag ? tagFormatter.Parse(currentTag) : [null, null, null];
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 --count=2 --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${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} ${refPrefixPattern}${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.DefaultLastReleaseResolver = DefaultLastReleaseResolver;

View file

@ -4,6 +4,8 @@ export class ActionConfig {
public branch: string = "HEAD";
/** The prefix to use to identify tags */
public tagPrefix: string = "v";
/** Use branches instead of tags */
public useBranches: 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 = "(MAJOR)";
/** 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. */

View file

@ -11,7 +11,7 @@ import { DefaultCommitsProvider } from './providers/DefaultCommitsProvider'
import { DefaultCurrentCommitResolver } from './providers/DefaultCurrentCommitResolver'
import { DefaultVersionClassifier } from './providers/DefaultVersionClassifier'
import { LastReleaseResolver } from './providers/LastReleaseResolver'
import { TagLastReleaseResolver } from './providers/TagLastReleaseResolver'
import { DefaultLastReleaseResolver } from './providers/DefaultLastReleaseResolver'
import { VersionClassifier } from './providers/VersionClassifier'
import { BumpAlwaysVersionClassifier } from './providers/BumpAlwaysVersionClassifier'
import { ActionConfig } from './ActionConfig';
@ -26,7 +26,7 @@ export class ConfigurationProvider {
public GetCurrentCommitResolver(): CurrentCommitResolver { return new DefaultCurrentCommitResolver(this.config); }
public GetLastReleaseResolver(): LastReleaseResolver { return new TagLastReleaseResolver(this.config); }
public GetLastReleaseResolver(): LastReleaseResolver { return new DefaultLastReleaseResolver(this.config); }
public GetCommitsProvider(): CommitsProvider { return new DefaultCommitsProvider(this.config); }

View file

@ -546,4 +546,37 @@ test('Correct tag is detected when versions pass 10s place', async () => {
const result = await repo.runAction();
expect(result.versionTag).toBe('v10.15.1');
}, 15000);
test('Tags on unmerged branches are not considered', async () => {
const repo = createTestRepo({ tagPrefix: 'v' }); // 0.0.0
repo.makeCommit('Initial Commit');
repo.makeCommit('Commit 1');
repo.exec('git checkout -b feature/branch1');
repo.makeCommit('Commit 2');
repo.exec('git tag v2.0.0');
repo.makeCommit('Commit 3');
repo.exec('git checkout master');
repo.makeCommit('Commit 4');
repo.exec('git tag v1.0.0');
repo.makeCommit('Commit 5');
const result = await repo.runAction();
expect(result.versionTag).toBe('v1.0.1');
}, 15000);
test('Can use branches instead of tags', async () => {
const repo = createTestRepo({ tagPrefix: 'release/', useBranches: true }); // 0.0.0
repo.makeCommit('Initial Commit');
repo.makeCommit('Commit 1');
repo.exec('git checkout -b release/1.0.0');
repo.makeCommit('Commit 2');
repo.exec('git checkout master');
repo.exec('git merge release/1.0.0');
repo.makeCommit('Commit 3');
const result = await repo.runAction();
expect(result.versionTag).toBe('release/1.0.1');
}, 15000);

View file

@ -36,6 +36,7 @@ export async function run() {
const config: ActionConfig = {
branch: core.getInput('branch'),
tagPrefix: core.getInput('tag_prefix'),
useBranches: core.getInput('use_branches') === 'true',
majorPattern: core.getInput('major_pattern'),
minorPattern: core.getInput('minor_pattern'),
versionFormat: core.getInput('version_format'),

View file

@ -5,12 +5,14 @@ import { ReleaseInformation } from "./ReleaseInformation";
import { ActionConfig } from "../ActionConfig";
import * as core from '@actions/core';
export class TagLastReleaseResolver implements LastReleaseResolver {
export class DefaultLastReleaseResolver implements LastReleaseResolver {
private changePath: string;
private useBranches: boolean;
constructor(config: ActionConfig) {
this.changePath = config.changePath;
this.useBranches = config.useBranches;
}
async ResolveAsync(current: string, tagFormatter: TagFormatter): Promise<ReleaseInformation> {
@ -23,14 +25,15 @@ export class TagLastReleaseResolver implements LastReleaseResolver {
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 --count=2 --sort=-v:*refname --format=%(refname:short) --merged=${current} refs/tags/${releasePattern}`;
const command = `git for-each-ref --count=2 --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
tag = await 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}`;
const command = `git for-each-ref --count=1 --sort=-v:*refname --format=%(refname:short) --merged=${current} ${refPrefixPattern}${releasePattern}`;
tag = await cmd(command);
}