versionary 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -11
- package/dist/{simple → app/release}/pr.d.ts +7 -4
- package/dist/{simple → app/release}/pr.js +89 -74
- package/dist/app/release/recovery.d.ts +17 -0
- package/dist/app/release/recovery.js +101 -0
- package/dist/{simple → app/release}/release.js +37 -24
- package/dist/app/release/state.d.ts +10 -0
- package/dist/{simple → app/release}/state.js +16 -4
- package/dist/app/release/verify.d.ts +2 -0
- package/dist/app/release/verify.js +5 -0
- package/dist/cli/index.js +12 -10
- package/dist/config/load-config.js +2 -12
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +1 -0
- package/dist/domain/release/changelog.d.ts +12 -0
- package/dist/domain/release/changelog.js +123 -0
- package/dist/{simple → domain/release}/plan.d.ts +4 -3
- package/dist/{simple → domain/release}/plan.js +32 -15
- package/dist/domain/release/semver.d.ts +15 -0
- package/dist/domain/release/semver.js +111 -0
- package/dist/domain/strategy/node.d.ts +2 -0
- package/dist/domain/strategy/node.js +37 -0
- package/dist/domain/strategy/resolve.d.ts +3 -0
- package/dist/domain/strategy/resolve.js +11 -0
- package/dist/domain/strategy/simple.d.ts +2 -0
- package/dist/domain/strategy/simple.js +28 -0
- package/dist/domain/strategy/types.d.ts +7 -0
- package/dist/domain/strategy/types.js +2 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +5 -1
- package/dist/infra/git/commits.d.ts +65 -0
- package/dist/infra/git/commits.js +436 -0
- package/dist/infra/scm/github/plugin.d.ts +1 -0
- package/dist/infra/scm/github/plugin.js +5 -0
- package/dist/infra/scm/runtime.d.ts +2 -0
- package/dist/infra/scm/runtime.js +24 -0
- package/dist/infra/scm/types.d.ts +1 -0
- package/dist/infra/scm/types.js +2 -0
- package/dist/plugins/runtime.d.ts +1 -2
- package/dist/plugins/runtime.js +3 -22
- package/dist/scm/github-plugin.js +133 -40
- package/dist/strategies/node.d.ts +1 -0
- package/dist/strategies/node.js +5 -0
- package/dist/strategies/resolve.d.ts +1 -0
- package/dist/strategies/resolve.js +5 -0
- package/dist/strategies/simple.d.ts +1 -0
- package/dist/strategies/simple.js +5 -0
- package/dist/strategies/types.d.ts +1 -0
- package/dist/strategies/types.js +2 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/plugins.d.ts +1 -0
- package/dist/verify/verify-project.js +3 -1
- package/package.json +1 -1
- package/dist/simple/changelog.d.ts +0 -3
- package/dist/simple/changelog.js +0 -46
- package/dist/simple/git.d.ts +0 -10
- package/dist/simple/git.js +0 -114
- package/dist/simple/semver.d.ts +0 -8
- package/dist/simple/semver.js +0 -25
- package/dist/simple/state.d.ts +0 -3
- /package/dist/{simple → app/release}/release.d.ts +0 -0
- /package/dist/{simple → infra/git}/repo-url.d.ts +0 -0
- /package/dist/{simple → infra/git}/repo-url.js +0 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.nodeVersionStrategy = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
exports.nodeVersionStrategy = {
|
|
10
|
+
name: "node",
|
|
11
|
+
getVersionFile(config) {
|
|
12
|
+
return config["version-file"] ?? "version.txt";
|
|
13
|
+
},
|
|
14
|
+
readVersion(cwd, config) {
|
|
15
|
+
const versionFile = this.getVersionFile(config);
|
|
16
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
17
|
+
if (!node_fs_1.default.existsSync(versionPath)) {
|
|
18
|
+
throw new Error(`Versionary requires ${versionFile} to exist.`);
|
|
19
|
+
}
|
|
20
|
+
return node_fs_1.default.readFileSync(versionPath, "utf8").trim();
|
|
21
|
+
},
|
|
22
|
+
writeVersion(cwd, config, version) {
|
|
23
|
+
const versionFile = this.getVersionFile(config);
|
|
24
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
25
|
+
node_fs_1.default.writeFileSync(versionPath, `${version}\n`, "utf8");
|
|
26
|
+
const updatedFiles = [versionFile];
|
|
27
|
+
const packageJsonPath = node_path_1.default.join(cwd, "package.json");
|
|
28
|
+
if (node_fs_1.default.existsSync(packageJsonPath)) {
|
|
29
|
+
const packageJsonRaw = node_fs_1.default.readFileSync(packageJsonPath, "utf8");
|
|
30
|
+
const packageJson = JSON.parse(packageJsonRaw);
|
|
31
|
+
packageJson.version = version;
|
|
32
|
+
node_fs_1.default.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, "utf8");
|
|
33
|
+
updatedFiles.push("package.json");
|
|
34
|
+
}
|
|
35
|
+
return updatedFiles;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveVersionStrategy = resolveVersionStrategy;
|
|
4
|
+
const node_js_1 = require("./node.js");
|
|
5
|
+
const simple_js_1 = require("./simple.js");
|
|
6
|
+
function resolveVersionStrategy(config) {
|
|
7
|
+
if (config["release-type"] === "node") {
|
|
8
|
+
return node_js_1.nodeVersionStrategy;
|
|
9
|
+
}
|
|
10
|
+
return simple_js_1.simpleVersionStrategy;
|
|
11
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.simpleVersionStrategy = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
exports.simpleVersionStrategy = {
|
|
10
|
+
name: "simple",
|
|
11
|
+
getVersionFile(config) {
|
|
12
|
+
return config["version-file"] ?? "version.txt";
|
|
13
|
+
},
|
|
14
|
+
readVersion(cwd, config) {
|
|
15
|
+
const versionFile = this.getVersionFile(config);
|
|
16
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
17
|
+
if (!node_fs_1.default.existsSync(versionPath)) {
|
|
18
|
+
throw new Error(`Versionary requires ${versionFile} to exist.`);
|
|
19
|
+
}
|
|
20
|
+
return node_fs_1.default.readFileSync(versionPath, "utf8").trim();
|
|
21
|
+
},
|
|
22
|
+
writeVersion(cwd, config, version) {
|
|
23
|
+
const versionFile = this.getVersionFile(config);
|
|
24
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
25
|
+
node_fs_1.default.writeFileSync(versionPath, `${version}\n`, "utf8");
|
|
26
|
+
return [versionFile];
|
|
27
|
+
},
|
|
28
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VersionaryConfig } from "../../types/config.js";
|
|
2
|
+
export interface VersionStrategy {
|
|
3
|
+
name: string;
|
|
4
|
+
getVersionFile(config: VersionaryConfig): string;
|
|
5
|
+
readVersion(cwd: string, config: VersionaryConfig): string;
|
|
6
|
+
writeVersion(cwd: string, config: VersionaryConfig, version: string): string[];
|
|
7
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export type { VersionaryConfig, VersionaryPackage, VersionaryArtifactRule, } from "./types/config.js";
|
|
2
|
-
export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, } from "./types/plugins.js";
|
|
3
1
|
export { loadConfig } from "./config/load-config.js";
|
|
4
|
-
export {
|
|
2
|
+
export { resolveVersionStrategy } from "./domain/strategy/resolve.js";
|
|
3
|
+
export { createGitHubPlugin } from "./infra/scm/github/plugin.js";
|
|
4
|
+
export { findPluginsByCapability, pluginHasCapability, } from "./plugins/capabilities.js";
|
|
5
5
|
export { loadRuntimePlugins } from "./plugins/runtime.js";
|
|
6
|
+
export type { VersionaryArtifactRule, VersionaryConfig, VersionaryPackage, } from "./types/config.js";
|
|
7
|
+
export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, } from "./types/plugins.js";
|
|
6
8
|
export { verifyProject } from "./verify/verify-project.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.verifyProject = exports.loadRuntimePlugins = exports.pluginHasCapability = exports.findPluginsByCapability = exports.loadConfig = void 0;
|
|
3
|
+
exports.verifyProject = exports.loadRuntimePlugins = exports.pluginHasCapability = exports.findPluginsByCapability = exports.createGitHubPlugin = exports.resolveVersionStrategy = exports.loadConfig = void 0;
|
|
4
4
|
var load_config_js_1 = require("./config/load-config.js");
|
|
5
5
|
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return load_config_js_1.loadConfig; } });
|
|
6
|
+
var resolve_js_1 = require("./domain/strategy/resolve.js");
|
|
7
|
+
Object.defineProperty(exports, "resolveVersionStrategy", { enumerable: true, get: function () { return resolve_js_1.resolveVersionStrategy; } });
|
|
8
|
+
var plugin_js_1 = require("./infra/scm/github/plugin.js");
|
|
9
|
+
Object.defineProperty(exports, "createGitHubPlugin", { enumerable: true, get: function () { return plugin_js_1.createGitHubPlugin; } });
|
|
6
10
|
var capabilities_js_1 = require("./plugins/capabilities.js");
|
|
7
11
|
Object.defineProperty(exports, "findPluginsByCapability", { enumerable: true, get: function () { return capabilities_js_1.findPluginsByCapability; } });
|
|
8
12
|
Object.defineProperty(exports, "pluginHasCapability", { enumerable: true, get: function () { return capabilities_js_1.pluginHasCapability; } });
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ReleaseType } from "../../domain/release/semver.js";
|
|
2
|
+
export interface CommitInfo {
|
|
3
|
+
hash: string;
|
|
4
|
+
subject: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ParsedCommit {
|
|
7
|
+
hash: string;
|
|
8
|
+
subject: string;
|
|
9
|
+
body: string;
|
|
10
|
+
fullMessage: string;
|
|
11
|
+
type: string | null;
|
|
12
|
+
scope: string | null;
|
|
13
|
+
description: string;
|
|
14
|
+
isBreaking: boolean;
|
|
15
|
+
isRevert: boolean;
|
|
16
|
+
footers: Array<{
|
|
17
|
+
token: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}>;
|
|
20
|
+
revertedShas: string[];
|
|
21
|
+
header?: string;
|
|
22
|
+
merge?: string | null;
|
|
23
|
+
footer?: string | null;
|
|
24
|
+
notes?: CommitNote[];
|
|
25
|
+
references?: CommitReference[];
|
|
26
|
+
mentions?: string[];
|
|
27
|
+
revert?: RevertInfo | null;
|
|
28
|
+
isConventional?: boolean;
|
|
29
|
+
diagnostics?: ParseDiagnostic[];
|
|
30
|
+
}
|
|
31
|
+
export type CommitParseDiagnosticCode = "invalid-header" | "malformed-breaking-footer" | "malformed-reference" | "ambiguous-revert";
|
|
32
|
+
export interface ParseDiagnostic {
|
|
33
|
+
code: CommitParseDiagnosticCode;
|
|
34
|
+
message: string;
|
|
35
|
+
}
|
|
36
|
+
export interface CommitNote {
|
|
37
|
+
title: string;
|
|
38
|
+
text: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CommitReference {
|
|
41
|
+
action: string | null;
|
|
42
|
+
owner: string | null;
|
|
43
|
+
repository: string | null;
|
|
44
|
+
issue: string | null;
|
|
45
|
+
raw: string;
|
|
46
|
+
prefix: "#" | "GH-";
|
|
47
|
+
}
|
|
48
|
+
export interface RevertInfo {
|
|
49
|
+
header: string;
|
|
50
|
+
hashes: string[];
|
|
51
|
+
}
|
|
52
|
+
export declare function parseConventionalCommitMessage(subject: string, body?: string): ParsedCommit;
|
|
53
|
+
export declare function parseConventionalCommitMessageDetailed(header: string, body?: string): ParsedCommit;
|
|
54
|
+
export declare function getCommitsSinceLastTag(cwd?: string, baselineSha?: string | null): CommitInfo[];
|
|
55
|
+
export declare function getCommitsForPath(cwd?: string, baselineSha?: string | null, packagePath?: string, excludePaths?: string[]): CommitInfo[];
|
|
56
|
+
export declare function getParsedCommitsSinceLastTag(cwd?: string, baselineSha?: string | null): ParsedCommit[];
|
|
57
|
+
export declare function getParsedCommitsForPath(cwd?: string, baselineSha?: string | null, packagePath?: string, excludePaths?: string[]): ParsedCommit[];
|
|
58
|
+
export declare function inferReleaseTypeFromParsedCommit(commit: ParsedCommit): ReleaseType;
|
|
59
|
+
export declare function inferReleaseTypeFromSubject(subject: string): ReleaseType;
|
|
60
|
+
export declare function isReleasableCommit(subject: string): boolean;
|
|
61
|
+
export declare function getCommitParseDiagnostics(commit: ParsedCommit): ParseDiagnostic[];
|
|
62
|
+
export declare function analyzeCommits(commits: CommitInfo[]): ReleaseType;
|
|
63
|
+
export declare function applyRevertSuppression(commits: ParsedCommit[]): ParsedCommit[];
|
|
64
|
+
export declare function analyzeParsedCommits(commits: ParsedCommit[]): ReleaseType;
|
|
65
|
+
export declare function isReleasableParsedCommit(commit: ParsedCommit): boolean;
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseConventionalCommitMessage = parseConventionalCommitMessage;
|
|
7
|
+
exports.parseConventionalCommitMessageDetailed = parseConventionalCommitMessageDetailed;
|
|
8
|
+
exports.getCommitsSinceLastTag = getCommitsSinceLastTag;
|
|
9
|
+
exports.getCommitsForPath = getCommitsForPath;
|
|
10
|
+
exports.getParsedCommitsSinceLastTag = getParsedCommitsSinceLastTag;
|
|
11
|
+
exports.getParsedCommitsForPath = getParsedCommitsForPath;
|
|
12
|
+
exports.inferReleaseTypeFromParsedCommit = inferReleaseTypeFromParsedCommit;
|
|
13
|
+
exports.inferReleaseTypeFromSubject = inferReleaseTypeFromSubject;
|
|
14
|
+
exports.isReleasableCommit = isReleasableCommit;
|
|
15
|
+
exports.getCommitParseDiagnostics = getCommitParseDiagnostics;
|
|
16
|
+
exports.analyzeCommits = analyzeCommits;
|
|
17
|
+
exports.applyRevertSuppression = applyRevertSuppression;
|
|
18
|
+
exports.analyzeParsedCommits = analyzeParsedCommits;
|
|
19
|
+
exports.isReleasableParsedCommit = isReleasableParsedCommit;
|
|
20
|
+
const node_child_process_1 = require("node:child_process");
|
|
21
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
22
|
+
function getReleaseBranchExcludeArgs(cwd) {
|
|
23
|
+
const releaseBranchesRaw = (0, node_child_process_1.execFileSync)("git", ["branch", "--list", "--format", "%(refname:short)"], {
|
|
24
|
+
cwd,
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27
|
+
});
|
|
28
|
+
const releaseBranches = releaseBranchesRaw
|
|
29
|
+
.split("\n")
|
|
30
|
+
.map((line) => line.trim())
|
|
31
|
+
.filter((line) => line.startsWith("versionary/release"));
|
|
32
|
+
return releaseBranches.flatMap((branch) => ["--exclude", branch]);
|
|
33
|
+
}
|
|
34
|
+
function getLatestReleaseTag(cwd) {
|
|
35
|
+
const excludeArgs = getReleaseBranchExcludeArgs(cwd);
|
|
36
|
+
try {
|
|
37
|
+
const cmd = [
|
|
38
|
+
"describe",
|
|
39
|
+
"--tags",
|
|
40
|
+
"--abbrev=0",
|
|
41
|
+
"--match",
|
|
42
|
+
"v[0-9]*",
|
|
43
|
+
...excludeArgs,
|
|
44
|
+
];
|
|
45
|
+
return (0, node_child_process_1.execFileSync)("git", cmd, {
|
|
46
|
+
cwd,
|
|
47
|
+
encoding: "utf8",
|
|
48
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
49
|
+
}).trim();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function isValidCommitish(cwd, ref) {
|
|
56
|
+
try {
|
|
57
|
+
(0, node_child_process_1.execFileSync)("git", ["rev-parse", "--verify", `${ref}^{commit}`], {
|
|
58
|
+
cwd,
|
|
59
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
60
|
+
});
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function resolveRange(cwd, baselineSha) {
|
|
68
|
+
const baseRef = baselineSha ?? "";
|
|
69
|
+
if (!baseRef) {
|
|
70
|
+
const latestTag = getLatestReleaseTag(cwd);
|
|
71
|
+
return latestTag ? `${latestTag}..HEAD` : "HEAD";
|
|
72
|
+
}
|
|
73
|
+
if (isValidCommitish(cwd, baseRef)) {
|
|
74
|
+
return `${baseRef}..HEAD`;
|
|
75
|
+
}
|
|
76
|
+
const latestTag = getLatestReleaseTag(cwd);
|
|
77
|
+
return latestTag ? `${latestTag}..HEAD` : "HEAD";
|
|
78
|
+
}
|
|
79
|
+
function readGitLog(cwd, range, pathspecs = []) {
|
|
80
|
+
const output = (0, node_child_process_1.execFileSync)("git", ["log", range, "--pretty=format:%H%x09%s", "--", ...pathspecs], {
|
|
81
|
+
cwd,
|
|
82
|
+
encoding: "utf8",
|
|
83
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
84
|
+
});
|
|
85
|
+
if (!output.trim()) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
return output
|
|
89
|
+
.trim()
|
|
90
|
+
.split("\n")
|
|
91
|
+
.map((line) => {
|
|
92
|
+
const [hash, subject] = line.split("\t");
|
|
93
|
+
return { hash, subject };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function readGitLogFull(cwd, range, pathspecs = []) {
|
|
97
|
+
const recordSeparator = "\x1e";
|
|
98
|
+
const fieldSeparator = "\x1f";
|
|
99
|
+
const output = (0, node_child_process_1.execFileSync)("git", [
|
|
100
|
+
"log",
|
|
101
|
+
range,
|
|
102
|
+
`--pretty=format:%H${fieldSeparator}%s${fieldSeparator}%b${recordSeparator}`,
|
|
103
|
+
"--",
|
|
104
|
+
...pathspecs,
|
|
105
|
+
], {
|
|
106
|
+
cwd,
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
109
|
+
});
|
|
110
|
+
if (!output.trim()) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
return output
|
|
114
|
+
.split(recordSeparator)
|
|
115
|
+
.map((record) => record.trim())
|
|
116
|
+
.filter((record) => record.length > 0)
|
|
117
|
+
.map((record) => {
|
|
118
|
+
const [hash = "", subject = "", body = ""] = record.split(fieldSeparator);
|
|
119
|
+
return parseCommitMessage(hash, subject.trim(), body.trim());
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function parseHeader(subject) {
|
|
123
|
+
const conventionalMatch = subject.match(/^([a-z][a-z0-9-]*)(?:\(([^)]+)\))?(!)?:\s+(.+)$/iu);
|
|
124
|
+
if (!conventionalMatch) {
|
|
125
|
+
return {
|
|
126
|
+
type: null,
|
|
127
|
+
scope: null,
|
|
128
|
+
description: subject,
|
|
129
|
+
isBreakingHeader: false,
|
|
130
|
+
isConventional: false,
|
|
131
|
+
diagnostics: [
|
|
132
|
+
{
|
|
133
|
+
code: "invalid-header",
|
|
134
|
+
message: "Header does not match Conventional Commits format: <type>(<scope>)?: <description>.",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
type: conventionalMatch[1]?.toLowerCase() ?? null,
|
|
141
|
+
scope: conventionalMatch[2]?.trim() ?? null,
|
|
142
|
+
isBreakingHeader: conventionalMatch[3] === "!",
|
|
143
|
+
description: conventionalMatch[4]?.trim() ?? subject,
|
|
144
|
+
isConventional: true,
|
|
145
|
+
diagnostics: [],
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function parseFooters(body) {
|
|
149
|
+
if (!body) {
|
|
150
|
+
return {
|
|
151
|
+
bodyText: "",
|
|
152
|
+
footerText: null,
|
|
153
|
+
footers: [],
|
|
154
|
+
diagnostics: [],
|
|
155
|
+
references: [],
|
|
156
|
+
notes: [],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const parseFooterLine = (line) => {
|
|
160
|
+
const colonMatch = line.match(/^(BREAKING CHANGE|BREAKING-CHANGE|[A-Za-z][A-Za-z0-9-]*):\s+(.+)$/u);
|
|
161
|
+
if (colonMatch) {
|
|
162
|
+
return {
|
|
163
|
+
token: colonMatch[1] ?? "",
|
|
164
|
+
value: colonMatch[2] ?? "",
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const issueRefMatch = line.match(/^([A-Za-z][A-Za-z0-9-]*)\s+((?:GH-|#).+)$/u);
|
|
168
|
+
if (issueRefMatch) {
|
|
169
|
+
return {
|
|
170
|
+
token: issueRefMatch[1] ?? "",
|
|
171
|
+
value: issueRefMatch[2] ?? "",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
};
|
|
176
|
+
const diagnostics = [];
|
|
177
|
+
const lines = body.split("\n");
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
if (/^\s*BREAKING(?:\s|-)?CHANGE\b/iu.test(line) &&
|
|
180
|
+
!/^\s*(BREAKING CHANGE|BREAKING-CHANGE):\s+/iu.test(line)) {
|
|
181
|
+
diagnostics.push({
|
|
182
|
+
code: "malformed-breaking-footer",
|
|
183
|
+
message: "Found BREAKING CHANGE-like footer without required ': ' separator.",
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const footerStart = lines.findIndex((line) => parseFooterLine(line) !== null);
|
|
188
|
+
const bodyLines = footerStart < 0 ? lines : lines.slice(0, footerStart);
|
|
189
|
+
const footerLines = footerStart < 0 ? [] : lines.slice(footerStart);
|
|
190
|
+
const footers = [];
|
|
191
|
+
const references = [];
|
|
192
|
+
const notes = [];
|
|
193
|
+
let current = null;
|
|
194
|
+
const pushCurrent = () => {
|
|
195
|
+
if (!current) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
footers.push({
|
|
199
|
+
token: current.token,
|
|
200
|
+
value: current.value.trim(),
|
|
201
|
+
});
|
|
202
|
+
current = null;
|
|
203
|
+
};
|
|
204
|
+
const extractReferences = (text, action) => {
|
|
205
|
+
const refs = text.match(/(?:GH-|#)[^\s,;:()]+/gu) ?? [];
|
|
206
|
+
for (const raw of refs) {
|
|
207
|
+
const issue = raw.replace(/^(GH-|#)/u, "");
|
|
208
|
+
if (!/^\d+$/u.test(issue)) {
|
|
209
|
+
diagnostics.push({
|
|
210
|
+
code: "malformed-reference",
|
|
211
|
+
message: `Malformed issue reference "${raw}" in footer.`,
|
|
212
|
+
});
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
references.push({
|
|
216
|
+
action,
|
|
217
|
+
owner: null,
|
|
218
|
+
repository: null,
|
|
219
|
+
issue,
|
|
220
|
+
raw,
|
|
221
|
+
prefix: raw.startsWith("GH-") ? "GH-" : "#",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
for (const line of footerLines) {
|
|
226
|
+
const footerMatch = parseFooterLine(line);
|
|
227
|
+
if (footerMatch) {
|
|
228
|
+
pushCurrent();
|
|
229
|
+
current = {
|
|
230
|
+
token: footerMatch.token,
|
|
231
|
+
value: footerMatch.value,
|
|
232
|
+
};
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (current && line.trim().length > 0) {
|
|
236
|
+
current.value = `${current.value}\n${line}`;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
pushCurrent();
|
|
240
|
+
}
|
|
241
|
+
pushCurrent();
|
|
242
|
+
for (const footer of footers) {
|
|
243
|
+
if (/^(BREAKING CHANGE|BREAKING-CHANGE)$/iu.test(footer.token)) {
|
|
244
|
+
notes.push({
|
|
245
|
+
title: "BREAKING CHANGE",
|
|
246
|
+
text: footer.value,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
extractReferences(`${footer.token}: ${footer.value}`, footer.token);
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
bodyText: bodyLines.join("\n").trim() || "",
|
|
253
|
+
footerText: footerLines.length > 0 ? footerLines.join("\n").trim() : null,
|
|
254
|
+
footers,
|
|
255
|
+
diagnostics,
|
|
256
|
+
references,
|
|
257
|
+
notes,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function extractRevertedShas(subject, body) {
|
|
261
|
+
const full = `${subject}\n${body}`;
|
|
262
|
+
const matches = full.match(/\b[0-9a-f]{7,40}\b/giu) ?? [];
|
|
263
|
+
return [...new Set(matches.map((sha) => sha.toLowerCase()))];
|
|
264
|
+
}
|
|
265
|
+
function extractMentions(text) {
|
|
266
|
+
const mentions = text.match(/(?:^|[^A-Za-z0-9_])@([A-Za-z0-9][A-Za-z0-9-]{0,38})/gu) ?? [];
|
|
267
|
+
return [
|
|
268
|
+
...new Set(mentions
|
|
269
|
+
.map((mention) => mention.replace(/^.*@/u, ""))
|
|
270
|
+
.filter((mention) => mention.length > 0)),
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
function parseCommitMessage(hash, subject, body) {
|
|
274
|
+
const header = parseHeader(subject);
|
|
275
|
+
const footerResult = parseFooters(body);
|
|
276
|
+
const bodyText = footerResult.bodyText;
|
|
277
|
+
const footers = footerResult.footers;
|
|
278
|
+
const hasBreakingFooter = footers.some((footer) => /^(BREAKING CHANGE|BREAKING-CHANGE)$/iu.test(footer.token));
|
|
279
|
+
const isRevert = (header.type?.toLowerCase() ?? "") === "revert" ||
|
|
280
|
+
/^revert:\s/i.test(subject);
|
|
281
|
+
const revertInfo = isRevert
|
|
282
|
+
? {
|
|
283
|
+
header: subject,
|
|
284
|
+
hashes: extractRevertedShas(subject, body),
|
|
285
|
+
}
|
|
286
|
+
: null;
|
|
287
|
+
const diagnostics = [...header.diagnostics, ...footerResult.diagnostics];
|
|
288
|
+
if (isRevert && revertInfo && revertInfo.hashes.length === 0) {
|
|
289
|
+
diagnostics.push({
|
|
290
|
+
code: "ambiguous-revert",
|
|
291
|
+
message: "Revert commit detected but no reverted commit SHA reference was parsed.",
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
hash,
|
|
296
|
+
subject,
|
|
297
|
+
body: bodyText,
|
|
298
|
+
fullMessage: body ? `${subject}\n\n${body}` : subject,
|
|
299
|
+
type: header.type,
|
|
300
|
+
scope: header.scope,
|
|
301
|
+
description: header.description,
|
|
302
|
+
isBreaking: header.isBreakingHeader || hasBreakingFooter,
|
|
303
|
+
isRevert,
|
|
304
|
+
footers,
|
|
305
|
+
revertedShas: isRevert ? extractRevertedShas(subject, body) : [],
|
|
306
|
+
header: subject,
|
|
307
|
+
merge: null,
|
|
308
|
+
footer: footerResult.footerText,
|
|
309
|
+
notes: footerResult.notes,
|
|
310
|
+
references: footerResult.references,
|
|
311
|
+
mentions: extractMentions(`${subject}\n${body}`),
|
|
312
|
+
revert: revertInfo,
|
|
313
|
+
isConventional: header.isConventional,
|
|
314
|
+
diagnostics,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
function parseConventionalCommitMessage(subject, body = "") {
|
|
318
|
+
return parseCommitMessage("", subject.trim(), body.trim());
|
|
319
|
+
}
|
|
320
|
+
function parseConventionalCommitMessageDetailed(header, body = "") {
|
|
321
|
+
return parseCommitMessage("", header.trim(), body.trim());
|
|
322
|
+
}
|
|
323
|
+
function getCommitsSinceLastTag(cwd = process.cwd(), baselineSha) {
|
|
324
|
+
const range = resolveRange(cwd, baselineSha);
|
|
325
|
+
return readGitLog(cwd, range);
|
|
326
|
+
}
|
|
327
|
+
function getCommitsForPath(cwd = process.cwd(), baselineSha, packagePath = ".", excludePaths = []) {
|
|
328
|
+
const range = resolveRange(cwd, baselineSha);
|
|
329
|
+
const normalizedPackagePath = packagePath === "." ? "." : packagePath;
|
|
330
|
+
const excludes = excludePaths.map((excludePath) => {
|
|
331
|
+
const combined = normalizedPackagePath === "."
|
|
332
|
+
? excludePath
|
|
333
|
+
: node_path_1.default.posix.join(normalizedPackagePath, excludePath);
|
|
334
|
+
return `:(exclude)${combined}`;
|
|
335
|
+
});
|
|
336
|
+
return readGitLog(cwd, range, [normalizedPackagePath, ...excludes]);
|
|
337
|
+
}
|
|
338
|
+
function getParsedCommitsSinceLastTag(cwd = process.cwd(), baselineSha) {
|
|
339
|
+
const range = resolveRange(cwd, baselineSha);
|
|
340
|
+
return readGitLogFull(cwd, range);
|
|
341
|
+
}
|
|
342
|
+
function getParsedCommitsForPath(cwd = process.cwd(), baselineSha, packagePath = ".", excludePaths = []) {
|
|
343
|
+
const range = resolveRange(cwd, baselineSha);
|
|
344
|
+
const normalizedPackagePath = packagePath === "." ? "." : packagePath;
|
|
345
|
+
const excludes = excludePaths.map((excludePath) => {
|
|
346
|
+
const combined = normalizedPackagePath === "."
|
|
347
|
+
? excludePath
|
|
348
|
+
: node_path_1.default.posix.join(normalizedPackagePath, excludePath);
|
|
349
|
+
return `:(exclude)${combined}`;
|
|
350
|
+
});
|
|
351
|
+
return readGitLogFull(cwd, range, [normalizedPackagePath, ...excludes]);
|
|
352
|
+
}
|
|
353
|
+
function inferReleaseTypeFromParsedCommit(commit) {
|
|
354
|
+
if (commit.isRevert) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
if (commit.isBreaking) {
|
|
358
|
+
return "major";
|
|
359
|
+
}
|
|
360
|
+
const type = commit.type?.toLowerCase() ?? "";
|
|
361
|
+
if (type === "feat") {
|
|
362
|
+
return "minor";
|
|
363
|
+
}
|
|
364
|
+
if (type === "fix" || type === "perf") {
|
|
365
|
+
return "patch";
|
|
366
|
+
}
|
|
367
|
+
if (type === "chore") {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
function inferReleaseTypeFromSubject(subject) {
|
|
373
|
+
return inferReleaseTypeFromParsedCommit(parseCommitMessage("", subject.trim(), ""));
|
|
374
|
+
}
|
|
375
|
+
function isReleasableCommit(subject) {
|
|
376
|
+
return inferReleaseTypeFromSubject(subject) !== null;
|
|
377
|
+
}
|
|
378
|
+
function getCommitParseDiagnostics(commit) {
|
|
379
|
+
return commit.diagnostics ?? [];
|
|
380
|
+
}
|
|
381
|
+
function analyzeCommits(commits) {
|
|
382
|
+
let result = null;
|
|
383
|
+
for (const commit of commits) {
|
|
384
|
+
const type = inferReleaseTypeFromSubject(commit.subject);
|
|
385
|
+
if (type === "major") {
|
|
386
|
+
return "major";
|
|
387
|
+
}
|
|
388
|
+
if (type === "minor") {
|
|
389
|
+
result = "minor";
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (type === "patch" && result === null) {
|
|
393
|
+
result = "patch";
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return result;
|
|
397
|
+
}
|
|
398
|
+
function applyRevertSuppression(commits) {
|
|
399
|
+
if (commits.length === 0) {
|
|
400
|
+
return commits;
|
|
401
|
+
}
|
|
402
|
+
const presentShas = new Set(commits.map((commit) => commit.hash.toLowerCase()));
|
|
403
|
+
const reverted = new Set();
|
|
404
|
+
for (const commit of commits) {
|
|
405
|
+
if (!commit.isRevert) {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
for (const sha of commit.revertedShas) {
|
|
409
|
+
if (presentShas.has(sha)) {
|
|
410
|
+
reverted.add(sha);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return commits.filter((commit) => !reverted.has(commit.hash.toLowerCase()));
|
|
415
|
+
}
|
|
416
|
+
function analyzeParsedCommits(commits) {
|
|
417
|
+
let result = null;
|
|
418
|
+
const effectiveCommits = applyRevertSuppression(commits);
|
|
419
|
+
for (const commit of effectiveCommits) {
|
|
420
|
+
const type = inferReleaseTypeFromParsedCommit(commit);
|
|
421
|
+
if (type === "major") {
|
|
422
|
+
return "major";
|
|
423
|
+
}
|
|
424
|
+
if (type === "minor") {
|
|
425
|
+
result = "minor";
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (type === "patch" && result === null) {
|
|
429
|
+
result = "patch";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return result;
|
|
433
|
+
}
|
|
434
|
+
function isReleasableParsedCommit(commit) {
|
|
435
|
+
return inferReleaseTypeFromParsedCommit(commit) !== null;
|
|
436
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createGitHubPlugin } from "../../../scm/github-plugin.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createGitHubPlugin = void 0;
|
|
4
|
+
var github_plugin_js_1 = require("../../../scm/github-plugin.js");
|
|
5
|
+
Object.defineProperty(exports, "createGitHubPlugin", { enumerable: true, get: function () { return github_plugin_js_1.createGitHubPlugin; } });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadRuntimePlugins = loadRuntimePlugins;
|
|
4
|
+
const load_config_js_1 = require("../../config/load-config.js");
|
|
5
|
+
const plugin_js_1 = require("./github/plugin.js");
|
|
6
|
+
const BUILTIN_PLUGIN_FACTORIES = {
|
|
7
|
+
github: plugin_js_1.createGitHubPlugin,
|
|
8
|
+
};
|
|
9
|
+
function loadRuntimePlugins(cwd = process.cwd()) {
|
|
10
|
+
const loaded = (0, load_config_js_1.loadConfig)(cwd);
|
|
11
|
+
const configured = loaded.config.plugins ?? [];
|
|
12
|
+
const plugins = [(0, plugin_js_1.createGitHubPlugin)()];
|
|
13
|
+
const seen = new Set(plugins.map((plugin) => plugin.name));
|
|
14
|
+
for (const name of configured) {
|
|
15
|
+
const factory = BUILTIN_PLUGIN_FACTORIES[name];
|
|
16
|
+
if (!factory || seen.has(name)) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const plugin = factory();
|
|
20
|
+
plugins.push(plugin);
|
|
21
|
+
seen.add(plugin.name);
|
|
22
|
+
}
|
|
23
|
+
return plugins;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { VersionaryPluginCapability, VersionaryPluginContext, VersionaryPluginRuntime, VersionaryScmReleaseMetadataInput, VersionaryScmReleaseMetadataResult, VersionaryScmReviewRequestInput, VersionaryScmReviewRequestResult, } from "../../types/plugins.js";
|