package-versioner 0.1.1 → 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 +36 -3
- package/dist/index.cjs +726 -478
- package/dist/index.js +726 -479
- package/docs/CI_CD_INTEGRATION.md +165 -0
- package/docs/VERSIONING_STRATEGIES.md +96 -0
- package/package.json +6 -5
package/dist/index.cjs
CHANGED
|
@@ -49,101 +49,136 @@ function loadConfig(configPath) {
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
// src/
|
|
53
|
-
var
|
|
52
|
+
// src/core/versionEngine.ts
|
|
53
|
+
var import_node_process5 = require("process");
|
|
54
|
+
var import_get_packages = require("@manypkg/get-packages");
|
|
55
|
+
|
|
56
|
+
// src/errors/gitError.ts
|
|
57
|
+
var GitError = class extends Error {
|
|
58
|
+
constructor(message, code) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.name = "GitError";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function createGitError(code, details) {
|
|
65
|
+
const messages = {
|
|
66
|
+
["NOT_GIT_REPO" /* NOT_GIT_REPO */]: "Not a git repository",
|
|
67
|
+
["GIT_PROCESS_ERROR" /* GIT_PROCESS_ERROR */]: "Failed to create new version",
|
|
68
|
+
["NO_FILES" /* NO_FILES */]: "No files specified for commit",
|
|
69
|
+
["NO_COMMIT_MESSAGE" /* NO_COMMIT_MESSAGE */]: "Commit message is required",
|
|
70
|
+
["GIT_ERROR" /* GIT_ERROR */]: "Git operation failed"
|
|
71
|
+
};
|
|
72
|
+
const baseMessage = messages[code];
|
|
73
|
+
const fullMessage = details ? `${baseMessage}: ${details}` : baseMessage;
|
|
74
|
+
return new GitError(fullMessage, code);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/errors/versionError.ts
|
|
78
|
+
var VersionError = class extends Error {
|
|
79
|
+
constructor(message, code) {
|
|
80
|
+
super(message);
|
|
81
|
+
this.code = code;
|
|
82
|
+
this.name = "VersionError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
function createVersionError(code, details) {
|
|
86
|
+
const messages = {
|
|
87
|
+
["CONFIG_REQUIRED" /* CONFIG_REQUIRED */]: "Configuration is required",
|
|
88
|
+
["PACKAGES_NOT_FOUND" /* PACKAGES_NOT_FOUND */]: "Failed to get packages information",
|
|
89
|
+
["WORKSPACE_ERROR" /* WORKSPACE_ERROR */]: "Failed to get workspace packages",
|
|
90
|
+
["INVALID_CONFIG" /* INVALID_CONFIG */]: "Invalid configuration",
|
|
91
|
+
["PACKAGE_NOT_FOUND" /* PACKAGE_NOT_FOUND */]: "Package not found",
|
|
92
|
+
["VERSION_CALCULATION_ERROR" /* VERSION_CALCULATION_ERROR */]: "Failed to calculate version"
|
|
93
|
+
};
|
|
94
|
+
const baseMessage = messages[code];
|
|
95
|
+
const fullMessage = details ? `${baseMessage}: ${details}` : baseMessage;
|
|
96
|
+
return new VersionError(fullMessage, code);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/utils/logging.ts
|
|
54
100
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
55
101
|
var import_figlet = __toESM(require("figlet"), 1);
|
|
56
102
|
|
|
57
|
-
//
|
|
58
|
-
var
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
main: "./dist/index.js",
|
|
64
|
-
module: "./dist/index.mjs",
|
|
65
|
-
types: "./dist/index.d.ts",
|
|
66
|
-
author: {
|
|
67
|
-
name: "Sam Maister",
|
|
68
|
-
email: "goosewobbler@protonmail.com"
|
|
69
|
-
},
|
|
70
|
-
repository: {
|
|
71
|
-
type: "git",
|
|
72
|
-
url: "https://github.com/goosewobbler/package-versioner",
|
|
73
|
-
homepage: "https://github.com/goosewobbler/package-versioner"
|
|
74
|
-
},
|
|
75
|
-
keywords: [
|
|
76
|
-
"version",
|
|
77
|
-
"semver",
|
|
78
|
-
"git",
|
|
79
|
-
"package"
|
|
80
|
-
],
|
|
81
|
-
license: "MIT",
|
|
82
|
-
files: [
|
|
83
|
-
"dist/**",
|
|
84
|
-
"package-versioner.schema.json"
|
|
85
|
-
],
|
|
86
|
-
bin: {
|
|
87
|
-
"package-versioner": "./dist/index.js"
|
|
88
|
-
},
|
|
89
|
-
scripts: {
|
|
90
|
-
build: "tsup src/index.ts --format esm,cjs --dts",
|
|
91
|
-
dev: "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
92
|
-
clean: "rm -rf node_modules && rm -rf dist",
|
|
93
|
-
test: "vitest run --coverage",
|
|
94
|
-
"test:watch": "vitest --coverage",
|
|
95
|
-
lint: "biome check .",
|
|
96
|
-
"lint:fix": "biome check --apply .",
|
|
97
|
-
format: "biome format --write .",
|
|
98
|
-
"format:check": "biome format .",
|
|
99
|
-
fix: "pnpm run lint:fix && pnpm run format",
|
|
100
|
-
prepare: "husky"
|
|
101
|
-
},
|
|
102
|
-
"lint-staged": {
|
|
103
|
-
"*.{js,ts,jsx,tsx}": [
|
|
104
|
-
"biome check --apply",
|
|
105
|
-
"biome format --write"
|
|
106
|
-
]
|
|
107
|
-
},
|
|
108
|
-
devDependencies: {
|
|
109
|
-
"@biomejs/biome": "^1.9.4",
|
|
110
|
-
"@types/figlet": "^1.5.5",
|
|
111
|
-
"@types/node": "^22.14.0",
|
|
112
|
-
"@types/semver": "^7.3.13",
|
|
113
|
-
"@vitest/coverage-v8": "^3.1.1",
|
|
114
|
-
husky: "^9.1.7",
|
|
115
|
-
"lint-staged": "^15.5.0",
|
|
116
|
-
tsup: "^8.4.0",
|
|
117
|
-
typescript: "^5.8.3",
|
|
118
|
-
vitest: "^3.1.1"
|
|
119
|
-
},
|
|
120
|
-
dependencies: {
|
|
121
|
-
"@manypkg/get-packages": "^2.2.2",
|
|
122
|
-
chalk: "^5.4.1",
|
|
123
|
-
commander: "^13.1.0",
|
|
124
|
-
"conventional-changelog-angular": "^8.0.0",
|
|
125
|
-
"conventional-recommended-bump": "^11.0.0",
|
|
126
|
-
figlet: "^1.8.0",
|
|
127
|
-
"git-semver-tags": "^8.0.0",
|
|
128
|
-
semver: "^7.7.1"
|
|
129
|
-
},
|
|
130
|
-
packageManager: "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971"
|
|
103
|
+
// src/utils/jsonOutput.ts
|
|
104
|
+
var _jsonOutputMode = false;
|
|
105
|
+
var _jsonData = {
|
|
106
|
+
dryRun: false,
|
|
107
|
+
updates: [],
|
|
108
|
+
tags: []
|
|
131
109
|
};
|
|
110
|
+
function enableJsonOutput(dryRun = false) {
|
|
111
|
+
_jsonOutputMode = true;
|
|
112
|
+
_jsonData.dryRun = dryRun;
|
|
113
|
+
_jsonData.updates = [];
|
|
114
|
+
_jsonData.tags = [];
|
|
115
|
+
_jsonData.commitMessage = void 0;
|
|
116
|
+
}
|
|
117
|
+
function isJsonOutputMode() {
|
|
118
|
+
return _jsonOutputMode;
|
|
119
|
+
}
|
|
120
|
+
function addPackageUpdate(packageName, newVersion, filePath) {
|
|
121
|
+
if (!_jsonOutputMode) return;
|
|
122
|
+
_jsonData.updates.push({
|
|
123
|
+
packageName,
|
|
124
|
+
newVersion,
|
|
125
|
+
filePath
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function addTag(tag) {
|
|
129
|
+
if (!_jsonOutputMode) return;
|
|
130
|
+
_jsonData.tags.push(tag);
|
|
131
|
+
}
|
|
132
|
+
function setCommitMessage(message) {
|
|
133
|
+
if (!_jsonOutputMode) return;
|
|
134
|
+
_jsonData.commitMessage = message;
|
|
135
|
+
}
|
|
136
|
+
function printJsonOutput() {
|
|
137
|
+
if (_jsonOutputMode) {
|
|
138
|
+
console.log(JSON.stringify(_jsonData, null, 2));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
132
141
|
|
|
133
|
-
// src/utils.ts
|
|
134
|
-
|
|
142
|
+
// src/utils/logging.ts
|
|
143
|
+
function log(message, status = "info") {
|
|
144
|
+
if (isJsonOutputMode() && status !== "error") {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
let chalkFn;
|
|
148
|
+
switch (status) {
|
|
149
|
+
case "success":
|
|
150
|
+
chalkFn = import_chalk.default.green;
|
|
151
|
+
break;
|
|
152
|
+
case "warning":
|
|
153
|
+
chalkFn = import_chalk.default.yellow;
|
|
154
|
+
break;
|
|
155
|
+
case "error":
|
|
156
|
+
chalkFn = import_chalk.default.red;
|
|
157
|
+
break;
|
|
158
|
+
case "debug":
|
|
159
|
+
chalkFn = import_chalk.default.gray;
|
|
160
|
+
break;
|
|
161
|
+
default:
|
|
162
|
+
chalkFn = import_chalk.default.blue;
|
|
163
|
+
}
|
|
164
|
+
console.log(chalkFn(message));
|
|
165
|
+
}
|
|
135
166
|
|
|
136
|
-
// src/
|
|
137
|
-
var
|
|
138
|
-
var
|
|
139
|
-
|
|
167
|
+
// src/core/versionStrategies.ts
|
|
168
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
169
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
170
|
+
|
|
171
|
+
// src/git/commands.ts
|
|
140
172
|
var import_node_process2 = require("process");
|
|
141
|
-
|
|
173
|
+
|
|
174
|
+
// src/git/commandExecutor.ts
|
|
175
|
+
var import_node_child_process = require("child_process");
|
|
176
|
+
var execAsync = (command, options) => {
|
|
177
|
+
const defaultOptions = { maxBuffer: 1024 * 1024 * 10, ...options };
|
|
142
178
|
return new Promise((resolve, reject) => {
|
|
143
|
-
const options = { maxBuffer: 1024 * 1024 * 10 };
|
|
144
179
|
(0, import_node_child_process.exec)(
|
|
145
180
|
command,
|
|
146
|
-
|
|
181
|
+
defaultOptions,
|
|
147
182
|
(error, stdout, stderr) => {
|
|
148
183
|
if (error) {
|
|
149
184
|
reject(error);
|
|
@@ -155,6 +190,32 @@ var execAsync = (command) => {
|
|
|
155
190
|
});
|
|
156
191
|
};
|
|
157
192
|
var execSync = (command, args) => (0, import_node_child_process.execSync)(command, { maxBuffer: 1024 * 1024 * 10, ...args });
|
|
193
|
+
|
|
194
|
+
// src/git/repository.ts
|
|
195
|
+
var import_node_fs = require("fs");
|
|
196
|
+
var import_node_path = require("path");
|
|
197
|
+
function isGitRepository(directory) {
|
|
198
|
+
const gitDir = (0, import_node_path.join)(directory, ".git");
|
|
199
|
+
if (!(0, import_node_fs.existsSync)(gitDir)) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
const stats = (0, import_node_fs.statSync)(gitDir);
|
|
203
|
+
if (!stats.isDirectory()) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
execSync("git rev-parse --is-inside-work-tree", { cwd: directory });
|
|
208
|
+
return true;
|
|
209
|
+
} catch (_error) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function getCurrentBranch() {
|
|
214
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD");
|
|
215
|
+
return result.toString().trim();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/git/commands.ts
|
|
158
219
|
async function gitAdd(files) {
|
|
159
220
|
const command = `git add ${files.join(" ")}`;
|
|
160
221
|
return execAsync(command);
|
|
@@ -181,36 +242,12 @@ async function createGitTag(options) {
|
|
|
181
242
|
const command = `git tag -a -m "${message}" ${tag} ${args}`;
|
|
182
243
|
return execAsync(command);
|
|
183
244
|
}
|
|
184
|
-
function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return Number(amount);
|
|
189
|
-
} catch {
|
|
190
|
-
return 0;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function isGitRepository(directory) {
|
|
194
|
-
const gitDir = (0, import_node_path.join)(directory, ".git");
|
|
195
|
-
if (!(0, import_node_fs.existsSync)(gitDir)) {
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
const stats = (0, import_node_fs.statSync)(gitDir);
|
|
199
|
-
if (!stats.isDirectory()) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
try {
|
|
203
|
-
execSync("git rev-parse --is-inside-work-tree", { cwd: directory });
|
|
204
|
-
return true;
|
|
205
|
-
} catch (_error) {
|
|
206
|
-
return false;
|
|
245
|
+
async function gitProcess(options) {
|
|
246
|
+
const { files, nextTag, commitMessage, skipHooks, dryRun } = options;
|
|
247
|
+
if (!isGitRepository((0, import_node_process2.cwd)())) {
|
|
248
|
+
throw createGitError("NOT_GIT_REPO" /* NOT_GIT_REPO */);
|
|
207
249
|
}
|
|
208
|
-
}
|
|
209
|
-
async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun }) {
|
|
210
250
|
try {
|
|
211
|
-
if (!isGitRepository((0, import_node_process2.cwd)())) {
|
|
212
|
-
throw new Error("Not a git repository (or any parent up to mount point /)");
|
|
213
|
-
}
|
|
214
251
|
if (!dryRun) {
|
|
215
252
|
await gitAdd(files);
|
|
216
253
|
await gitCommit({
|
|
@@ -225,498 +262,709 @@ async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun })
|
|
|
225
262
|
});
|
|
226
263
|
}
|
|
227
264
|
} else {
|
|
228
|
-
log("
|
|
265
|
+
log("[DRY RUN] Would add files:", "info");
|
|
229
266
|
for (const file of files) {
|
|
230
|
-
log(
|
|
267
|
+
log(` - ${file}`, "info");
|
|
231
268
|
}
|
|
232
|
-
log(
|
|
269
|
+
log(`[DRY RUN] Would commit with message: "${commitMessage}"`, "info");
|
|
233
270
|
if (nextTag) {
|
|
234
|
-
log(
|
|
271
|
+
log(`[DRY RUN] Would create tag: ${nextTag}`, "info");
|
|
235
272
|
}
|
|
236
273
|
}
|
|
237
274
|
} catch (err) {
|
|
238
|
-
console.log(err);
|
|
239
275
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
240
|
-
throw
|
|
276
|
+
throw createGitError("GIT_PROCESS_ERROR" /* GIT_PROCESS_ERROR */, errorMessage);
|
|
241
277
|
}
|
|
242
278
|
}
|
|
243
|
-
async function
|
|
279
|
+
async function createGitCommitAndTag(files, nextTag, commitMessage, skipHooks, dryRun) {
|
|
244
280
|
try {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
281
|
+
if (!files || files.length === 0) {
|
|
282
|
+
throw createGitError("NO_FILES" /* NO_FILES */);
|
|
283
|
+
}
|
|
284
|
+
if (!commitMessage) {
|
|
285
|
+
throw createGitError("NO_COMMIT_MESSAGE" /* NO_COMMIT_MESSAGE */);
|
|
286
|
+
}
|
|
287
|
+
setCommitMessage(commitMessage);
|
|
288
|
+
if (nextTag) {
|
|
289
|
+
addTag(nextTag);
|
|
290
|
+
}
|
|
291
|
+
await gitProcess({
|
|
292
|
+
files,
|
|
293
|
+
nextTag,
|
|
294
|
+
commitMessage,
|
|
295
|
+
skipHooks,
|
|
296
|
+
dryRun
|
|
297
|
+
});
|
|
298
|
+
if (!dryRun) {
|
|
299
|
+
log(`Created tag: ${nextTag}`, "success");
|
|
300
|
+
}
|
|
249
301
|
} catch (error) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
302
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
303
|
+
log(`Failed to create git commit and tag: ${errorMessage}`, "error");
|
|
304
|
+
if (error instanceof Error) {
|
|
305
|
+
console.error(error.stack || error.message);
|
|
306
|
+
} else {
|
|
307
|
+
console.error(error);
|
|
308
|
+
}
|
|
309
|
+
throw new GitError(`Git operation failed: ${errorMessage}`, "GIT_ERROR" /* GIT_ERROR */);
|
|
255
310
|
}
|
|
256
311
|
}
|
|
257
|
-
function getCurrentBranch() {
|
|
258
|
-
const result = execSync("git rev-parse --abbrev-ref HEAD");
|
|
259
|
-
return result.toString().trim();
|
|
260
|
-
}
|
|
261
312
|
|
|
262
|
-
// src/
|
|
263
|
-
|
|
264
|
-
const font = "Standard";
|
|
265
|
-
import_figlet.default.text(package_default.name, { font }, (err, data) => {
|
|
266
|
-
if (err) {
|
|
267
|
-
log("warning", "Could not print figlet banner: Figlet error");
|
|
268
|
-
console.error(err);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
if (data) {
|
|
272
|
-
const figletText = data;
|
|
273
|
-
const versionText = `v${package_default.version}`;
|
|
274
|
-
process.stdout.write(`${import_chalk.default.hex("#FF1F57")(figletText)}
|
|
275
|
-
`);
|
|
276
|
-
process.stdout.write(`${import_chalk.default.hex("#0096FF")(versionText)}
|
|
313
|
+
// src/git/tagsAndBranches.ts
|
|
314
|
+
var import_git_semver_tags = require("git-semver-tags");
|
|
277
315
|
|
|
278
|
-
|
|
316
|
+
// src/utils/formatting.ts
|
|
317
|
+
function escapeRegExp(string) {
|
|
318
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
319
|
+
}
|
|
320
|
+
function formatTag(version, tagPrefix) {
|
|
321
|
+
if (!tagPrefix) return version;
|
|
322
|
+
return tagPrefix.endsWith("/") ? `${tagPrefix}${version}` : `${tagPrefix}/${version}`;
|
|
323
|
+
}
|
|
324
|
+
function formatTagPrefix(tagPrefix, scope) {
|
|
325
|
+
if (!tagPrefix) return "";
|
|
326
|
+
const prefix = tagPrefix.replace(/\/$/, "");
|
|
327
|
+
if (scope) {
|
|
328
|
+
return `${prefix}/${scope}`;
|
|
329
|
+
}
|
|
330
|
+
return prefix;
|
|
331
|
+
}
|
|
332
|
+
function formatCommitMessage(template, version, scope) {
|
|
333
|
+
return createTemplateString(template, { version, scope });
|
|
334
|
+
}
|
|
335
|
+
function createTemplateString(template, variables) {
|
|
336
|
+
return Object.entries(variables).reduce((result, [key, value]) => {
|
|
337
|
+
if (value === void 0) {
|
|
338
|
+
return result;
|
|
279
339
|
}
|
|
280
|
-
|
|
340
|
+
const regex = new RegExp(`\\$\\{${key}\\}`, "g");
|
|
341
|
+
return result.replace(regex, value);
|
|
342
|
+
}, template);
|
|
281
343
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
344
|
+
|
|
345
|
+
// src/git/tagsAndBranches.ts
|
|
346
|
+
function getCommitsLength(pkgRoot) {
|
|
347
|
+
try {
|
|
348
|
+
const gitCommand = `git rev-list --count HEAD ^$(git describe --tags --abbrev=0) ${pkgRoot}`;
|
|
349
|
+
const amount = execSync(gitCommand).toString().trim();
|
|
350
|
+
return Number(amount);
|
|
351
|
+
} catch (error) {
|
|
352
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
353
|
+
log(`Failed to get number of commits since last tag: ${errorMessage}`, "error");
|
|
354
|
+
return 0;
|
|
355
|
+
}
|
|
291
356
|
}
|
|
292
357
|
async function getLatestTag() {
|
|
293
358
|
try {
|
|
294
359
|
const tags = await (0, import_git_semver_tags.getSemverTags)({});
|
|
295
360
|
return tags[0] || "";
|
|
296
361
|
} catch (error) {
|
|
297
|
-
|
|
298
|
-
|
|
362
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
363
|
+
log(`Failed to get latest tag: ${errorMessage}`, "error");
|
|
299
364
|
if (error instanceof Error && error.message.includes("No names found")) {
|
|
300
|
-
log("
|
|
365
|
+
log("No tags found in the repository.", "info");
|
|
301
366
|
}
|
|
302
367
|
return "";
|
|
303
368
|
}
|
|
304
369
|
}
|
|
305
|
-
function
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
370
|
+
async function lastMergeBranchName(branches, baseBranch) {
|
|
371
|
+
try {
|
|
372
|
+
const escapedBranches = branches.map((branch) => escapeRegExp(branch));
|
|
373
|
+
const branchesRegex = `${escapedBranches.join("/(.*)|")}/(.*)`;
|
|
374
|
+
const command = `git for-each-ref --sort=-committerdate --format='%(refname:short)' refs/heads --merged ${baseBranch} | grep -o -i -E "${branchesRegex}" | awk -F'[ ]' '{print $1}' | head -n 1`;
|
|
375
|
+
const { stdout } = await execAsync(command);
|
|
376
|
+
return stdout.trim();
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error(
|
|
379
|
+
"Error while getting the last branch name:",
|
|
380
|
+
error instanceof Error ? error.message : String(error)
|
|
381
|
+
);
|
|
382
|
+
return null;
|
|
310
383
|
}
|
|
311
|
-
return `${tagPrefix ? tagPrefix : "v"}${version}`;
|
|
312
|
-
}
|
|
313
|
-
function formatTagPrefix(tagPrefix) {
|
|
314
|
-
return tagPrefix ? `${tagPrefix}@` : "";
|
|
315
|
-
}
|
|
316
|
-
function createTemplateString(template, data) {
|
|
317
|
-
return template.replace(/\$\{([^}]+)\}/g, (_, key) => data[key] || "");
|
|
318
384
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
385
|
+
|
|
386
|
+
// src/package/packageManagement.ts
|
|
387
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
388
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
389
|
+
function updatePackageVersion(packagePath, version) {
|
|
323
390
|
try {
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
391
|
+
const packageContent = import_node_fs2.default.readFileSync(packagePath, "utf8");
|
|
392
|
+
const packageJson = JSON.parse(packageContent);
|
|
393
|
+
const packageName = packageJson.name;
|
|
394
|
+
packageJson.version = version;
|
|
395
|
+
import_node_fs2.default.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}
|
|
329
396
|
`);
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
log("info", `[DRY RUN] Would update ${name} package.json to version ${version}`);
|
|
333
|
-
}
|
|
397
|
+
addPackageUpdate(packageName, version, packagePath);
|
|
398
|
+
log(`Updated package.json at ${packagePath} to version ${version}`, "success");
|
|
334
399
|
} catch (error) {
|
|
335
|
-
log(
|
|
336
|
-
|
|
400
|
+
log(`Failed to update package.json at ${packagePath}`, "error");
|
|
401
|
+
if (error instanceof Error) {
|
|
402
|
+
log(error.message, "error");
|
|
403
|
+
}
|
|
404
|
+
throw error;
|
|
337
405
|
}
|
|
338
406
|
}
|
|
339
407
|
|
|
340
|
-
// src/
|
|
341
|
-
var
|
|
342
|
-
var
|
|
408
|
+
// src/package/packageProcessor.ts
|
|
409
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
410
|
+
var import_node_process4 = require("process");
|
|
411
|
+
|
|
412
|
+
// src/core/versionCalculator.ts
|
|
343
413
|
var import_node_process3 = require("process");
|
|
344
|
-
var import_get_packages = require("@manypkg/get-packages");
|
|
345
414
|
var import_conventional_recommended_bump = require("conventional-recommended-bump");
|
|
346
415
|
var import_semver = __toESM(require("semver"), 1);
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
416
|
+
async function calculateVersion(config, options) {
|
|
417
|
+
const { latestTag, type, path: path4, name, branchPattern, prereleaseIdentifier } = options;
|
|
418
|
+
const originalPrefix = config.tagPrefix || "v";
|
|
419
|
+
const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
|
|
420
|
+
function determineTagSearchPattern(packageName, prefix) {
|
|
421
|
+
if (packageName) {
|
|
422
|
+
return prefix ? `${prefix}${packageName}@` : `${packageName}@`;
|
|
423
|
+
}
|
|
424
|
+
return prefix ? `${prefix}v` : "v";
|
|
351
425
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
426
|
+
const tagSearchPattern = determineTagSearchPattern(name, originalPrefix);
|
|
427
|
+
const escapedTagPattern = escapeRegExp(tagSearchPattern);
|
|
428
|
+
let determinedReleaseType = type || null;
|
|
429
|
+
if (determinedReleaseType) {
|
|
430
|
+
if (!latestTag) {
|
|
431
|
+
return initialVersion;
|
|
432
|
+
}
|
|
433
|
+
const currentVersion = import_semver.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
434
|
+
return import_semver.default.inc(currentVersion, determinedReleaseType, prereleaseIdentifier) || "";
|
|
435
|
+
}
|
|
436
|
+
if (config.versionStrategy === "branchPattern" && (branchPattern == null ? void 0 : branchPattern.length)) {
|
|
437
|
+
const currentBranch = await getCurrentBranch();
|
|
438
|
+
const mergeBranch = await lastMergeBranchName(branchPattern, config.baseBranch);
|
|
439
|
+
const branch = mergeBranch || currentBranch;
|
|
440
|
+
for (const pattern of branchPattern) {
|
|
441
|
+
const [match, releaseType] = pattern.split(":");
|
|
442
|
+
if (branch.includes(match) && releaseType) {
|
|
443
|
+
determinedReleaseType = releaseType;
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
361
447
|
if (determinedReleaseType) {
|
|
362
448
|
if (!latestTag) {
|
|
363
449
|
return initialVersion;
|
|
364
450
|
}
|
|
365
|
-
const currentVersion = import_semver.default.clean(latestTag.replace(
|
|
451
|
+
const currentVersion = import_semver.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
366
452
|
return import_semver.default.inc(currentVersion, determinedReleaseType, prereleaseIdentifier) || "";
|
|
367
453
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
break;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
if (determinedReleaseType) {
|
|
380
|
-
if (!latestTag) {
|
|
381
|
-
return initialVersion;
|
|
382
|
-
}
|
|
383
|
-
const currentVersion = import_semver.default.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
|
|
384
|
-
return import_semver.default.inc(currentVersion, determinedReleaseType, prereleaseIdentifier) || "";
|
|
385
|
-
}
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
const bumper = new import_conventional_recommended_bump.Bumper();
|
|
457
|
+
bumper.loadPreset(config.preset);
|
|
458
|
+
const recommendedBump = await bumper.bump();
|
|
459
|
+
const releaseTypeFromCommits = recommendedBump.releaseType;
|
|
460
|
+
if (!latestTag) {
|
|
461
|
+
return initialVersion;
|
|
386
462
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
);
|
|
402
|
-
return "";
|
|
403
|
-
}
|
|
404
|
-
if (!releaseTypeFromCommits) {
|
|
405
|
-
log(
|
|
406
|
-
"info",
|
|
407
|
-
`No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`
|
|
408
|
-
);
|
|
409
|
-
return "";
|
|
410
|
-
}
|
|
411
|
-
const currentVersion = import_semver.default.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
|
|
412
|
-
return import_semver.default.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
|
|
413
|
-
} catch (error) {
|
|
414
|
-
log("error", `Failed to calculate version for ${name || "project"}`);
|
|
415
|
-
console.error(error);
|
|
416
|
-
if (error instanceof Error && error.message.includes("No names found")) {
|
|
417
|
-
log("info", "No tags found, proceeding with initial version calculation (if applicable).");
|
|
418
|
-
return initialVersion;
|
|
419
|
-
}
|
|
463
|
+
const checkPath = path4 || (0, import_node_process3.cwd)();
|
|
464
|
+
const commitsLength = getCommitsLength(checkPath);
|
|
465
|
+
if (commitsLength === 0) {
|
|
466
|
+
log(
|
|
467
|
+
`No new commits found for ${name || "project"} since ${latestTag}, skipping version bump`,
|
|
468
|
+
"info"
|
|
469
|
+
);
|
|
470
|
+
return "";
|
|
471
|
+
}
|
|
472
|
+
if (!releaseTypeFromCommits) {
|
|
473
|
+
log(
|
|
474
|
+
`No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`,
|
|
475
|
+
"info"
|
|
476
|
+
);
|
|
420
477
|
return "";
|
|
421
478
|
}
|
|
479
|
+
const currentVersion = import_semver.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
480
|
+
return import_semver.default.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
|
|
481
|
+
} catch (error) {
|
|
482
|
+
log(`Failed to calculate version for ${name || "project"}`, "error");
|
|
483
|
+
console.error(error);
|
|
484
|
+
if (error instanceof Error && error.message.includes("No names found")) {
|
|
485
|
+
log("No tags found, proceeding with initial version calculation (if applicable).", "info");
|
|
486
|
+
return initialVersion;
|
|
487
|
+
}
|
|
488
|
+
throw error;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// src/package/packageProcessor.ts
|
|
493
|
+
var PackageProcessor = class {
|
|
494
|
+
skip;
|
|
495
|
+
targets;
|
|
496
|
+
tagPrefix;
|
|
497
|
+
commitMessageTemplate;
|
|
498
|
+
dryRun;
|
|
499
|
+
skipHooks;
|
|
500
|
+
getLatestTag;
|
|
501
|
+
config;
|
|
502
|
+
// Config for version calculation
|
|
503
|
+
fullConfig;
|
|
504
|
+
constructor(options) {
|
|
505
|
+
this.skip = options.skip || [];
|
|
506
|
+
this.targets = options.targets || [];
|
|
507
|
+
this.tagPrefix = options.tagPrefix || "v";
|
|
508
|
+
this.commitMessageTemplate = options.commitMessageTemplate || "";
|
|
509
|
+
this.dryRun = options.dryRun || false;
|
|
510
|
+
this.skipHooks = options.skipHooks || false;
|
|
511
|
+
this.getLatestTag = options.getLatestTag;
|
|
512
|
+
this.config = options.config;
|
|
513
|
+
this.fullConfig = options.fullConfig;
|
|
422
514
|
}
|
|
423
515
|
/**
|
|
424
|
-
*
|
|
425
|
-
* Returns a list of package.json file paths that were updated (or would be in dry run).
|
|
516
|
+
* Set package targets to process
|
|
426
517
|
*/
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
518
|
+
setTargets(targets) {
|
|
519
|
+
this.targets = targets;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Process packages based on targeting criteria
|
|
523
|
+
*/
|
|
524
|
+
async processPackages(packages) {
|
|
525
|
+
var _a;
|
|
526
|
+
const tags = [];
|
|
527
|
+
const updatedPackagesInfo = [];
|
|
528
|
+
const tagPrefix = this.tagPrefix;
|
|
529
|
+
if (!packages || !Array.isArray(packages)) {
|
|
530
|
+
log("Invalid packages data provided. Expected array of packages.", "error");
|
|
531
|
+
return { updatedPackages: [], tags: [] };
|
|
532
|
+
}
|
|
533
|
+
const pkgsToConsider = packages.filter((pkg) => {
|
|
534
|
+
var _a2;
|
|
535
|
+
const pkgName = pkg.packageJson.name;
|
|
536
|
+
if ((_a2 = this.skip) == null ? void 0 : _a2.includes(pkgName)) {
|
|
537
|
+
log(`Skipping package ${pkgName} as it's in the skip list.`, "info");
|
|
433
538
|
return false;
|
|
434
539
|
}
|
|
435
|
-
if (targets.length
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
540
|
+
if (!this.targets || this.targets.length === 0) {
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
const isTargeted = this.targets.includes(pkgName);
|
|
544
|
+
if (!isTargeted) {
|
|
545
|
+
log(`Package ${pkgName} not in target list, skipping.`, "info");
|
|
440
546
|
}
|
|
441
|
-
return
|
|
547
|
+
return isTargeted;
|
|
442
548
|
});
|
|
443
|
-
log(
|
|
549
|
+
log(`Found ${pkgsToConsider.length} targeted package(s) to process after filtering.`, "info");
|
|
550
|
+
if (pkgsToConsider.length === 0) {
|
|
551
|
+
log("No matching targeted packages found to process.", "info");
|
|
552
|
+
return { updatedPackages: [], tags: [] };
|
|
553
|
+
}
|
|
444
554
|
for (const pkg of pkgsToConsider) {
|
|
445
555
|
const name = pkg.packageJson.name;
|
|
446
556
|
const pkgPath = pkg.dir;
|
|
447
557
|
const prefix = formatTagPrefix(tagPrefix);
|
|
448
|
-
const
|
|
449
|
-
const
|
|
558
|
+
const latestTagResult = await this.getLatestTag();
|
|
559
|
+
const latestTag = latestTagResult || "";
|
|
560
|
+
const nextVersion = await calculateVersion(this.fullConfig, {
|
|
450
561
|
latestTag,
|
|
451
|
-
// This might need refinement for async based on package-specific tags
|
|
452
562
|
tagPrefix: prefix,
|
|
453
563
|
path: pkgPath,
|
|
454
564
|
name,
|
|
455
|
-
// Pass name for potential package-specific tag lookups
|
|
456
565
|
branchPattern: this.config.branchPattern,
|
|
457
566
|
baseBranch: this.config.baseBranch,
|
|
458
567
|
prereleaseIdentifier: this.config.prereleaseIdentifier,
|
|
459
568
|
type: this.config.forceType
|
|
460
|
-
// Pass forced type if provided
|
|
461
569
|
});
|
|
462
570
|
if (!nextVersion) {
|
|
463
571
|
continue;
|
|
464
572
|
}
|
|
465
|
-
updatePackageVersion(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
573
|
+
updatePackageVersion(import_node_path3.default.join(pkgPath, "package.json"), nextVersion);
|
|
574
|
+
const packageTag = formatTag(nextVersion, tagPrefix);
|
|
575
|
+
const tagMessage = `chore(release): ${name} ${nextVersion}`;
|
|
576
|
+
addTag(packageTag);
|
|
577
|
+
tags.push(packageTag);
|
|
578
|
+
if (!this.dryRun) {
|
|
579
|
+
try {
|
|
580
|
+
await createGitTag({ tag: packageTag, message: tagMessage });
|
|
581
|
+
log(`Created tag: ${packageTag}`, "success");
|
|
582
|
+
} catch (tagError) {
|
|
583
|
+
log(
|
|
584
|
+
`Failed to create tag ${packageTag} for ${name}: ${tagError.message}`,
|
|
585
|
+
"error"
|
|
586
|
+
);
|
|
587
|
+
log(tagError.stack || "No stack trace available", "error");
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
log(`[DRY RUN] Would create tag: ${packageTag}`, "info");
|
|
591
|
+
}
|
|
592
|
+
updatedPackagesInfo.push({ name, version: nextVersion, path: pkgPath });
|
|
593
|
+
}
|
|
594
|
+
if (updatedPackagesInfo.length === 0) {
|
|
595
|
+
log("No targeted packages required a version update.", "info");
|
|
596
|
+
return { updatedPackages: [], tags };
|
|
472
597
|
}
|
|
473
|
-
|
|
598
|
+
const filesToCommit = updatedPackagesInfo.map((info) => import_node_path3.default.join(info.path, "package.json"));
|
|
599
|
+
const packageNames = updatedPackagesInfo.map((p) => p.name).join(", ");
|
|
600
|
+
const representativeVersion = ((_a = updatedPackagesInfo[0]) == null ? void 0 : _a.version) || "multiple";
|
|
601
|
+
let commitMessage = this.commitMessageTemplate || "chore(release): publish packages";
|
|
602
|
+
if (updatedPackagesInfo.length === 1 && commitMessage.includes("${version}")) {
|
|
603
|
+
commitMessage = formatCommitMessage(commitMessage, representativeVersion);
|
|
604
|
+
} else {
|
|
605
|
+
commitMessage = `chore(release): ${packageNames} ${representativeVersion}`;
|
|
606
|
+
}
|
|
607
|
+
commitMessage += " [skip-ci]";
|
|
608
|
+
setCommitMessage(commitMessage);
|
|
609
|
+
if (!this.dryRun) {
|
|
610
|
+
try {
|
|
611
|
+
await gitAdd(filesToCommit);
|
|
612
|
+
await gitCommit({ message: commitMessage, skipHooks: this.skipHooks });
|
|
613
|
+
log(`Created commit for targeted release: ${packageNames}`, "success");
|
|
614
|
+
} catch (commitError) {
|
|
615
|
+
log("Failed to create commit for targeted release.", "error");
|
|
616
|
+
console.error(commitError);
|
|
617
|
+
(0, import_node_process4.exit)(1);
|
|
618
|
+
}
|
|
619
|
+
} else {
|
|
620
|
+
log("[DRY RUN] Would add files:", "info");
|
|
621
|
+
for (const file of filesToCommit) {
|
|
622
|
+
log(` - ${file}`, "info");
|
|
623
|
+
}
|
|
624
|
+
log(`[DRY RUN] Would commit with message: "${commitMessage}"`, "info");
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
updatedPackages: updatedPackagesInfo,
|
|
628
|
+
commitMessage,
|
|
629
|
+
tags
|
|
630
|
+
};
|
|
474
631
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
// src/core/versionStrategies.ts
|
|
635
|
+
function shouldProcessPackage(pkg, config, targets = []) {
|
|
636
|
+
var _a;
|
|
637
|
+
const pkgName = pkg.packageJson.name;
|
|
638
|
+
if ((_a = config.skip) == null ? void 0 : _a.includes(pkgName)) {
|
|
639
|
+
return false;
|
|
640
|
+
}
|
|
641
|
+
if (!targets || targets.length === 0) {
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
return targets.includes(pkgName);
|
|
645
|
+
}
|
|
646
|
+
function createSyncedStrategy(config) {
|
|
647
|
+
return async (packages) => {
|
|
479
648
|
try {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
649
|
+
const {
|
|
650
|
+
tagPrefix,
|
|
651
|
+
baseBranch,
|
|
652
|
+
branchPattern,
|
|
653
|
+
commitMessage = "chore(release): v${version}",
|
|
654
|
+
prereleaseIdentifier,
|
|
655
|
+
dryRun,
|
|
656
|
+
skipHooks
|
|
657
|
+
} = config;
|
|
658
|
+
const prefix = formatTagPrefix(tagPrefix || "v");
|
|
659
|
+
const latestTag = await getLatestTag();
|
|
660
|
+
const nextVersion = await calculateVersion(config, {
|
|
661
|
+
latestTag,
|
|
662
|
+
tagPrefix: prefix,
|
|
663
|
+
branchPattern,
|
|
664
|
+
baseBranch,
|
|
665
|
+
prereleaseIdentifier
|
|
486
666
|
});
|
|
487
|
-
if (!
|
|
488
|
-
log("
|
|
667
|
+
if (!nextVersion) {
|
|
668
|
+
log("No version change needed", "info");
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
const files = [];
|
|
672
|
+
const updatedPackages = [];
|
|
673
|
+
try {
|
|
674
|
+
const rootPkgPath = import_node_path4.default.join(packages.root, "package.json");
|
|
675
|
+
if (import_node_fs3.default.existsSync(rootPkgPath)) {
|
|
676
|
+
updatePackageVersion(rootPkgPath, nextVersion);
|
|
677
|
+
files.push(rootPkgPath);
|
|
678
|
+
updatedPackages.push("root");
|
|
679
|
+
}
|
|
680
|
+
} catch (_error) {
|
|
681
|
+
log("Failed to update root package.json", "error");
|
|
682
|
+
}
|
|
683
|
+
for (const pkg of packages.packages) {
|
|
684
|
+
if (!shouldProcessPackage(pkg, config)) {
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
const packageJsonPath = import_node_path4.default.join(pkg.dir, "package.json");
|
|
688
|
+
updatePackageVersion(packageJsonPath, nextVersion);
|
|
689
|
+
files.push(packageJsonPath);
|
|
690
|
+
updatedPackages.push(pkg.packageJson.name);
|
|
489
691
|
}
|
|
692
|
+
if (updatedPackages.length > 0) {
|
|
693
|
+
log(`Updated ${updatedPackages.length} package(s) to version ${nextVersion}`, "success");
|
|
694
|
+
} else {
|
|
695
|
+
log("No packages were updated", "warning");
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const nextTag = formatTag(nextVersion, tagPrefix || "v");
|
|
699
|
+
const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
|
|
700
|
+
await createGitCommitAndTag(files, nextTag, formattedCommitMessage, skipHooks, dryRun);
|
|
490
701
|
} catch (error) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
*/
|
|
499
|
-
async syncedStrategy() {
|
|
500
|
-
var _a;
|
|
501
|
-
const {
|
|
502
|
-
tagPrefix,
|
|
503
|
-
baseBranch,
|
|
504
|
-
branchPattern,
|
|
505
|
-
commitMessage = "chore(release): v${version}",
|
|
506
|
-
prereleaseIdentifier
|
|
507
|
-
} = this.config;
|
|
508
|
-
const prefix = formatTagPrefix(tagPrefix);
|
|
509
|
-
const latestTag = await getLatestTag();
|
|
510
|
-
const nextVersion = await this.calculateVersion({
|
|
511
|
-
latestTag,
|
|
512
|
-
tagPrefix: prefix,
|
|
513
|
-
branchPattern,
|
|
514
|
-
baseBranch,
|
|
515
|
-
prereleaseIdentifier
|
|
516
|
-
});
|
|
517
|
-
if (!nextVersion) {
|
|
518
|
-
log("info", "No version change needed");
|
|
519
|
-
return;
|
|
702
|
+
if (error instanceof VersionError || error instanceof GitError) {
|
|
703
|
+
log(`Synced Strategy failed: ${error.message} (${error.code || "UNKNOWN"})`, "error");
|
|
704
|
+
} else {
|
|
705
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
706
|
+
log(`Synced Strategy failed: ${errorMessage}`, "error");
|
|
707
|
+
}
|
|
708
|
+
throw error;
|
|
520
709
|
}
|
|
521
|
-
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function createSingleStrategy(config) {
|
|
713
|
+
return async (packages) => {
|
|
522
714
|
try {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
715
|
+
const {
|
|
716
|
+
packages: configPackages,
|
|
717
|
+
tagPrefix,
|
|
718
|
+
commitMessage = "chore(release): ${version}",
|
|
719
|
+
dryRun,
|
|
720
|
+
skipHooks
|
|
721
|
+
} = config;
|
|
722
|
+
if (!configPackages || configPackages.length !== 1) {
|
|
723
|
+
throw createVersionError(
|
|
724
|
+
"INVALID_CONFIG" /* INVALID_CONFIG */,
|
|
725
|
+
"Single mode requires exactly one package name"
|
|
726
|
+
);
|
|
526
727
|
}
|
|
728
|
+
const packageName = configPackages[0];
|
|
729
|
+
const pkg = packages.packages.find((p) => p.packageJson.name === packageName);
|
|
730
|
+
if (!pkg) {
|
|
731
|
+
throw createVersionError("PACKAGE_NOT_FOUND" /* PACKAGE_NOT_FOUND */, packageName);
|
|
732
|
+
}
|
|
733
|
+
const pkgPath = pkg.dir;
|
|
734
|
+
const prefix = formatTagPrefix(tagPrefix || "v");
|
|
735
|
+
const latestTag = await getLatestTag();
|
|
736
|
+
let nextVersion = void 0;
|
|
737
|
+
try {
|
|
738
|
+
nextVersion = await calculateVersion(config, {
|
|
739
|
+
latestTag,
|
|
740
|
+
tagPrefix: prefix,
|
|
741
|
+
path: pkgPath,
|
|
742
|
+
name: packageName
|
|
743
|
+
});
|
|
744
|
+
} catch (error) {
|
|
745
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
746
|
+
throw createVersionError("VERSION_CALCULATION_ERROR" /* VERSION_CALCULATION_ERROR */, errorMessage);
|
|
747
|
+
}
|
|
748
|
+
if (nextVersion === void 0 || nextVersion === "") {
|
|
749
|
+
log(`No version change needed for ${packageName}`, "info");
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
const packageJsonPath = import_node_path4.default.join(pkgPath, "package.json");
|
|
753
|
+
updatePackageVersion(packageJsonPath, nextVersion);
|
|
754
|
+
log(`Updated package ${packageName} to version ${nextVersion}`, "success");
|
|
755
|
+
const nextTag = formatTag(nextVersion, tagPrefix || "v");
|
|
756
|
+
const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
|
|
757
|
+
await createGitCommitAndTag(
|
|
758
|
+
[packageJsonPath],
|
|
759
|
+
nextTag,
|
|
760
|
+
formattedCommitMessage,
|
|
761
|
+
skipHooks,
|
|
762
|
+
dryRun
|
|
763
|
+
);
|
|
527
764
|
} catch (error) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
765
|
+
if (error instanceof VersionError || error instanceof GitError) {
|
|
766
|
+
log(
|
|
767
|
+
`Single Package Strategy failed: ${error.message} (${error.code || "UNKNOWN"})`,
|
|
768
|
+
"error"
|
|
769
|
+
);
|
|
770
|
+
} else {
|
|
771
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
772
|
+
log(`Single Package Strategy failed: ${errorMessage}`, "error");
|
|
773
|
+
}
|
|
774
|
+
throw error;
|
|
532
775
|
}
|
|
533
|
-
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
function createAsyncStrategy(config) {
|
|
779
|
+
const dependencies = {
|
|
780
|
+
getLatestTag
|
|
781
|
+
};
|
|
782
|
+
const processorOptions = {
|
|
783
|
+
skip: config.skip || [],
|
|
784
|
+
targets: config.packages || [],
|
|
785
|
+
tagPrefix: config.tagPrefix || "v",
|
|
786
|
+
commitMessageTemplate: config.commitMessage || "",
|
|
787
|
+
dryRun: config.dryRun || false,
|
|
788
|
+
skipHooks: config.skipHooks || false,
|
|
789
|
+
getLatestTag: dependencies.getLatestTag,
|
|
790
|
+
fullConfig: config,
|
|
791
|
+
config: {
|
|
792
|
+
branchPattern: config.branchPattern || [],
|
|
793
|
+
baseBranch: config.baseBranch || "main",
|
|
794
|
+
prereleaseIdentifier: config.prereleaseIdentifier,
|
|
795
|
+
forceType: config.forceType
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const packageProcessor = new PackageProcessor(processorOptions);
|
|
799
|
+
return async (packages, targets = []) => {
|
|
534
800
|
try {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
dryRun: this.config.dryRun
|
|
542
|
-
});
|
|
543
|
-
files.push(rootPkgPath);
|
|
801
|
+
const targetPackages = targets.length > 0 ? targets : config.packages || [];
|
|
802
|
+
packageProcessor.setTargets(targetPackages);
|
|
803
|
+
if (targetPackages.length > 0) {
|
|
804
|
+
log(`Processing targeted packages: ${targetPackages.join(", ")}`, "info");
|
|
805
|
+
} else {
|
|
806
|
+
log("No targets specified, processing all non-skipped packages", "info");
|
|
544
807
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
808
|
+
const result = await packageProcessor.processPackages(packages.packages);
|
|
809
|
+
if (result.updatedPackages.length === 0) {
|
|
810
|
+
log("No packages required a version update.", "info");
|
|
811
|
+
} else {
|
|
812
|
+
const packageNames = result.updatedPackages.map((p) => p.name).join(", ");
|
|
813
|
+
log(`Updated ${result.updatedPackages.length} package(s): ${packageNames}`, "success");
|
|
814
|
+
if (result.tags.length > 0) {
|
|
815
|
+
log(`Created ${result.tags.length} tag(s): ${result.tags.join(", ")}`, "success");
|
|
816
|
+
}
|
|
817
|
+
if (result.commitMessage) {
|
|
818
|
+
log(`Created commit with message: "${result.commitMessage}"`, "success");
|
|
819
|
+
}
|
|
551
820
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
821
|
+
} catch (error) {
|
|
822
|
+
if (error instanceof VersionError || error instanceof GitError) {
|
|
823
|
+
log(`Async Strategy failed: ${error.message} (${error.code || "UNKNOWN"})`, "error");
|
|
824
|
+
} else {
|
|
825
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
826
|
+
log(`Async Strategy failed: ${errorMessage}`, "error");
|
|
827
|
+
}
|
|
828
|
+
throw error;
|
|
559
829
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function createStrategy(config) {
|
|
833
|
+
var _a;
|
|
834
|
+
if (config.synced) {
|
|
835
|
+
return createSyncedStrategy(config);
|
|
836
|
+
}
|
|
837
|
+
if (((_a = config.packages) == null ? void 0 : _a.length) === 1) {
|
|
838
|
+
return createSingleStrategy(config);
|
|
839
|
+
}
|
|
840
|
+
return createAsyncStrategy(config);
|
|
841
|
+
}
|
|
842
|
+
function createStrategyMap(config) {
|
|
843
|
+
return {
|
|
844
|
+
synced: createSyncedStrategy(config),
|
|
845
|
+
single: createSingleStrategy(config),
|
|
846
|
+
async: createAsyncStrategy(config)
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/core/versionEngine.ts
|
|
851
|
+
var VersionEngine = class {
|
|
852
|
+
config;
|
|
853
|
+
jsonMode;
|
|
854
|
+
workspaceCache = null;
|
|
855
|
+
strategies;
|
|
856
|
+
currentStrategy;
|
|
857
|
+
constructor(config, jsonMode = false) {
|
|
858
|
+
if (!config) {
|
|
859
|
+
throw createVersionError("CONFIG_REQUIRED" /* CONFIG_REQUIRED */);
|
|
860
|
+
}
|
|
861
|
+
if (!config.preset) {
|
|
862
|
+
config.preset = "conventional-commits";
|
|
863
|
+
log("No preset specified, using default: conventional-commits", "warning");
|
|
864
|
+
}
|
|
865
|
+
this.config = config;
|
|
866
|
+
this.jsonMode = jsonMode;
|
|
867
|
+
this.strategies = createStrategyMap(config);
|
|
868
|
+
this.currentStrategy = createStrategy(config);
|
|
566
869
|
}
|
|
567
870
|
/**
|
|
568
|
-
*
|
|
871
|
+
* Get workspace packages information - with caching for performance
|
|
569
872
|
*/
|
|
570
|
-
async
|
|
571
|
-
const {
|
|
572
|
-
packages: configPackages,
|
|
573
|
-
tagPrefix,
|
|
574
|
-
commitMessage = "chore(release): ${version}"
|
|
575
|
-
} = this.config;
|
|
576
|
-
if (configPackages.length !== 1) {
|
|
577
|
-
log("error", "Single mode requires exactly one package name");
|
|
578
|
-
(0, import_node_process3.exit)(1);
|
|
579
|
-
}
|
|
580
|
-
const packageName = configPackages[0];
|
|
581
|
-
let pkgsResult;
|
|
873
|
+
async getWorkspacePackages() {
|
|
582
874
|
try {
|
|
583
|
-
|
|
875
|
+
if (this.workspaceCache) {
|
|
876
|
+
return this.workspaceCache;
|
|
877
|
+
}
|
|
878
|
+
const pkgsResult = (0, import_get_packages.getPackagesSync)((0, import_node_process5.cwd)());
|
|
584
879
|
if (!pkgsResult || !pkgsResult.packages) {
|
|
585
|
-
throw
|
|
880
|
+
throw createVersionError("PACKAGES_NOT_FOUND" /* PACKAGES_NOT_FOUND */);
|
|
586
881
|
}
|
|
882
|
+
this.workspaceCache = pkgsResult;
|
|
883
|
+
return pkgsResult;
|
|
587
884
|
} catch (error) {
|
|
588
|
-
|
|
885
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
886
|
+
log(`Failed to get packages information: ${errorMessage}`, "error");
|
|
589
887
|
console.error(error);
|
|
590
|
-
(
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
const pkg = pkgsResult.packages.find((p) => p.packageJson.name === packageName);
|
|
594
|
-
if (!pkg) {
|
|
595
|
-
log("error", `Package ${packageName} not found`);
|
|
596
|
-
(0, import_node_process3.exit)(1);
|
|
597
|
-
}
|
|
598
|
-
const pkgPath = pkg.dir;
|
|
599
|
-
const prefix = formatTagPrefix(tagPrefix);
|
|
600
|
-
const latestTag = await getLatestTag();
|
|
601
|
-
const nextVersion = await this.calculateVersion({
|
|
602
|
-
latestTag,
|
|
603
|
-
tagPrefix: prefix,
|
|
604
|
-
path: pkgPath,
|
|
605
|
-
name: packageName
|
|
606
|
-
});
|
|
607
|
-
if (!nextVersion) {
|
|
608
|
-
log("info", `No version change needed for ${packageName}`);
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
updatePackageVersion({
|
|
612
|
-
path: pkgPath,
|
|
613
|
-
version: nextVersion,
|
|
614
|
-
name: packageName,
|
|
615
|
-
dryRun: this.config.dryRun
|
|
616
|
-
});
|
|
617
|
-
const nextTag = formatTag(
|
|
618
|
-
{ tagPrefix, name: packageName, synced: false },
|
|
619
|
-
{ tagPrefix: prefix, version: nextVersion }
|
|
620
|
-
);
|
|
621
|
-
const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
|
|
622
|
-
await this.createGitCommitAndTag(
|
|
623
|
-
[import_node_path2.default.join(pkgPath, "package.json")],
|
|
624
|
-
nextTag,
|
|
625
|
-
formattedCommitMessage,
|
|
626
|
-
this.config.dryRun
|
|
627
|
-
);
|
|
888
|
+
throw createVersionError("WORKSPACE_ERROR" /* WORKSPACE_ERROR */, errorMessage);
|
|
889
|
+
}
|
|
628
890
|
}
|
|
629
891
|
/**
|
|
630
|
-
*
|
|
892
|
+
* Run the current strategy
|
|
893
|
+
* @param targets Optional package targets to process (only used by async strategy)
|
|
631
894
|
*/
|
|
632
|
-
async
|
|
633
|
-
const {
|
|
634
|
-
commitMessage = "chore(release): ${version}",
|
|
635
|
-
// Align with test expectations
|
|
636
|
-
skipHooks
|
|
637
|
-
// Add skipHooks here
|
|
638
|
-
} = this.config;
|
|
639
|
-
let pkgsResult;
|
|
895
|
+
async run(targets = []) {
|
|
640
896
|
try {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
throw new Error("Failed to get packages information");
|
|
644
|
-
}
|
|
897
|
+
const packages = await this.getWorkspacePackages();
|
|
898
|
+
return this.currentStrategy(packages, targets);
|
|
645
899
|
} catch (error) {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
const pkgsToProcess = await this.processPackages(pkgsResult.packages, cliTargets);
|
|
652
|
-
if (pkgsToProcess.length === 0) {
|
|
653
|
-
log("info", "No packages to process based on changes and targets");
|
|
654
|
-
return;
|
|
655
|
-
}
|
|
656
|
-
const formattedCommitMessage = commitMessage;
|
|
657
|
-
try {
|
|
658
|
-
await gitProcess({
|
|
659
|
-
files: pkgsToProcess,
|
|
660
|
-
nextTag: "",
|
|
661
|
-
commitMessage: formattedCommitMessage,
|
|
662
|
-
skipHooks,
|
|
663
|
-
dryRun: this.config.dryRun
|
|
664
|
-
});
|
|
665
|
-
if (!this.config.dryRun) {
|
|
666
|
-
log("success", `Created version commit for ${pkgsToProcess.length} package(s)`);
|
|
900
|
+
if (error instanceof VersionError || error instanceof GitError) {
|
|
901
|
+
log(`Version engine failed: ${error.message} (${error.code || "UNKNOWN"})`, "error");
|
|
902
|
+
} else {
|
|
903
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
904
|
+
log(`Version engine failed: ${errorMessage}`, "error");
|
|
667
905
|
}
|
|
668
|
-
|
|
669
|
-
log("error", "Failed to create version commit");
|
|
670
|
-
console.error(error);
|
|
671
|
-
(0, import_node_process3.exit)(1);
|
|
906
|
+
throw error;
|
|
672
907
|
}
|
|
673
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* Change the current strategy
|
|
911
|
+
* @param strategyType The strategy type to use: 'synced', 'single', or 'async'
|
|
912
|
+
*/
|
|
913
|
+
setStrategy(strategyType) {
|
|
914
|
+
this.currentStrategy = this.strategies[strategyType];
|
|
915
|
+
}
|
|
674
916
|
};
|
|
675
917
|
|
|
676
918
|
// src/index.ts
|
|
677
919
|
async function run() {
|
|
678
|
-
printFiglet();
|
|
679
920
|
const program = new import_commander.Command();
|
|
680
921
|
program.name("package-versioner").description(
|
|
681
|
-
"
|
|
682
|
-
).version(
|
|
683
|
-
"--
|
|
684
|
-
"
|
|
685
|
-
).option(
|
|
686
|
-
"-t, --target <targets>",
|
|
687
|
-
"Comma-separated list of package names to target (only for async strategy)"
|
|
688
|
-
).parse(process.argv);
|
|
922
|
+
"A lightweight yet powerful CLI tool for automated semantic versioning based on Git history and conventional commits."
|
|
923
|
+
).version(process.env.npm_package_version || "0.0.0").option(
|
|
924
|
+
"-c, --config <path>",
|
|
925
|
+
"Path to config file (defaults to version.config.json in current directory)"
|
|
926
|
+
).option("-d, --dry-run", "Dry run (no changes made)", false).option("-b, --bump <type>", "Force specific bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --synced", "Force synchronized versioning across all packages").option("-j, --json", "Output results as JSON", false).option("-t, --target <packages>", "Comma-delimited list of package names to target").parse(process.argv);
|
|
689
927
|
const options = program.opts();
|
|
928
|
+
if (options.json) {
|
|
929
|
+
enableJsonOutput(options.dryRun);
|
|
930
|
+
}
|
|
690
931
|
try {
|
|
691
932
|
const config = await loadConfig(options.config);
|
|
692
|
-
log(
|
|
933
|
+
log(`Loaded configuration from ${options.config || "version.config.json"}`, "info");
|
|
693
934
|
if (options.dryRun) config.dryRun = true;
|
|
694
935
|
if (options.synced) config.synced = true;
|
|
695
936
|
if (options.bump) config.forceType = options.bump;
|
|
696
937
|
if (options.prerelease)
|
|
697
938
|
config.prereleaseIdentifier = options.prerelease === true ? "rc" : options.prerelease;
|
|
698
939
|
const cliTargets = options.target ? options.target.split(",").map((t) => t.trim()) : [];
|
|
699
|
-
const engine = new VersionEngine(config);
|
|
940
|
+
const engine = new VersionEngine(config, !!options.json);
|
|
700
941
|
if (config.synced) {
|
|
701
|
-
log("
|
|
702
|
-
|
|
942
|
+
log("Using synced versioning strategy.", "info");
|
|
943
|
+
engine.setStrategy("synced");
|
|
944
|
+
await engine.run();
|
|
703
945
|
} else if (config.packages && config.packages.length === 1) {
|
|
704
|
-
log("
|
|
946
|
+
log("Using single package versioning strategy.", "info");
|
|
705
947
|
if (cliTargets.length > 0) {
|
|
706
|
-
log("
|
|
948
|
+
log("--target flag is ignored for single package strategy.", "warning");
|
|
707
949
|
}
|
|
708
|
-
|
|
950
|
+
engine.setStrategy("single");
|
|
951
|
+
await engine.run();
|
|
709
952
|
} else {
|
|
710
|
-
log("
|
|
953
|
+
log("Using async versioning strategy.", "info");
|
|
711
954
|
if (cliTargets.length > 0) {
|
|
712
|
-
log(
|
|
955
|
+
log(`Targeting specific packages: ${cliTargets.join(", ")}`, "info");
|
|
713
956
|
}
|
|
714
|
-
|
|
957
|
+
engine.setStrategy("async");
|
|
958
|
+
await engine.run(cliTargets);
|
|
715
959
|
}
|
|
716
|
-
log("
|
|
960
|
+
log("Versioning process completed.", "success");
|
|
961
|
+
printJsonOutput();
|
|
717
962
|
} catch (error) {
|
|
718
|
-
log(
|
|
963
|
+
log(error instanceof Error ? error.message : String(error), "error");
|
|
719
964
|
process.exit(1);
|
|
720
965
|
}
|
|
721
966
|
}
|
|
722
|
-
run()
|
|
967
|
+
run().catch((error) => {
|
|
968
|
+
console.error("Fatal error:", error);
|
|
969
|
+
process.exit(1);
|
|
970
|
+
});
|