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

158
dist/index.js vendored
View file

@ -42,7 +42,12 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.cmd = void 0; exports.cmd = void 0;
// Using require instead of import to support integration testing // Using require instead of import to support integration testing
const exec = __importStar(__nccwpck_require__(1514)); 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* () { 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 = { const options = {
silent: true, silent: true,
@ -53,15 +58,14 @@ const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* ()
silent: true silent: true
} }
}; };
let caughtError = null;
try { try {
yield exec.exec(command, args, options); yield exec.exec(command, args, options);
} }
catch (err) { 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; return output;
}); });
exports.cmd = cmd; exports.cmd = cmd;
@ -85,9 +89,11 @@ const DefaultCurrentCommitResolver_1 = __nccwpck_require__(35);
const DefaultVersionClassifier_1 = __nccwpck_require__(5527); const DefaultVersionClassifier_1 = __nccwpck_require__(5527);
const DefaultLastReleaseResolver_1 = __nccwpck_require__(8337); const DefaultLastReleaseResolver_1 = __nccwpck_require__(8337);
const BumpAlwaysVersionClassifier_1 = __nccwpck_require__(6482); const BumpAlwaysVersionClassifier_1 = __nccwpck_require__(6482);
const DebugManager_1 = __nccwpck_require__(1823);
class ConfigurationProvider { class ConfigurationProvider {
constructor(config) { constructor(config) {
this.config = config; this.config = config;
DebugManager_1.DebugManager.getInstance().setDebugEnabled(config.debug);
} }
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); } GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); } GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(this.config); }
@ -112,6 +118,110 @@ class ConfigurationProvider {
exports.ConfigurationProvider = 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: /***/ 2010:
@ -138,8 +248,9 @@ class VersionResult {
* @param currentCommit - The current commit hash * @param currentCommit - The current commit hash
* @param previousCommit - The previous commit hash * @param previousCommit - The previous commit hash
* @param previousVersion - The previous version * @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.major = major;
this.minor = minor; this.minor = minor;
this.patch = patch; this.patch = patch;
@ -153,6 +264,7 @@ class VersionResult {
this.currentCommit = currentCommit; this.currentCommit = currentCommit;
this.previousCommit = previousCommit; this.previousCommit = previousCommit;
this.previousVersion = previousVersion; this.previousVersion = previousVersion;
this.debugOutput = debugOutput;
} }
} }
exports.VersionResult = VersionResult; exports.VersionResult = VersionResult;
@ -180,6 +292,7 @@ const VersionResult_1 = __nccwpck_require__(2010);
const VersionType_1 = __nccwpck_require__(895); const VersionType_1 = __nccwpck_require__(895);
const UserInfo_1 = __nccwpck_require__(5907); const UserInfo_1 = __nccwpck_require__(5907);
const VersionInformation_1 = __nccwpck_require__(5686); const VersionInformation_1 = __nccwpck_require__(5686);
const DebugManager_1 = __nccwpck_require__(1823);
function runAction(configurationProvider) { function runAction(configurationProvider) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const currentCommitResolver = configurationProvider.GetCurrentCommitResolver(); const currentCommitResolver = configurationProvider.GetCurrentCommitResolver();
@ -187,14 +300,15 @@ function runAction(configurationProvider) {
const commitsProvider = configurationProvider.GetCommitsProvider(); const commitsProvider = configurationProvider.GetCommitsProvider();
const versionClassifier = configurationProvider.GetVersionClassifier(); const versionClassifier = configurationProvider.GetVersionClassifier();
const versionFormatter = configurationProvider.GetVersionFormatter(); const versionFormatter = configurationProvider.GetVersionFormatter();
const tagFormmater = configurationProvider.GetTagFormatter(); const tagFormatter = configurationProvider.GetTagFormatter();
const userFormatter = configurationProvider.GetUserFormatter(); const userFormatter = configurationProvider.GetUserFormatter();
const debugManager = DebugManager_1.DebugManager.getInstance();
if (yield currentCommitResolver.IsEmptyRepoAsync()) { if (yield currentCommitResolver.IsEmptyRepoAsync()) {
const versionInfo = new VersionInformation_1.VersionInformation(0, 0, 0, 0, VersionType_1.VersionType.None, [], false, false); 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 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 commitSet = yield commitsProvider.GetCommitsAsync(lastRelease.hash, currentCommit);
const classification = yield versionClassifier.ClassifyAsync(lastRelease, commitSet); const classification = yield versionClassifier.ClassifyAsync(lastRelease, commitSet);
const { isTagged } = lastRelease; const { isTagged } = lastRelease;
@ -213,7 +327,7 @@ function runAction(configurationProvider) {
const authors = Object.values(allAuthors) const authors = Object.values(allAuthors)
.map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c)) .map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c))
.sort((a, b) => b.commits - a.commits); .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; exports.runAction = runAction;
@ -434,6 +548,9 @@ function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true', searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'), userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true', 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') !== '') { if (config.versionFormat === '' && core.getInput('format') !== '') {
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`); 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 { class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVersionClassifier {
constructor(config) { constructor(config) {
super(config); super(config);
// Placeholder for consistency this.patchPattern = !config.bumpEachCommitPatchPattern ?
_ => true :
this.parsePattern(config.bumpEachCommitPatchPattern, "", config.searchCommitBody);
} }
ClassifyAsync(lastRelease, commitSet) { ClassifyAsync(lastRelease, commitSet) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -484,6 +603,7 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
} }
let { major, minor, patch } = lastRelease; let { major, minor, patch } = lastRelease;
let type = VersionType_1.VersionType.None; let type = VersionType_1.VersionType.None;
let increment = 0;
if (commitSet.commits.length === 0) { if (commitSet.commits.length === 0) {
return new VersionClassification_1.VersionClassification(type, 0, false, major, minor, patch); return new VersionClassification_1.VersionClassification(type, 0, false, major, minor, patch);
} }
@ -493,18 +613,28 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
minor = 0; minor = 0;
patch = 0; patch = 0;
type = VersionType_1.VersionType.Major; type = VersionType_1.VersionType.Major;
increment = 0;
} }
else if (this.minorPattern(commit)) { else if (this.minorPattern(commit)) {
minor += 1; minor += 1;
patch = 0; patch = 0;
type = VersionType_1.VersionType.Minor; type = VersionType_1.VersionType.Minor;
increment = 0;
} }
else { else {
if (this.patchPattern(commit) ||
(major === 0 && minor === 0 && patch === 0 && commitSet.commits.length > 0)) {
patch += 1; patch += 1;
type = VersionType_1.VersionType.Patch; 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; this.enablePrereleaseMode = config.enablePrereleaseMode;
} }
parsePattern(pattern, flags, searchBody) { parsePattern(pattern, flags, searchBody) {
if (pattern.startsWith('/') && pattern.endsWith('/')) { if (/^\/.+\/[i]*$/.test(pattern)) {
var regex = new RegExp(pattern.slice(1, -1), flags); const regexEnd = pattern.lastIndexOf('/');
const parsedFlags = pattern.slice(pattern.lastIndexOf('/') + 1);
const regex = new RegExp(pattern.slice(1, regexEnd), parsedFlags || flags);
return searchBody ? return searchBody ?
(commit) => regex.test(commit.subject) || regex.test(commit.body) : (commit) => regex.test(commit.subject) || regex.test(commit.body) :
(commit) => regex.test(commit.subject); (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"; 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. */ /** 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; 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; exports.ActionConfig = ActionConfig;

View file

@ -35,7 +35,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.cmd = void 0; exports.cmd = void 0;
// Using require instead of import to support integration testing // Using require instead of import to support integration testing
const exec = __importStar(require("@actions/exec")); 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* () { 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 = { const options = {
silent: true, silent: true,
@ -46,15 +51,14 @@ const cmd = (command, ...args) => __awaiter(void 0, void 0, void 0, function* ()
silent: true silent: true
} }
}; };
let caughtError = null;
try { try {
yield exec.exec(command, args, options); yield exec.exec(command, args, options);
} }
catch (err) { 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; return output;
}); });
exports.cmd = cmd; exports.cmd = cmd;

View file

@ -10,9 +10,11 @@ const DefaultCurrentCommitResolver_1 = require("./providers/DefaultCurrentCommit
const DefaultVersionClassifier_1 = require("./providers/DefaultVersionClassifier"); const DefaultVersionClassifier_1 = require("./providers/DefaultVersionClassifier");
const DefaultLastReleaseResolver_1 = require("./providers/DefaultLastReleaseResolver"); const DefaultLastReleaseResolver_1 = require("./providers/DefaultLastReleaseResolver");
const BumpAlwaysVersionClassifier_1 = require("./providers/BumpAlwaysVersionClassifier"); const BumpAlwaysVersionClassifier_1 = require("./providers/BumpAlwaysVersionClassifier");
const DebugManager_1 = require("./DebugManager");
class ConfigurationProvider { class ConfigurationProvider {
constructor(config) { constructor(config) {
this.config = config; this.config = config;
DebugManager_1.DebugManager.getInstance().setDebugEnabled(config.debug);
} }
GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); } GetCurrentCommitResolver() { return new DefaultCurrentCommitResolver_1.DefaultCurrentCommitResolver(this.config); }
GetLastReleaseResolver() { return new DefaultLastReleaseResolver_1.DefaultLastReleaseResolver(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 currentCommit - The current commit hash
* @param previousCommit - The previous commit hash * @param previousCommit - The previous commit hash
* @param previousVersion - The previous version * @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.major = major;
this.minor = minor; this.minor = minor;
this.patch = patch; this.patch = patch;
@ -33,6 +34,7 @@ class VersionResult {
this.currentCommit = currentCommit; this.currentCommit = currentCommit;
this.previousCommit = previousCommit; this.previousCommit = previousCommit;
this.previousVersion = previousVersion; this.previousVersion = previousVersion;
this.debugOutput = debugOutput;
} }
} }
exports.VersionResult = VersionResult; exports.VersionResult = VersionResult;

View file

@ -14,6 +14,7 @@ const VersionResult_1 = require("./VersionResult");
const VersionType_1 = require("./providers/VersionType"); const VersionType_1 = require("./providers/VersionType");
const UserInfo_1 = require("./providers/UserInfo"); const UserInfo_1 = require("./providers/UserInfo");
const VersionInformation_1 = require("./providers/VersionInformation"); const VersionInformation_1 = require("./providers/VersionInformation");
const DebugManager_1 = require("./DebugManager");
function runAction(configurationProvider) { function runAction(configurationProvider) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const currentCommitResolver = configurationProvider.GetCurrentCommitResolver(); const currentCommitResolver = configurationProvider.GetCurrentCommitResolver();
@ -21,14 +22,15 @@ function runAction(configurationProvider) {
const commitsProvider = configurationProvider.GetCommitsProvider(); const commitsProvider = configurationProvider.GetCommitsProvider();
const versionClassifier = configurationProvider.GetVersionClassifier(); const versionClassifier = configurationProvider.GetVersionClassifier();
const versionFormatter = configurationProvider.GetVersionFormatter(); const versionFormatter = configurationProvider.GetVersionFormatter();
const tagFormmater = configurationProvider.GetTagFormatter(); const tagFormatter = configurationProvider.GetTagFormatter();
const userFormatter = configurationProvider.GetUserFormatter(); const userFormatter = configurationProvider.GetUserFormatter();
const debugManager = DebugManager_1.DebugManager.getInstance();
if (yield currentCommitResolver.IsEmptyRepoAsync()) { if (yield currentCommitResolver.IsEmptyRepoAsync()) {
const versionInfo = new VersionInformation_1.VersionInformation(0, 0, 0, 0, VersionType_1.VersionType.None, [], false, false); 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 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 commitSet = yield commitsProvider.GetCommitsAsync(lastRelease.hash, currentCommit);
const classification = yield versionClassifier.ClassifyAsync(lastRelease, commitSet); const classification = yield versionClassifier.ClassifyAsync(lastRelease, commitSet);
const { isTagged } = lastRelease; const { isTagged } = lastRelease;
@ -47,7 +49,7 @@ function runAction(configurationProvider) {
const authors = Object.values(allAuthors) const authors = Object.values(allAuthors)
.map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c)) .map((u) => new UserInfo_1.UserInfo(u.n, u.e, u.c))
.sort((a, b) => b.commits - a.commits); .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; exports.runAction = runAction;

View file

@ -78,6 +78,9 @@ function run() {
searchCommitBody: core.getInput('search_commit_body') === 'true', searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'), userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true', 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') !== '') { if (config.versionFormat === '' && core.getInput('format') !== '') {
core.warning(`The 'format' input is deprecated, use 'versionFormat' instead`); 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 { class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVersionClassifier {
constructor(config) { constructor(config) {
super(config); super(config);
// Placeholder for consistency this.patchPattern = !config.bumpEachCommitPatchPattern ?
_ => true :
this.parsePattern(config.bumpEachCommitPatchPattern, "", config.searchCommitBody);
} }
ClassifyAsync(lastRelease, commitSet) { ClassifyAsync(lastRelease, commitSet) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
@ -25,6 +27,7 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
} }
let { major, minor, patch } = lastRelease; let { major, minor, patch } = lastRelease;
let type = VersionType_1.VersionType.None; let type = VersionType_1.VersionType.None;
let increment = 0;
if (commitSet.commits.length === 0) { if (commitSet.commits.length === 0) {
return new VersionClassification_1.VersionClassification(type, 0, false, major, minor, patch); return new VersionClassification_1.VersionClassification(type, 0, false, major, minor, patch);
} }
@ -34,18 +37,28 @@ class BumpAlwaysVersionClassifier extends DefaultVersionClassifier_1.DefaultVers
minor = 0; minor = 0;
patch = 0; patch = 0;
type = VersionType_1.VersionType.Major; type = VersionType_1.VersionType.Major;
increment = 0;
} }
else if (this.minorPattern(commit)) { else if (this.minorPattern(commit)) {
minor += 1; minor += 1;
patch = 0; patch = 0;
type = VersionType_1.VersionType.Minor; type = VersionType_1.VersionType.Minor;
increment = 0;
} }
else { else {
if (this.patchPattern(commit) ||
(major === 0 && minor === 0 && patch === 0 && commitSet.commits.length > 0)) {
patch += 1; patch += 1;
type = VersionType_1.VersionType.Patch; 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; this.enablePrereleaseMode = config.enablePrereleaseMode;
} }
parsePattern(pattern, flags, searchBody) { parsePattern(pattern, flags, searchBody) {
if (pattern.startsWith('/') && pattern.endsWith('/')) { if (/^\/.+\/[i]*$/.test(pattern)) {
var regex = new RegExp(pattern.slice(1, -1), flags); const regexEnd = pattern.lastIndexOf('/');
const parsedFlags = pattern.slice(pattern.lastIndexOf('/') + 1);
const regex = new RegExp(pattern.slice(1, regexEnd), parsedFlags || flags);
return searchBody ? return searchBody ?
(commit) => regex.test(commit.subject) || regex.test(commit.body) : (commit) => regex.test(commit.subject) || regex.test(commit.body) :
(commit) => regex.test(commit.subject); (commit) => regex.test(commit.subject);

View file

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

View file

@ -15,11 +15,13 @@ import { DefaultLastReleaseResolver } from './providers/DefaultLastReleaseResolv
import { VersionClassifier } from './providers/VersionClassifier' import { VersionClassifier } from './providers/VersionClassifier'
import { BumpAlwaysVersionClassifier } from './providers/BumpAlwaysVersionClassifier' import { BumpAlwaysVersionClassifier } from './providers/BumpAlwaysVersionClassifier'
import { ActionConfig } from './ActionConfig'; import { ActionConfig } from './ActionConfig';
import { DebugManager } from './DebugManager';
export class ConfigurationProvider { export class ConfigurationProvider {
constructor(config: ActionConfig) { constructor(config: ActionConfig) {
this.config = config; this.config = config;
DebugManager.getInstance().initializeConfig(config);
} }
private config: ActionConfig; 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 currentCommit - The current commit hash
* @param previousCommit - The previous commit hash * @param previousCommit - The previous commit hash
* @param previousVersion - The previous version * @param previousVersion - The previous version
* @param debugOutput - Diagnostic information, if debug is enabled
*/ */
constructor( constructor(
public major: number, public major: number,
@ -32,5 +33,6 @@ export class VersionResult {
public authors: string, public authors: string,
public currentCommit: string, public currentCommit: string,
public previousCommit: 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 { VersionType } from './providers/VersionType';
import { UserInfo } from './providers/UserInfo'; import { UserInfo } from './providers/UserInfo';
import { VersionInformation } from './providers/VersionInformation'; import { VersionInformation } from './providers/VersionInformation';
import { DebugManager } from './DebugManager';
export async function runAction(configurationProvider: ConfigurationProvider): Promise<VersionResult> { export async function runAction(configurationProvider: ConfigurationProvider): Promise<VersionResult> {
@ -14,6 +15,8 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
const tagFormatter = configurationProvider.GetTagFormatter(); const tagFormatter = configurationProvider.GetTagFormatter();
const userFormatter = configurationProvider.GetUserFormatter(); const userFormatter = configurationProvider.GetUserFormatter();
const debugManager = DebugManager.getInstance();
if (await currentCommitResolver.IsEmptyRepoAsync()) { 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( return new VersionResult(
@ -29,7 +32,8 @@ export async function runAction(configurationProvider: ConfigurationProvider): P
userFormatter.Format('author', []), 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), userFormatter.Format('author', authors),
currentCommit, currentCommit,
lastRelease.hash, 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 { runAction } from '../src/action';
import { ConfigurationProvider } from './ConfigurationProvider'; import { ConfigurationProvider } from './ConfigurationProvider';
import { ActionConfig } from './ActionConfig'; import { ActionConfig } from './ActionConfig';
import exp from 'constants';
const windows = process.platform === "win32"; const windows = process.platform === "win32";
const timeout = 30000; const timeout = 30000;
@ -1034,3 +1035,26 @@ test('Highest tag is chosen when multiple tags are present', async () => {
expect(result.formattedVersion).toBe('3.0.1+0'); 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', searchCommitBody: core.getInput('search_commit_body') === 'true',
userFormatType: core.getInput('user_format_type'), userFormatType: core.getInput('user_format_type'),
enablePrereleaseMode: core.getInput('enable_prerelease_mode') === 'true', 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') !== '') { if (config.versionFormat === '' && core.getInput('format') !== '') {