package-versioner 0.0.1
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/LICENCE.md +7 -0
- package/README.md +89 -0
- package/dist/index.cjs +667 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +648 -0
- package/package-versioner.schema.json +90 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { exit as exit2 } from "process";
|
|
5
|
+
import chalk2 from "chalk";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import figlet from "figlet";
|
|
8
|
+
|
|
9
|
+
// package.json
|
|
10
|
+
var package_default = {
|
|
11
|
+
name: "package-versioner",
|
|
12
|
+
description: "A powerful CLI tool for automated semantic versioning based on Git history and conventional commits. Supports monorepos with synchronized or independent package versioning strategies.",
|
|
13
|
+
version: "0.0.1",
|
|
14
|
+
type: "module",
|
|
15
|
+
main: "./dist/index.js",
|
|
16
|
+
module: "./dist/index.mjs",
|
|
17
|
+
types: "./dist/index.d.ts",
|
|
18
|
+
author: {
|
|
19
|
+
name: "Sam Maister",
|
|
20
|
+
email: "goosewobbler@protonmail.com"
|
|
21
|
+
},
|
|
22
|
+
repository: {
|
|
23
|
+
type: "git",
|
|
24
|
+
url: "https://github.com/goosewobbler/package-versioner",
|
|
25
|
+
homepage: "https://github.com/goosewobbler/package-versioner"
|
|
26
|
+
},
|
|
27
|
+
keywords: ["version", "semver", "git", "package"],
|
|
28
|
+
license: "MIT",
|
|
29
|
+
files: ["dist/**", "package-versioner.schema.json"],
|
|
30
|
+
bin: {
|
|
31
|
+
"package-versioner": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
scripts: {
|
|
34
|
+
build: "tsup src/index.ts --format esm,cjs --dts",
|
|
35
|
+
dev: "tsup src/index.ts --format esm,cjs --watch --dts",
|
|
36
|
+
clean: "rm -rf node_modules && rm -rf dist",
|
|
37
|
+
test: "vitest run --coverage",
|
|
38
|
+
"test:watch": "vitest --coverage",
|
|
39
|
+
lint: "biome check .",
|
|
40
|
+
"lint:fix": "biome check --apply .",
|
|
41
|
+
format: "biome format --write .",
|
|
42
|
+
"format:check": "biome format .",
|
|
43
|
+
fix: "pnpm run lint:fix && pnpm run format",
|
|
44
|
+
prepare: "husky"
|
|
45
|
+
},
|
|
46
|
+
"lint-staged": {
|
|
47
|
+
"*.{js,ts,jsx,tsx}": ["biome check --apply", "biome format --write"]
|
|
48
|
+
},
|
|
49
|
+
devDependencies: {
|
|
50
|
+
"@biomejs/biome": "^1.9.4",
|
|
51
|
+
"@types/figlet": "^1.5.5",
|
|
52
|
+
"@types/node": "^18.14.0",
|
|
53
|
+
"@types/semver": "^7.3.13",
|
|
54
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
55
|
+
husky: "^9.1.7",
|
|
56
|
+
"lint-staged": "^15.5.0",
|
|
57
|
+
tsup: "^5.10.1",
|
|
58
|
+
typescript: "^5.8.3",
|
|
59
|
+
vitest: "^3.1.1"
|
|
60
|
+
},
|
|
61
|
+
dependencies: {
|
|
62
|
+
"@manypkg/get-packages": "^2.2.2",
|
|
63
|
+
chalk: "^5.4.1",
|
|
64
|
+
commander: "^13.1.0",
|
|
65
|
+
"conventional-changelog-angular": "^8.0.0",
|
|
66
|
+
"conventional-recommended-bump": "^11.0.0",
|
|
67
|
+
figlet: "^1.8.0",
|
|
68
|
+
"git-semver-tags": "^8.0.0",
|
|
69
|
+
semver: "^7.7.1"
|
|
70
|
+
},
|
|
71
|
+
packageManager: "pnpm@10.8.0+sha512.0e82714d1b5b43c74610193cb20734897c1d00de89d0e18420aebc5977fa13d780a9cb05734624e81ebd81cc876cd464794850641c48b9544326b5622ca29971"
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// src/config.ts
|
|
75
|
+
import * as fs from "fs";
|
|
76
|
+
import { cwd } from "process";
|
|
77
|
+
function loadConfig(configPath) {
|
|
78
|
+
const localProcess = cwd();
|
|
79
|
+
const filePath = configPath || `${localProcess}/version.config.json`;
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
fs.readFile(filePath, "utf-8", (err, data) => {
|
|
82
|
+
if (err) {
|
|
83
|
+
reject(new Error(`Could not locate the config file at ${filePath}: ${err.message}`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const config = JSON.parse(data);
|
|
88
|
+
resolve(config);
|
|
89
|
+
} catch (err2) {
|
|
90
|
+
const errorMessage = err2 instanceof Error ? err2.message : String(err2);
|
|
91
|
+
reject(new Error(`Failed to parse config file ${filePath}: ${errorMessage}`));
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/utils.ts
|
|
98
|
+
import fs2 from "fs";
|
|
99
|
+
import chalk from "chalk";
|
|
100
|
+
import { getSemverTags } from "git-semver-tags";
|
|
101
|
+
|
|
102
|
+
// src/git.ts
|
|
103
|
+
import { exec, execSync as syncExec } from "child_process";
|
|
104
|
+
import { existsSync, statSync } from "fs";
|
|
105
|
+
import { join } from "path";
|
|
106
|
+
import { cwd as cwd2 } from "process";
|
|
107
|
+
var execAsync = (command) => {
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
const options = { maxBuffer: 1024 * 1024 * 10 };
|
|
110
|
+
exec(
|
|
111
|
+
command,
|
|
112
|
+
options,
|
|
113
|
+
(error, stdout, stderr) => {
|
|
114
|
+
if (error) {
|
|
115
|
+
reject(error);
|
|
116
|
+
} else {
|
|
117
|
+
resolve({ stdout: stdout.toString(), stderr: stderr.toString() });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
var execSync = (command, args) => syncExec(command, { maxBuffer: 1024 * 1024 * 10, ...args });
|
|
124
|
+
async function gitAdd(files) {
|
|
125
|
+
const command = `git add ${files.join(" ")}`;
|
|
126
|
+
return execAsync(command);
|
|
127
|
+
}
|
|
128
|
+
async function gitCommit(options) {
|
|
129
|
+
const command = ["commit"];
|
|
130
|
+
if (options.amend) {
|
|
131
|
+
command.push("--amend");
|
|
132
|
+
}
|
|
133
|
+
if (options.author) {
|
|
134
|
+
command.push(`--author="${options.author}"`);
|
|
135
|
+
}
|
|
136
|
+
if (options.date) {
|
|
137
|
+
command.push(`--date="${options.date}"`);
|
|
138
|
+
}
|
|
139
|
+
if (options.skipHooks) {
|
|
140
|
+
command.push("--no-verify");
|
|
141
|
+
}
|
|
142
|
+
command.push(`-m "${options.message}"`);
|
|
143
|
+
return execAsync(`git ${command.join(" ")}`);
|
|
144
|
+
}
|
|
145
|
+
async function createGitTag(options) {
|
|
146
|
+
const { tag, message = "", args = "" } = options;
|
|
147
|
+
const command = `git tag -a -m "${message}" ${tag} ${args}`;
|
|
148
|
+
return execAsync(command);
|
|
149
|
+
}
|
|
150
|
+
function getCommitsLength(pkgRoot) {
|
|
151
|
+
try {
|
|
152
|
+
const gitCommand = `git rev-list --count HEAD ^$(git describe --tags --abbrev=0) ${pkgRoot}`;
|
|
153
|
+
const amount = execSync(gitCommand).toString().trim();
|
|
154
|
+
return Number(amount);
|
|
155
|
+
} catch {
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function isGitRepository(directory) {
|
|
160
|
+
const gitDir = join(directory, ".git");
|
|
161
|
+
if (!existsSync(gitDir)) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const stats = statSync(gitDir);
|
|
165
|
+
if (!stats.isDirectory()) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
execSync("git rev-parse --is-inside-work-tree", { cwd: directory });
|
|
170
|
+
return true;
|
|
171
|
+
} catch (_error) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async function gitProcess({ files, nextTag, commitMessage, skipHooks, dryRun }) {
|
|
176
|
+
try {
|
|
177
|
+
if (!isGitRepository(cwd2())) {
|
|
178
|
+
throw new Error("Not a git repository (or any parent up to mount point /)");
|
|
179
|
+
}
|
|
180
|
+
if (!dryRun) {
|
|
181
|
+
await gitAdd(files);
|
|
182
|
+
await gitCommit({
|
|
183
|
+
message: commitMessage,
|
|
184
|
+
skipHooks
|
|
185
|
+
});
|
|
186
|
+
const tagMessage = `New Version ${nextTag} generated at ${new Date().toISOString()}`;
|
|
187
|
+
await createGitTag({
|
|
188
|
+
tag: nextTag,
|
|
189
|
+
message: tagMessage
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
log("info", "[DRY RUN] Would add files:");
|
|
193
|
+
for (const file of files) {
|
|
194
|
+
log("info", ` - ${file}`);
|
|
195
|
+
}
|
|
196
|
+
log("info", `[DRY RUN] Would commit with message: "${commitMessage}"`);
|
|
197
|
+
log("info", `[DRY RUN] Would create tag: ${nextTag}`);
|
|
198
|
+
}
|
|
199
|
+
} catch (err) {
|
|
200
|
+
console.log(err);
|
|
201
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
202
|
+
throw new Error(`Failed to create new version: ${errorMessage}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
async function lastMergeBranchName(branches, baseBranch) {
|
|
206
|
+
try {
|
|
207
|
+
const branchesRegex = `${branches.join("/(.*)|")}/(.*)`;
|
|
208
|
+
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`;
|
|
209
|
+
const { stdout } = await execAsync(command);
|
|
210
|
+
return stdout.trim();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.error(
|
|
213
|
+
"Error while getting the last branch name:",
|
|
214
|
+
error instanceof Error ? error.message : String(error)
|
|
215
|
+
);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function getCurrentBranch() {
|
|
220
|
+
const result = execSync("git rev-parse --abbrev-ref HEAD");
|
|
221
|
+
return result.toString().trim();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/utils.ts
|
|
225
|
+
function log(status, message) {
|
|
226
|
+
const statusColors = {
|
|
227
|
+
info: chalk.blue("\u2139"),
|
|
228
|
+
success: chalk.green("\u2713"),
|
|
229
|
+
error: chalk.red("\u2717"),
|
|
230
|
+
warning: chalk.yellow("\u26A0")
|
|
231
|
+
};
|
|
232
|
+
process.stdout.write(`${statusColors[status]} ${message}
|
|
233
|
+
`);
|
|
234
|
+
}
|
|
235
|
+
async function getLatestTag() {
|
|
236
|
+
try {
|
|
237
|
+
const tags = await getSemverTags({});
|
|
238
|
+
return tags[0] || "";
|
|
239
|
+
} catch (error) {
|
|
240
|
+
log("error", "Failed to get latest tag");
|
|
241
|
+
console.error(error);
|
|
242
|
+
if (error instanceof Error && error.message.includes("No names found")) {
|
|
243
|
+
log("info", "No tags found in the repository.");
|
|
244
|
+
}
|
|
245
|
+
return "";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function formatTag(options, props) {
|
|
249
|
+
const { name: name2, synced, tagPrefix } = options;
|
|
250
|
+
const { version } = props;
|
|
251
|
+
if (!synced && name2) {
|
|
252
|
+
return `${tagPrefix ? tagPrefix : ""}${name2}@${version}`;
|
|
253
|
+
}
|
|
254
|
+
return `${tagPrefix ? tagPrefix : "v"}${version}`;
|
|
255
|
+
}
|
|
256
|
+
function formatTagPrefix(tagPrefix) {
|
|
257
|
+
return tagPrefix ? `${tagPrefix}@` : "";
|
|
258
|
+
}
|
|
259
|
+
function createTemplateString(template, data) {
|
|
260
|
+
return template.replace(/\$\{([^}]+)\}/g, (_, key) => data[key] || "");
|
|
261
|
+
}
|
|
262
|
+
function formatCommitMessage(template, version) {
|
|
263
|
+
return createTemplateString(template, { version });
|
|
264
|
+
}
|
|
265
|
+
function updatePackageVersion({ path: path2, version, name: name2, dryRun }) {
|
|
266
|
+
try {
|
|
267
|
+
const pkgPath = `${path2}/package.json`;
|
|
268
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
|
|
269
|
+
pkg.version = version;
|
|
270
|
+
if (!dryRun) {
|
|
271
|
+
fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
272
|
+
`);
|
|
273
|
+
log("success", `${name2}: ${version}`);
|
|
274
|
+
} else {
|
|
275
|
+
log("info", `[DRY RUN] Would update ${name2} package.json to version ${version}`);
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
log("error", `Failed to update ${name2} to version ${version}`);
|
|
279
|
+
console.error(error);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/versionEngine.ts
|
|
284
|
+
import fs3 from "fs";
|
|
285
|
+
import path from "path";
|
|
286
|
+
import { cwd as cwd3, exit } from "process";
|
|
287
|
+
import { getPackagesSync } from "@manypkg/get-packages";
|
|
288
|
+
import { Bumper } from "conventional-recommended-bump";
|
|
289
|
+
import semver from "semver";
|
|
290
|
+
var VersionEngine = class {
|
|
291
|
+
constructor(config) {
|
|
292
|
+
this.config = config;
|
|
293
|
+
}
|
|
294
|
+
async calculateVersion(options) {
|
|
295
|
+
const { latestTag, type, path: path2, name: name2, branchPattern, prereleaseIdentifier } = options;
|
|
296
|
+
const originalPrefix = this.config.tagPrefix || "";
|
|
297
|
+
const initialVersion = prereleaseIdentifier ? `0.0.1-${prereleaseIdentifier}` : "0.0.1";
|
|
298
|
+
const tagSearchPattern = name2 ? originalPrefix ? `${originalPrefix}${name2}@` : `${name2}@` : originalPrefix ? `${originalPrefix}v` : "v";
|
|
299
|
+
let determinedReleaseType = type || null;
|
|
300
|
+
if (determinedReleaseType) {
|
|
301
|
+
if (!latestTag) {
|
|
302
|
+
return initialVersion;
|
|
303
|
+
}
|
|
304
|
+
const currentVersion = semver.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
|
|
305
|
+
return semver.inc(currentVersion, determinedReleaseType, prereleaseIdentifier) || "";
|
|
306
|
+
}
|
|
307
|
+
if (this.config.versionStrategy === "branchPattern" && (branchPattern == null ? void 0 : branchPattern.length)) {
|
|
308
|
+
const currentBranch = await getCurrentBranch();
|
|
309
|
+
const mergeBranch = await lastMergeBranchName(branchPattern, this.config.baseBranch);
|
|
310
|
+
const branch = mergeBranch || currentBranch;
|
|
311
|
+
for (const pattern of branchPattern) {
|
|
312
|
+
const [match, releaseType] = pattern.split(":");
|
|
313
|
+
if (branch.includes(match) && releaseType) {
|
|
314
|
+
determinedReleaseType = releaseType;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (determinedReleaseType) {
|
|
319
|
+
if (!latestTag) {
|
|
320
|
+
return initialVersion;
|
|
321
|
+
}
|
|
322
|
+
const currentVersion = semver.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
|
|
323
|
+
return semver.inc(currentVersion, determinedReleaseType, prereleaseIdentifier) || "";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const bumper = new Bumper();
|
|
328
|
+
bumper.loadPreset(this.config.preset);
|
|
329
|
+
const recommendedBump = await bumper.bump();
|
|
330
|
+
const releaseTypeFromCommits = recommendedBump.releaseType;
|
|
331
|
+
if (!latestTag) {
|
|
332
|
+
return initialVersion;
|
|
333
|
+
}
|
|
334
|
+
const checkPath = path2 || cwd3();
|
|
335
|
+
const commitsLength = await getCommitsLength(checkPath);
|
|
336
|
+
if (commitsLength === 0) {
|
|
337
|
+
log(
|
|
338
|
+
"info",
|
|
339
|
+
`No new commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
|
|
340
|
+
);
|
|
341
|
+
return "";
|
|
342
|
+
}
|
|
343
|
+
if (!releaseTypeFromCommits) {
|
|
344
|
+
log(
|
|
345
|
+
"info",
|
|
346
|
+
`No relevant commits found for ${name2 || "project"} since ${latestTag}, skipping version bump`
|
|
347
|
+
);
|
|
348
|
+
return "";
|
|
349
|
+
}
|
|
350
|
+
const currentVersion = semver.clean(latestTag.replace(tagSearchPattern, "")) || "0.0.0";
|
|
351
|
+
return semver.inc(currentVersion, releaseTypeFromCommits, prereleaseIdentifier) || "";
|
|
352
|
+
} catch (error) {
|
|
353
|
+
log("error", `Failed to calculate version for ${name2 || "project"}`);
|
|
354
|
+
console.error(error);
|
|
355
|
+
if (error instanceof Error && error.message.includes("No names found")) {
|
|
356
|
+
log("info", "No tags found, proceeding with initial version calculation (if applicable).");
|
|
357
|
+
return initialVersion;
|
|
358
|
+
}
|
|
359
|
+
return "";
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
async processPackages(packages = [], configPackages = []) {
|
|
363
|
+
const { tagPrefix } = this.config;
|
|
364
|
+
const pkgsResult = packages.length ? { packages } : getPackagesSync(cwd3());
|
|
365
|
+
const files = [];
|
|
366
|
+
const selectedPackages = pkgsResult.packages.filter((pkg) => {
|
|
367
|
+
var _a;
|
|
368
|
+
if ((_a = this.config.skip) == null ? void 0 : _a.includes(pkg.packageJson.name)) {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
return configPackages.length === 0 || configPackages.includes(pkg.packageJson.name);
|
|
372
|
+
});
|
|
373
|
+
for (const pkg of selectedPackages) {
|
|
374
|
+
const name2 = pkg.packageJson.name;
|
|
375
|
+
const pkgPath = pkg.dir;
|
|
376
|
+
const prefix = formatTagPrefix(tagPrefix);
|
|
377
|
+
const latestTag = await getLatestTag();
|
|
378
|
+
const nextVersion = await this.calculateVersion({
|
|
379
|
+
latestTag,
|
|
380
|
+
tagPrefix: prefix,
|
|
381
|
+
path: pkgPath,
|
|
382
|
+
name: name2,
|
|
383
|
+
branchPattern: this.config.branchPattern,
|
|
384
|
+
baseBranch: this.config.baseBranch,
|
|
385
|
+
prereleaseIdentifier: this.config.prereleaseIdentifier
|
|
386
|
+
});
|
|
387
|
+
if (!nextVersion) {
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
updatePackageVersion({
|
|
391
|
+
path: pkgPath,
|
|
392
|
+
version: nextVersion,
|
|
393
|
+
name: name2,
|
|
394
|
+
dryRun: this.config.dryRun
|
|
395
|
+
});
|
|
396
|
+
files.push(path.join(pkgPath, "package.json"));
|
|
397
|
+
}
|
|
398
|
+
return files;
|
|
399
|
+
}
|
|
400
|
+
async createGitCommitAndTag(files, nextTag, commitMessage, dryRun) {
|
|
401
|
+
try {
|
|
402
|
+
await gitProcess({
|
|
403
|
+
files,
|
|
404
|
+
nextTag,
|
|
405
|
+
commitMessage,
|
|
406
|
+
skipHooks: this.config.skipHooks,
|
|
407
|
+
dryRun
|
|
408
|
+
});
|
|
409
|
+
if (!dryRun) {
|
|
410
|
+
log("success", `Created tag: ${nextTag}`);
|
|
411
|
+
}
|
|
412
|
+
} catch (error) {
|
|
413
|
+
log("error", "Failed to create git commit and tag");
|
|
414
|
+
console.error(error);
|
|
415
|
+
exit(1);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async syncedStrategy() {
|
|
419
|
+
var _a;
|
|
420
|
+
const {
|
|
421
|
+
tagPrefix,
|
|
422
|
+
baseBranch,
|
|
423
|
+
branchPattern,
|
|
424
|
+
commitMessage = "chore(release): v${version}",
|
|
425
|
+
prereleaseIdentifier
|
|
426
|
+
} = this.config;
|
|
427
|
+
const prefix = formatTagPrefix(tagPrefix);
|
|
428
|
+
const latestTag = await getLatestTag();
|
|
429
|
+
const nextVersion = await this.calculateVersion({
|
|
430
|
+
latestTag,
|
|
431
|
+
tagPrefix: prefix,
|
|
432
|
+
branchPattern,
|
|
433
|
+
baseBranch,
|
|
434
|
+
prereleaseIdentifier
|
|
435
|
+
});
|
|
436
|
+
if (!nextVersion) {
|
|
437
|
+
log("info", "No version change needed");
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
let pkgsResult;
|
|
441
|
+
try {
|
|
442
|
+
pkgsResult = getPackagesSync(cwd3());
|
|
443
|
+
if (!pkgsResult || !pkgsResult.packages) {
|
|
444
|
+
throw new Error("Failed to get packages information");
|
|
445
|
+
}
|
|
446
|
+
} catch (error) {
|
|
447
|
+
log("error", "Failed to get packages information");
|
|
448
|
+
console.error(error);
|
|
449
|
+
exit(1);
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
const files = [];
|
|
453
|
+
try {
|
|
454
|
+
const rootPkgPath = path.join(pkgsResult.root, "package.json");
|
|
455
|
+
if (fs3.existsSync(rootPkgPath)) {
|
|
456
|
+
updatePackageVersion({
|
|
457
|
+
path: pkgsResult.root,
|
|
458
|
+
version: nextVersion,
|
|
459
|
+
name: "root",
|
|
460
|
+
dryRun: this.config.dryRun
|
|
461
|
+
});
|
|
462
|
+
files.push(rootPkgPath);
|
|
463
|
+
}
|
|
464
|
+
} catch (_error) {
|
|
465
|
+
log("error", "Failed to update root package.json");
|
|
466
|
+
}
|
|
467
|
+
for (const pkg of pkgsResult.packages) {
|
|
468
|
+
if ((_a = this.config.skip) == null ? void 0 : _a.includes(pkg.packageJson.name)) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
updatePackageVersion({
|
|
472
|
+
path: pkg.dir,
|
|
473
|
+
version: nextVersion,
|
|
474
|
+
name: pkg.packageJson.name,
|
|
475
|
+
dryRun: this.config.dryRun
|
|
476
|
+
});
|
|
477
|
+
files.push(path.join(pkg.dir, "package.json"));
|
|
478
|
+
}
|
|
479
|
+
const nextTag = formatTag(
|
|
480
|
+
{ synced: true, tagPrefix },
|
|
481
|
+
{ tagPrefix: prefix, version: nextVersion }
|
|
482
|
+
);
|
|
483
|
+
const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
|
|
484
|
+
await this.createGitCommitAndTag(files, nextTag, formattedCommitMessage, this.config.dryRun);
|
|
485
|
+
}
|
|
486
|
+
async singleStrategy() {
|
|
487
|
+
const {
|
|
488
|
+
packages: configPackages,
|
|
489
|
+
tagPrefix,
|
|
490
|
+
commitMessage = "chore(release): ${version}"
|
|
491
|
+
} = this.config;
|
|
492
|
+
if (configPackages.length !== 1) {
|
|
493
|
+
log("error", "Single mode requires exactly one package name");
|
|
494
|
+
exit(1);
|
|
495
|
+
}
|
|
496
|
+
const packageName = configPackages[0];
|
|
497
|
+
let pkgsResult;
|
|
498
|
+
try {
|
|
499
|
+
pkgsResult = getPackagesSync(cwd3());
|
|
500
|
+
if (!pkgsResult || !pkgsResult.packages) {
|
|
501
|
+
throw new Error("Failed to get packages information");
|
|
502
|
+
}
|
|
503
|
+
} catch (error) {
|
|
504
|
+
log("error", "Failed to get packages information");
|
|
505
|
+
console.error(error);
|
|
506
|
+
exit(1);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const pkg = pkgsResult.packages.find((p) => p.packageJson.name === packageName);
|
|
510
|
+
if (!pkg) {
|
|
511
|
+
log("error", `Package ${packageName} not found`);
|
|
512
|
+
exit(1);
|
|
513
|
+
}
|
|
514
|
+
const pkgPath = pkg.dir;
|
|
515
|
+
const prefix = formatTagPrefix(tagPrefix);
|
|
516
|
+
const latestTag = await getLatestTag();
|
|
517
|
+
const nextVersion = await this.calculateVersion({
|
|
518
|
+
latestTag,
|
|
519
|
+
tagPrefix: prefix,
|
|
520
|
+
path: pkgPath,
|
|
521
|
+
name: packageName
|
|
522
|
+
});
|
|
523
|
+
if (!nextVersion) {
|
|
524
|
+
log("info", `No version change needed for ${packageName}`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
updatePackageVersion({
|
|
528
|
+
path: pkgPath,
|
|
529
|
+
version: nextVersion,
|
|
530
|
+
name: packageName,
|
|
531
|
+
dryRun: this.config.dryRun
|
|
532
|
+
});
|
|
533
|
+
const nextTag = formatTag(
|
|
534
|
+
{ tagPrefix, name: packageName, synced: false },
|
|
535
|
+
{ tagPrefix: prefix, version: nextVersion }
|
|
536
|
+
);
|
|
537
|
+
const formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion);
|
|
538
|
+
await this.createGitCommitAndTag(
|
|
539
|
+
[path.join(pkgPath, "package.json")],
|
|
540
|
+
nextTag,
|
|
541
|
+
formattedCommitMessage,
|
|
542
|
+
this.config.dryRun
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
async asyncStrategy() {
|
|
546
|
+
const {
|
|
547
|
+
packages: configPackages,
|
|
548
|
+
commitMessage = "chore(release): ${version}",
|
|
549
|
+
skipHooks
|
|
550
|
+
} = this.config;
|
|
551
|
+
let pkgsResult;
|
|
552
|
+
try {
|
|
553
|
+
pkgsResult = getPackagesSync(cwd3());
|
|
554
|
+
if (!pkgsResult || !pkgsResult.packages) {
|
|
555
|
+
throw new Error("Failed to get packages information");
|
|
556
|
+
}
|
|
557
|
+
} catch (error) {
|
|
558
|
+
log("error", "Failed to get packages information");
|
|
559
|
+
console.error(error);
|
|
560
|
+
exit(1);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
const pkgsToProcess = await this.processPackages(pkgsResult.packages, configPackages);
|
|
564
|
+
if (pkgsToProcess.length === 0) {
|
|
565
|
+
log("info", "No packages to process");
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const formattedCommitMessage = commitMessage;
|
|
569
|
+
try {
|
|
570
|
+
await gitProcess({
|
|
571
|
+
files: pkgsToProcess,
|
|
572
|
+
nextTag: "",
|
|
573
|
+
commitMessage: formattedCommitMessage,
|
|
574
|
+
skipHooks,
|
|
575
|
+
dryRun: this.config.dryRun
|
|
576
|
+
});
|
|
577
|
+
if (!this.config.dryRun) {
|
|
578
|
+
log("success", "Created version commit");
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
log("error", "Failed to create version commit");
|
|
582
|
+
console.error(error);
|
|
583
|
+
exit(1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
// src/index.ts
|
|
589
|
+
var name = "package-versioner";
|
|
590
|
+
var program = new Command();
|
|
591
|
+
program.name("package-versioner").description("Manages package versions using Git context.").version(package_default.version);
|
|
592
|
+
program.option("-t, --target <project>", "specific package to update").option("-b, --bump <version>", "type of version bump to perform", (value) => {
|
|
593
|
+
const validBumps = [
|
|
594
|
+
"patch",
|
|
595
|
+
"minor",
|
|
596
|
+
"major",
|
|
597
|
+
"premajor",
|
|
598
|
+
"preminor",
|
|
599
|
+
"prepatch",
|
|
600
|
+
"prerelease"
|
|
601
|
+
];
|
|
602
|
+
if (!validBumps.includes(value)) {
|
|
603
|
+
log("error", `Invalid bump type '${value}'. Valid options are: ${validBumps.join(", ")}`);
|
|
604
|
+
process.exit(1);
|
|
605
|
+
}
|
|
606
|
+
return value;
|
|
607
|
+
}).option("--base-branch <branch>", "override the base branch for this operation").option("--synced", "force synced versioning mode").option("--no-synced", "force async versioning mode").option(
|
|
608
|
+
"--skip <packages>",
|
|
609
|
+
"comma-separated list of packages to skip",
|
|
610
|
+
(value) => value.split(",")
|
|
611
|
+
).option("--prerelease <identifier>", "set prerelease identifier (e.g., alpha, beta)").option("--skip-hooks", "skip Git hooks for this operation").option("--config <path>", "specify a custom config file path").option("--dry-run", "Calculate version and log actions without changing files or Git state");
|
|
612
|
+
program.description("Version packages based on Git context and conventional commits").action(async (options) => {
|
|
613
|
+
const figletText = figlet.textSync(name);
|
|
614
|
+
const versionText = `v${package_default.version}`;
|
|
615
|
+
process.stdout.write(`${chalk2.hex("#FF1F57")(figletText)}
|
|
616
|
+
`);
|
|
617
|
+
process.stdout.write(`${chalk2.hex("#0096FF")(versionText)}
|
|
618
|
+
|
|
619
|
+
`);
|
|
620
|
+
try {
|
|
621
|
+
const configPath = options.config || void 0;
|
|
622
|
+
const config = await loadConfig(configPath);
|
|
623
|
+
if (options.baseBranch)
|
|
624
|
+
config.baseBranch = options.baseBranch;
|
|
625
|
+
if (options.synced !== void 0)
|
|
626
|
+
config.synced = options.synced;
|
|
627
|
+
if (options.skip)
|
|
628
|
+
config.skip = options.skip;
|
|
629
|
+
if (options.prerelease)
|
|
630
|
+
config.prereleaseIdentifier = options.prerelease;
|
|
631
|
+
if (options.skipHooks !== void 0)
|
|
632
|
+
config.skipHooks = options.skipHooks;
|
|
633
|
+
if (options.dryRun !== void 0)
|
|
634
|
+
config.dryRun = options.dryRun;
|
|
635
|
+
const engine = new VersionEngine(config);
|
|
636
|
+
if (config.synced) {
|
|
637
|
+
await engine.syncedStrategy();
|
|
638
|
+
} else if (options.bump && options.target) {
|
|
639
|
+
await engine.singleStrategy();
|
|
640
|
+
} else {
|
|
641
|
+
await engine.asyncStrategy();
|
|
642
|
+
}
|
|
643
|
+
} catch (err) {
|
|
644
|
+
log("error", `${err instanceof Error ? err.message : String(err)}`);
|
|
645
|
+
exit2(1);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
program.parse();
|