Add debug/replay mode (MINOR)

This commit is contained in:
Paul Hatcherian 2023-08-20 21:33:26 -04:00
parent 4f07cfb9e0
commit d93d2fb887
20 changed files with 480 additions and 56 deletions

View file

@ -62,6 +62,10 @@ inputs:
description: "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."
required: true
default: ""
debug:
description: "If enabled, diagnostic information will be added to the action output"
required: true
default: "false"
outputs:
major:
description: "Current major number"
@ -89,6 +93,8 @@ outputs:
description: "Indicates the previous version"
current_commit:
description: "The current commit hash"
debug_output:
description: "Diagnostic information, if debug is enabled"
runs:
using: "node16"

162
dist/index.js vendored
View file

@ -42,7 +42,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.cmd = void 0;
// Using require instead of import to support integration testing
const exec = __importStar(__nccwpck_require__(1514));
const DebugManager_1 = __nccwpck_require__(1823);
const debugManager = DebugManager_1.DebugManager.getInstance();
const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* () {
if (debugManager.isReplayMode()) {
return debugManager.replayCommand(command, args);
}
let output = '', errors = '';
const options = {
silent: true,
@ -53,15 +58,14 @@ const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* ()
silent: true
}
};
let caughtError = null;
try {
yield exec.exec(command, args, options);
}
catch (err) {
//core.info(`The command cd '${command} ${args.join(' ')}' failed: ${err}`);
}
if (errors !== '') {
//core.info(`stderr: ${errors}`);
caughtError = err;
}
debugManager.recordCommand(command, args, output, errors, caughtError);
return output;
});
exports.cmd = cmd;
@ -85,9 +89,11 @@ const DefaultCurrentCommitResolver_1 = __nccwpck_require__(35);
const DefaultVersionClassifier_1 = __nccwpck_require__(5527);
const DefaultLastReleaseResolver_1 = __nccwpck_require__(8337);
const BumpAlwaysVersionClassifier_1 = __nccwpck_require__(6482);
const DebugManager_1 = __nccwpck_require__(1823);
class ConfigurationProvider {
constructor(config) {
this.config = config;
DebugManager_1.DebugManager.getInstance().setDebugEnabled(config.debug);
}
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
@ -112,6 +118,110 @@ class ConfigurationProvider {
exports.ConfigurationProvider = ConfigurationProvider;
/***/ }),
/***/ 1823:
/***/ ((__unused_webpack_module, exports) => {
"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;
}
/** 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) {
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));
}
}
/***/ }),
/***/ 2010:
@ -138,8 +248,9 @@ class VersionResult {
* @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) {
constructor(major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput) {
this.major = major;
this.minor = minor;
this.patch = patch;
@ -153,6 +264,7 @@ class VersionResult {
this.currentCommit = currentCommit;
this.previousCommit = previousCommit;
this.previousVersion = previousVersion;
this.debugOutput = debugOutput;
}
}
exports.VersionResult = VersionResult;
@ -180,6 +292,7 @@ const VersionResult_1 = __nccwpck_require__(2010);
const VersionType_1 = __nccwpck_require__(895);
const UserInfo_1 = __nccwpck_require__(5907);
const VersionInformation_1 = __nccwpck_require__(5686);
const DebugManager_1 = __nccwpck_require__(1823);
function runAction(configurationProvider) {
return __awaiter(this, void 0, void 0, function* () {
const currentCommitResolver = configurationProvider.GetCurrentCommitResolver();
@ -187,14 +300,15 @@ function runAction(configurationProvider) {
const commitsProvider = configurationProvider.GetCommitsProvider();
const versionClassifier = configurationProvider.GetVersionClassifier();
const versionFormatter = configurationProvider.GetVersionFormatter();
const tagFormmater = configurationProvider.GetTagFormatter();
const tagFormatter = configurationProvider.GetTagFormatter();
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), tagFormmater.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', []), '', '', '0.0.0');
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', []), '', '', '0.0.0', debugManager.getDebugOutput(true));
}
const currentCommit = yield currentCommitResolver.ResolveAsync();
const lastRelease = yield lastReleaseResolver.ResolveAsync(currentCommit, tagFormmater);
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;
@ -213,7 +327,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), tagFormmater.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', authors), currentCommit, lastRelease.hash, `${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`);
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;
@ -434,6 +548,9 @@ function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true',
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern'),
debug: core.getInput('debug') === 'true',
replay: ''
};
if (config.versionFormat === '' && core.getInput('format') !== '') {
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`);
@ -475,7 +592,9 @@ const VersionType_1 = __nccwpck_require__(895);
class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVersionClassifier {
constructor(config) {
super(config);
// Placeholder for consistency
this.patchPattern = !config.bumpEachCommitPatchPattern ?
_ => true :
this.parsePattern(config.bumpEachCommitPatchPattern, "", config.searchCommitBody);
}
ClassifyAsync(lastRelease, commitSet) {
return __awaiter(this, void 0, void 0, function* () {
@ -484,6 +603,7 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
}
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);
}
@ -493,18 +613,28 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
minor = 0;
patch = 0;
type = VersionType_1.VersionType.Major;
increment = 0;
}
else if (this.minorPattern(commit)) {
minor += 1;
patch = 0;
type = VersionType_1.VersionType.Minor;
increment = 0;
}
else {
patch += 1;
type = VersionType_1.VersionType.Patch;
if (this.patchPattern(commit) ||
(major === 0 && minor === 0 && patch === 0 && commitSet.commits.length > 0)) {
patch += 1;
type = VersionType_1.VersionType.Patch;
increment = 0;
}
else {
type = VersionType_1.VersionType.None;
increment++;
}
}
}
return new VersionClassification_1.VersionClassification(type, 0, true, major, minor, patch);
return new VersionClassification_1.VersionClassification(type, increment, true, major, minor, patch);
});
}
}
@ -826,8 +956,10 @@ class DefaultVersionClassifier {
this.enablePrereleaseMode = config.enablePrereleaseMode;
}
parsePattern(pattern, flags, searchBody) {
if (pattern.startsWith('/') && pattern.endsWith('/')) {
var regex = new RegExp(pattern.slice(1, -1), flags);
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);

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

View file

@ -32,6 +32,12 @@ 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;
/** 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 = null;
}
}
exports.ActionConfig = ActionConfig;

View file

@ -35,7 +35,12 @@ 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 debugManager = DebugManager_1.DebugManager.getInstance();
const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* () {
if (debugManager.isReplayMode()) {
return debugManager.replayCommand(command, args);
}
let output = '', errors = '';
const options = {
silent: true,
@ -46,15 +51,14 @@ const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* ()
silent: true
}
};
let caughtError = null;
try {
yield exec.exec(command, args, options);
}
catch (err) {
//core.info(`The command cd '${command} ${args.join(' ')}' failed: ${err}`);
}
if (errors !== '') {
//core.info(`stderr: ${errors}`);
caughtError = err;
}
debugManager.recordCommand(command, args, output, errors, caughtError);
return output;
});
exports.cmd = cmd;

View file

@ -10,9 +10,11 @@ const DefaultCurrentCommitResolver_1 = require("./providers/DefaultCurrentCommit
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().setDebugEnabled(config.debug);
}
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }

96
lib/DebugManager.js Normal file
View file

@ -0,0 +1,96 @@
"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;
}
/** 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) {
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));
}
}

View file

@ -18,8 +18,9 @@ class VersionResult {
* @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) {
constructor(major, minor, patch, increment, versionType, formattedVersion, versionTag, changed, isTagged, authors, currentCommit, previousCommit, previousVersion, debugOutput) {
this.major = major;
this.minor = minor;
this.patch = patch;
@ -33,6 +34,7 @@ class VersionResult {
this.currentCommit = currentCommit;
this.previousCommit = previousCommit;
this.previousVersion = previousVersion;
this.debugOutput = debugOutput;
}
}
exports.VersionResult = VersionResult;

View file

@ -14,6 +14,7 @@ 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();
@ -21,14 +22,15 @@ function runAction(configurationProvider) {
const commitsProvider = configurationProvider.GetCommitsProvider();
const versionClassifier = configurationProvider.GetVersionClassifier();
const versionFormatter = configurationProvider.GetVersionFormatter();
const tagFormmater = configurationProvider.GetTagFormatter();
const tagFormatter = configurationProvider.GetTagFormatter();
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), tagFormmater.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', []), '', '', '0.0.0');
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', []), '', '', '0.0.0', debugManager.getDebugOutput(true));
}
const currentCommit = yield currentCommitResolver.ResolveAsync();
const lastRelease = yield lastReleaseResolver.ResolveAsync(currentCommit, tagFormmater);
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;
@ -47,7 +49,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), tagFormmater.Format(versionInfo), versionInfo.changed, versionInfo.isTagged, userFormatter.Format('author', authors), currentCommit, lastRelease.hash, `${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`);
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;

View file

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

View file

@ -16,7 +16,9 @@ const VersionType_1 = require("./VersionType");
class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVersionClassifier {
constructor(config) {
super(config);
// Placeholder for consistency
this.patchPattern = !config.bumpEachCommitPatchPattern ?
_ => true :
this.parsePattern(config.bumpEachCommitPatchPattern, "", config.searchCommitBody);
}
ClassifyAsync(lastRelease, commitSet) {
return __awaiter(this, void 0, void 0, function* () {
@ -25,6 +27,7 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
}
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);
}
@ -34,18 +37,28 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
minor = 0;
patch = 0;
type = VersionType_1.VersionType.Major;
increment = 0;
}
else if (this.minorPattern(commit)) {
minor += 1;
patch = 0;
type = VersionType_1.VersionType.Minor;
increment = 0;
}
else {
patch += 1;
type = VersionType_1.VersionType.Patch;
if (this.patchPattern(commit) ||
(major === 0 && minor === 0 && patch === 0 && commitSet.commits.length > 0)) {
patch += 1;
type = VersionType_1.VersionType.Patch;
increment = 0;
}
else {
type = VersionType_1.VersionType.None;
increment++;
}
}
}
return new VersionClassification_1.VersionClassification(type, 0, true, major, minor, patch);
return new VersionClassification_1.VersionClassification(type, increment, true, major, minor, patch);
});
}
}

View file

@ -20,8 +20,10 @@ class DefaultVersionClassifier {
this.enablePrereleaseMode = config.enablePrereleaseMode;
}
parsePattern(pattern, flags, searchBody) {
if (pattern.startsWith('/') && pattern.endsWith('/')) {
var regex = new RegExp(pattern.slice(1, -1), flags);
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);

View file

@ -30,4 +30,8 @@ export class ActionConfig {
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 = '';
}

View file

@ -1,8 +1,15 @@
// Using require instead of import to support integration testing
import * as exec from '@actions/exec';
import * as core from '@actions/core';
import { DebugManager } from './DebugManager';
const debugManager = DebugManager.getInstance();
export const cmd = async (command: string, ...args: any): Promise<string> => {
if (debugManager.isReplayMode()) {
return debugManager.replayCommand(command, args);
}
let output = '', errors = '';
const options = {
silent: true,
@ -14,15 +21,14 @@ export const cmd = async (command: string, ...args: any): Promise<string> => {
}
};
let caughtError: any = null;
try {
await exec.exec(command, args, options);
} catch (err) {
//core.info(`The command cd '${command} ${args.join(' ')}' failed: ${err}`);
caughtError = err;
}
if (errors !== '') {
//core.info(`stderr: ${errors}`);
}
debugManager.recordCommand(command, args, output, errors, caughtError);
return output;
};

View file

@ -15,11 +15,13 @@ import { DefaultLastReleaseResolver } from './providers/DefaultLastReleaseResolv
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);
}
private config: ActionConfig;

111
src/DebugManager.ts Normal file
View file

@ -0,0 +1,111 @@
import exp from "constants";
import { ActionConfig } from "./ActionConfig";
/** Utility class for managing debug mode and diagnostic information */
export class DebugManager {
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 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');
}
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;
}
}
/** 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;
}
}
/** 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));
}
}

View file

@ -18,6 +18,7 @@ export class VersionResult {
* @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(
public major: number,
@ -32,5 +33,6 @@ export class VersionResult {
public authors: string,
public currentCommit: string,
public previousCommit: string,
public previousVersion: string) { }
public previousVersion: string,
public debugOutput: string) { }
}

View file

@ -3,6 +3,7 @@ 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> {
@ -14,6 +15,8 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
const tagFormatter = configurationProvider.GetTagFormatter();
const userFormatter = configurationProvider.GetUserFormatter();
const debugManager = DebugManager.getInstance();
if (await currentCommitResolver.IsEmptyRepoAsync()) {
const versionInfo = new VersionInformation(0, 0, 0, 0, VersionType.None, [], false, false);
return new VersionResult(
@ -29,7 +32,8 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
userFormatter.Format('author', []),
'',
'',
'0.0.0'
'0.0.0',
debugManager.getDebugOutput(true)
);
}
@ -71,6 +75,7 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
userFormatter.Format('author', authors),
currentCommit,
lastRelease.hash,
`${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`
`${lastRelease.major}.${lastRelease.minor}.${lastRelease.patch}`,
debugManager.getDebugOutput()
);
}

View file

@ -6,6 +6,7 @@ import { expect, test } from '@jest/globals'
import { runAction } from '../src/action';
import { ConfigurationProvider } from './ConfigurationProvider';
import { ActionConfig } from './ActionConfig';
import exp from 'constants';
const windows = process.platform === "win32";
const timeout = 30000;
@ -829,7 +830,7 @@ test('Patch does not increment on bump each commit if a patch pattern is set', a
test('Patch pattern increment is correct on empty repo', async () => {
const repo = createTestRepo({ tagPrefix: '', versionFormat: "${major}.${minor}.${patch}+${increment}", bumpEachCommit: true, bumpEachCommitPatchPattern: '(PATCH)' });
const initialResult = await repo.runAction();
repo.makeCommit('Initial Commit');
const firstResult = await repo.runAction();
@ -851,7 +852,7 @@ test('Patch pattern increment is correct on empty repo', async () => {
test('Patch pattern increment is correct/matches non-bumped on empty repo', async () => {
const repo = createTestRepo({ tagPrefix: '', versionFormat: "${major}.${minor}.${patch}+${increment}", bumpEachCommit: true, bumpEachCommitPatchPattern: '(PATCH)' });
repo.makeCommit('Initial Commit');
const firstResult = await repo.runAction();
repo.makeCommit('Second Commit');
@ -871,7 +872,7 @@ test('Patch pattern increment is correct/matches non-bumped on empty repo', asyn
test('Patch pattern applied when present', async () => {
const repo = createTestRepo({ tagPrefix: '', versionFormat: "${major}.${minor}.${patch}+${increment}", bumpEachCommit: true, bumpEachCommitPatchPattern: '(PATCH)' });
repo.makeCommit('Initial Commit');
const firstResult = await repo.runAction();
repo.makeCommit('Second Commit');
@ -928,11 +929,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');
@ -961,14 +962,14 @@ test('Pre-release mode does not update major version if major version is 0', asy
const repo = createTestRepo({ tagPrefix: '', versionFormat: "${major}.${minor}.${patch}", enablePrereleaseMode: true });
repo.makeCommit('Initial Commit');
expect(( await repo.runAction()).formattedVersion).toBe('0.0.1');
expect((await repo.runAction()).formattedVersion).toBe('0.0.1');
repo.makeCommit('Second Commit (MINOR)');
expect(( await repo.runAction()).formattedVersion).toBe('0.0.1');
expect((await repo.runAction()).formattedVersion).toBe('0.0.1');
repo.makeCommit('Third Commit (MAJOR)');
expect(( await repo.runAction()).formattedVersion).toBe('0.1.0');
expect((await repo.runAction()).formattedVersion).toBe('0.1.0');
repo.exec('git tag 0.1.0');
repo.makeCommit('Fourth Commit (MAJOR)');
expect(( await repo.runAction()).formattedVersion).toBe('0.2.0');
expect((await repo.runAction()).formattedVersion).toBe('0.2.0');
}, timeout);
test('Pre-release mode updates major version if major version is not 0', async () => {
@ -977,14 +978,14 @@ test('Pre-release mode updates major version if major version is not 0', async (
repo.makeCommit('Initial Commit');
repo.exec('git tag 1.0.0');
repo.makeCommit('Second Commit');
expect(( await repo.runAction()).formattedVersion).toBe('1.0.1');
expect((await repo.runAction()).formattedVersion).toBe('1.0.1');
repo.makeCommit('Third Commit (MINOR)');
expect(( await repo.runAction()).formattedVersion).toBe('1.1.0');
expect((await repo.runAction()).formattedVersion).toBe('1.1.0');
repo.makeCommit('Fourth Commit (MAJOR)');
expect(( await repo.runAction()).formattedVersion).toBe('2.0.0');
expect((await repo.runAction()).formattedVersion).toBe('2.0.0');
repo.exec('git tag 2.0.0');
repo.makeCommit('Fifth Commit (MAJOR)');
expect(( await repo.runAction()).formattedVersion).toBe('3.0.0');
expect((await repo.runAction()).formattedVersion).toBe('3.0.0');
}, timeout);
test('Tagged commit is flagged as release', async () => {
@ -1020,7 +1021,7 @@ test('Highest tag is chosen when multiple tags are present', async () => {
repo.makeCommit('Initial Commit');
repo.exec('git tag v2.0.0');
repo.exec('git tag v1.0.0');
repo.exec('git tag v1.5.0');
repo.exec('git tag v1.5.0');
var result = await repo.runAction();
expect(result.formattedVersion).toBe('2.0.0+0');
@ -1033,4 +1034,27 @@ test('Highest tag is chosen when multiple tags are present', async () => {
result = await repo.runAction();
expect(result.formattedVersion).toBe('3.0.1+0');
}, timeout);
}, timeout);
test('Debug records and replays expected data', async () => {
const repo = createTestRepo();
repo.makeCommit('Initial Commit');
repo.exec('git tag v3.0.0');
repo.makeCommit(`Second Commit`);
const result = await repo.runAction({ debug: true });
repo.makeCommit(`Breaking Commit`);
repo.exec('git tag v9.9.9');
var replayResult = await repo.runAction({ replay: result.debugOutput });
expect(replayResult.major).toBe(result.major);
expect(replayResult.minor).toBe(result.minor);
expect(replayResult.patch).toBe(result.patch);
expect(replayResult.increment).toBe(result.increment);
}, timeout);

View file

@ -51,7 +51,9 @@ export async function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true',
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern')
bumpEachCommitPatchPattern: core.getInput('bump_each_commit_patch_pattern'),
debug: core.getInput('debug') === 'true',
replay: ''
};
if (config.versionFormat === '' && core.getInput('format') !== '') {