package-versioner 0.8.3 → 0.8.5
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 +14 -2
- package/dist/index.cjs +168 -148
- package/dist/index.js +168 -148
- package/docs/CI_CD_INTEGRATION.md +2 -1
- package/docs/changelogs.md +7 -0
- package/docs/versioning.md +114 -78
- package/package-versioner.schema.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,6 +55,9 @@ npx package-versioner --bump patch --prerelease alpha
|
|
|
55
55
|
# Target specific packages (only in async/independent mode, comma-separated)
|
|
56
56
|
npx package-versioner -t @scope/package-a,@scope/package-b
|
|
57
57
|
|
|
58
|
+
# Run from a different directory
|
|
59
|
+
npx package-versioner --project-dir /path/to/project
|
|
60
|
+
|
|
58
61
|
# Perform a dry run: calculates version, logs actions, but makes no file changes or Git commits/tags
|
|
59
62
|
npx package-versioner --dry-run
|
|
60
63
|
|
|
@@ -159,7 +162,7 @@ Customize behaviour by creating a `version.config.json` file in your project roo
|
|
|
159
162
|
#### Monorepo-Specific Options
|
|
160
163
|
- `synced`: Whether all packages should be versioned together (default: true)
|
|
161
164
|
- `skip`: Array of package names or patterns to exclude from versioning. Supports exact names, scope wildcards, path patterns, and global wildcards (e.g., ["@scope/package-a", "@scope/*", "packages/**/*"])
|
|
162
|
-
- `packages`: Array of package names or patterns to target for versioning. Supports exact names, scope wildcards, and global wildcards (e.g., ["@scope/package-a", "@scope/*", "*"])
|
|
165
|
+
- `packages`: Array of package names or patterns to target for versioning. Supports exact names, scope wildcards, path patterns and global wildcards (e.g., ["@scope/package-a", "@scope/*", "*"])
|
|
163
166
|
- `mainPackage`: Package name whose commit history should drive version determination
|
|
164
167
|
- `packageSpecificTags`: Whether to enable package-specific tagging behaviour (default: false)
|
|
165
168
|
- `updateInternalDependencies`: How to update internal dependencies ("patch", "minor", "major", or "inherit")
|
|
@@ -185,6 +188,15 @@ Target all packages within a specific scope:
|
|
|
185
188
|
}
|
|
186
189
|
```
|
|
187
190
|
|
|
191
|
+
#### Path Patterns / Globs
|
|
192
|
+
Target all packages in a directory or matching a path pattern:
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"packages": ["packages/**/*", "examples/**"]
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
This will match all packages in nested directories under `packages/` or `examples/`.
|
|
199
|
+
|
|
188
200
|
#### Global Wildcard
|
|
189
201
|
Target all packages in the workspace:
|
|
190
202
|
```json
|
|
@@ -197,7 +209,7 @@ Target all packages in the workspace:
|
|
|
197
209
|
Combine different pattern types:
|
|
198
210
|
```json
|
|
199
211
|
{
|
|
200
|
-
"packages": ["@mycompany/*", "@utils/logger", "legacy-package"]
|
|
212
|
+
"packages": ["@mycompany/*", "@utils/logger", "legacy-package", "packages/**/*"]
|
|
201
213
|
}
|
|
202
214
|
```
|
|
203
215
|
|
package/dist/index.cjs
CHANGED
|
@@ -759,7 +759,7 @@ function matchesPackageNamePattern(packageName, pattern) {
|
|
|
759
759
|
}
|
|
760
760
|
|
|
761
761
|
// src/core/versionStrategies.ts
|
|
762
|
-
var
|
|
762
|
+
var import_node_child_process4 = require("child_process");
|
|
763
763
|
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
764
764
|
var path8 = __toESM(require("path"), 1);
|
|
765
765
|
|
|
@@ -1041,9 +1041,14 @@ function formatCommitMessage(template, version, packageName, additionalContext)
|
|
|
1041
1041
|
}
|
|
1042
1042
|
|
|
1043
1043
|
// src/git/tagsAndBranches.ts
|
|
1044
|
-
function getCommitsLength(pkgRoot) {
|
|
1044
|
+
function getCommitsLength(pkgRoot, sinceTag) {
|
|
1045
1045
|
try {
|
|
1046
|
-
|
|
1046
|
+
let gitCommand;
|
|
1047
|
+
if (sinceTag && sinceTag.trim() !== "") {
|
|
1048
|
+
gitCommand = `git rev-list --count ${sinceTag}..HEAD ${pkgRoot}`;
|
|
1049
|
+
} else {
|
|
1050
|
+
gitCommand = `git rev-list --count HEAD ^$(git describe --tags --abbrev=0) ${pkgRoot}`;
|
|
1051
|
+
}
|
|
1047
1052
|
const amount = execSync3(gitCommand).toString().trim();
|
|
1048
1053
|
return Number(amount);
|
|
1049
1054
|
} catch (error) {
|
|
@@ -1317,7 +1322,6 @@ function updatePackageVersion(packagePath, version) {
|
|
|
1317
1322
|
}
|
|
1318
1323
|
|
|
1319
1324
|
// src/package/packageProcessor.ts
|
|
1320
|
-
var import_node_child_process4 = require("child_process");
|
|
1321
1325
|
var fs8 = __toESM(require("fs"), 1);
|
|
1322
1326
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
1323
1327
|
var import_node_process4 = require("process");
|
|
@@ -1376,18 +1380,41 @@ function getVersionFromManifests(packageDir) {
|
|
|
1376
1380
|
manifestType: null
|
|
1377
1381
|
};
|
|
1378
1382
|
}
|
|
1379
|
-
function throwIfNoManifestsFound(packageDir) {
|
|
1380
|
-
const packageJsonPath = import_node_path6.default.join(packageDir, "package.json");
|
|
1381
|
-
const cargoTomlPath = import_node_path6.default.join(packageDir, "Cargo.toml");
|
|
1382
|
-
throw new Error(
|
|
1383
|
-
`Neither package.json nor Cargo.toml found at ${packageDir}. Checked paths: ${packageJsonPath}, ${cargoTomlPath}. Cannot determine version.`
|
|
1384
|
-
);
|
|
1385
|
-
}
|
|
1386
1383
|
|
|
1387
1384
|
// src/utils/versionUtils.ts
|
|
1388
1385
|
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
1389
1386
|
var import_semver2 = __toESM(require("semver"), 1);
|
|
1390
1387
|
var TOML2 = __toESM(require("smol-toml"), 1);
|
|
1388
|
+
|
|
1389
|
+
// src/git/tagVerification.ts
|
|
1390
|
+
function verifyTag(tagName, cwd5) {
|
|
1391
|
+
if (!tagName || tagName.trim() === "") {
|
|
1392
|
+
return { exists: false, reachable: false, error: "Empty tag name" };
|
|
1393
|
+
}
|
|
1394
|
+
try {
|
|
1395
|
+
execSync3(`git rev-parse --verify "${tagName}"`, {
|
|
1396
|
+
cwd: cwd5,
|
|
1397
|
+
stdio: "ignore"
|
|
1398
|
+
});
|
|
1399
|
+
return { exists: true, reachable: true };
|
|
1400
|
+
} catch (error) {
|
|
1401
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1402
|
+
if (errorMessage.includes("unknown revision") || errorMessage.includes("bad revision") || errorMessage.includes("No such ref")) {
|
|
1403
|
+
return {
|
|
1404
|
+
exists: false,
|
|
1405
|
+
reachable: false,
|
|
1406
|
+
error: `Tag '${tagName}' not found in repository`
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
return {
|
|
1410
|
+
exists: false,
|
|
1411
|
+
reachable: false,
|
|
1412
|
+
error: `Git error: ${errorMessage}`
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/utils/versionUtils.ts
|
|
1391
1418
|
var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
|
|
1392
1419
|
function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
1393
1420
|
if (prereleaseIdentifier === true) {
|
|
@@ -1421,6 +1448,59 @@ function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
|
|
|
1421
1448
|
}
|
|
1422
1449
|
return import_semver2.default.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
|
|
1423
1450
|
}
|
|
1451
|
+
async function getBestVersionSource(tagName, packageVersion, cwd5) {
|
|
1452
|
+
if (!(tagName == null ? void 0 : tagName.trim())) {
|
|
1453
|
+
return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
|
|
1454
|
+
}
|
|
1455
|
+
const verification = verifyTag(tagName, cwd5);
|
|
1456
|
+
if (!verification.exists || !verification.reachable) {
|
|
1457
|
+
if (packageVersion) {
|
|
1458
|
+
log(
|
|
1459
|
+
`Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
|
|
1460
|
+
"warning"
|
|
1461
|
+
);
|
|
1462
|
+
return { source: "package", version: packageVersion, reason: "Git tag unreachable" };
|
|
1463
|
+
}
|
|
1464
|
+
log(
|
|
1465
|
+
`Git tag '${tagName}' unreachable and no package version available, using initial version`,
|
|
1466
|
+
"warning"
|
|
1467
|
+
);
|
|
1468
|
+
return {
|
|
1469
|
+
source: "initial",
|
|
1470
|
+
version: "0.1.0",
|
|
1471
|
+
reason: "Git tag unreachable, no package version"
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
if (!packageVersion) {
|
|
1475
|
+
return {
|
|
1476
|
+
source: "git",
|
|
1477
|
+
version: tagName,
|
|
1478
|
+
reason: "Git tag exists, no package version to compare"
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
try {
|
|
1482
|
+
const cleanTagVersion = tagName.replace(/^.*?([0-9])/, "$1");
|
|
1483
|
+
const cleanPackageVersion = packageVersion;
|
|
1484
|
+
if (import_semver2.default.gt(cleanPackageVersion, cleanTagVersion)) {
|
|
1485
|
+
log(
|
|
1486
|
+
`Package version ${packageVersion} is newer than git tag ${tagName}, using package version`,
|
|
1487
|
+
"info"
|
|
1488
|
+
);
|
|
1489
|
+
return { source: "package", version: packageVersion, reason: "Package version is newer" };
|
|
1490
|
+
}
|
|
1491
|
+
if (import_semver2.default.gt(cleanTagVersion, cleanPackageVersion)) {
|
|
1492
|
+
log(
|
|
1493
|
+
`Git tag ${tagName} is newer than package version ${packageVersion}, using git tag`,
|
|
1494
|
+
"info"
|
|
1495
|
+
);
|
|
1496
|
+
return { source: "git", version: tagName, reason: "Git tag is newer" };
|
|
1497
|
+
}
|
|
1498
|
+
return { source: "git", version: tagName, reason: "Versions equal, using git tag" };
|
|
1499
|
+
} catch (error) {
|
|
1500
|
+
log(`Failed to compare versions, defaulting to git tag: ${error}`, "warning");
|
|
1501
|
+
return { source: "git", version: tagName, reason: "Version comparison failed" };
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1424
1504
|
|
|
1425
1505
|
// src/core/versionCalculator.ts
|
|
1426
1506
|
async function calculateVersion(config, options) {
|
|
@@ -1452,76 +1532,35 @@ async function calculateVersion(config, options) {
|
|
|
1452
1532
|
return `${packageName}@${prefix}`;
|
|
1453
1533
|
}, escapeRegExp3 = function(string) {
|
|
1454
1534
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1535
|
+
}, getCurrentVersionFromSource2 = function() {
|
|
1536
|
+
if (!versionSource) {
|
|
1537
|
+
if (hasNoTags) {
|
|
1538
|
+
return initialVersion;
|
|
1539
|
+
}
|
|
1540
|
+
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1541
|
+
return import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1542
|
+
}
|
|
1543
|
+
if (versionSource.source === "git") {
|
|
1544
|
+
const cleanedTag = import_semver3.default.clean(versionSource.version) || versionSource.version;
|
|
1545
|
+
return import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1546
|
+
}
|
|
1547
|
+
return versionSource.version;
|
|
1455
1548
|
};
|
|
1456
|
-
var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3;
|
|
1549
|
+
var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3, getCurrentVersionFromSource = getCurrentVersionFromSource2;
|
|
1457
1550
|
const originalPrefix = versionPrefix || "";
|
|
1458
1551
|
const tagSearchPattern = determineTagSearchPattern2(name, originalPrefix);
|
|
1459
1552
|
const escapedTagPattern = escapeRegExp3(tagSearchPattern);
|
|
1460
|
-
|
|
1553
|
+
let versionSource;
|
|
1554
|
+
if (pkgPath) {
|
|
1461
1555
|
const packageDir = pkgPath || (0, import_node_process3.cwd)();
|
|
1462
1556
|
const manifestResult = getVersionFromManifests(packageDir);
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
const packageVersion = manifestResult.version;
|
|
1467
|
-
if (import_semver3.default.gt(packageVersion, tagVersion)) {
|
|
1468
|
-
log(
|
|
1469
|
-
`Warning: Version mismatch detected!
|
|
1470
|
-
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
1471
|
-
\u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
|
|
1472
|
-
\u2022 Package version is AHEAD of Git tags
|
|
1473
|
-
|
|
1474
|
-
This usually happens when:
|
|
1475
|
-
\u2022 A version was released but the tag wasn't pushed to the remote repository
|
|
1476
|
-
\u2022 The ${manifestResult.manifestType} was manually updated without creating a corresponding tag
|
|
1477
|
-
\u2022 You're running in CI and the latest tag isn't available yet
|
|
1478
|
-
|
|
1479
|
-
The tool will use the Git tag version (${tagVersion}) as the base for calculation.
|
|
1480
|
-
Expected next version will be based on ${tagVersion}, not ${packageVersion}.
|
|
1481
|
-
|
|
1482
|
-
To fix this mismatch:
|
|
1483
|
-
\u2022 Push missing tags: git push origin --tags
|
|
1484
|
-
\u2022 Or use package version as base by ensuring tags are up to date`,
|
|
1485
|
-
"warning"
|
|
1486
|
-
);
|
|
1487
|
-
} else if (import_semver3.default.gt(tagVersion, packageVersion)) {
|
|
1488
|
-
log(
|
|
1489
|
-
`Warning: Version mismatch detected!
|
|
1490
|
-
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
1491
|
-
\u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
|
|
1492
|
-
\u2022 Git tag version is AHEAD of package version
|
|
1493
|
-
|
|
1494
|
-
This usually happens when:
|
|
1495
|
-
\u2022 A release was tagged but the ${manifestResult.manifestType} wasn't updated
|
|
1496
|
-
\u2022 You're on an older branch that hasn't been updated with the latest version
|
|
1497
|
-
\u2022 Automated release process created tags but didn't update manifest files
|
|
1498
|
-
\u2022 You pulled tags but not the corresponding commits that update the package version
|
|
1499
|
-
|
|
1500
|
-
The tool will use the Git tag version (${tagVersion}) as the base for calculation.
|
|
1501
|
-
This will likely result in a version that's already been released.
|
|
1502
|
-
|
|
1503
|
-
To fix this mismatch:
|
|
1504
|
-
\u2022 Update ${manifestResult.manifestType}: Set version to ${tagVersion} or higher
|
|
1505
|
-
\u2022 Or checkout the branch/commit that corresponds to the tag
|
|
1506
|
-
\u2022 Or ensure your branch is up to date with the latest changes`,
|
|
1507
|
-
"warning"
|
|
1508
|
-
);
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1557
|
+
const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
|
|
1558
|
+
versionSource = await getBestVersionSource(latestTag, packageVersion, packageDir);
|
|
1559
|
+
log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
|
|
1511
1560
|
}
|
|
1512
1561
|
const specifiedType = type;
|
|
1513
1562
|
if (specifiedType) {
|
|
1514
|
-
|
|
1515
|
-
return getPackageVersionFallback(
|
|
1516
|
-
pkgPath,
|
|
1517
|
-
name,
|
|
1518
|
-
specifiedType,
|
|
1519
|
-
normalizedPrereleaseId,
|
|
1520
|
-
initialVersion
|
|
1521
|
-
);
|
|
1522
|
-
}
|
|
1523
|
-
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1524
|
-
const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1563
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1525
1564
|
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (import_semver3.default.prerelease(currentVersion) || normalizedPrereleaseId)) {
|
|
1526
1565
|
log(
|
|
1527
1566
|
normalizedPrereleaseId ? `Creating prerelease version with identifier '${normalizedPrereleaseId}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
|
|
@@ -1551,17 +1590,7 @@ To fix this mismatch:
|
|
|
1551
1590
|
}
|
|
1552
1591
|
}
|
|
1553
1592
|
if (branchVersionType) {
|
|
1554
|
-
|
|
1555
|
-
return getPackageVersionFallback(
|
|
1556
|
-
pkgPath,
|
|
1557
|
-
name,
|
|
1558
|
-
branchVersionType,
|
|
1559
|
-
normalizedPrereleaseId,
|
|
1560
|
-
initialVersion
|
|
1561
|
-
);
|
|
1562
|
-
}
|
|
1563
|
-
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1564
|
-
const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1593
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1565
1594
|
log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
|
|
1566
1595
|
return bumpVersion(currentVersion, branchVersionType, normalizedPrereleaseId);
|
|
1567
1596
|
}
|
|
@@ -1571,35 +1600,34 @@ To fix this mismatch:
|
|
|
1571
1600
|
bumper.loadPreset(preset);
|
|
1572
1601
|
const recommendedBump = await bumper.bump();
|
|
1573
1602
|
const releaseTypeFromCommits = recommendedBump && "releaseType" in recommendedBump ? recommendedBump.releaseType : void 0;
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1603
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1604
|
+
if (versionSource && versionSource.source === "git") {
|
|
1605
|
+
const checkPath = pkgPath || (0, import_node_process3.cwd)();
|
|
1606
|
+
const commitsLength = getCommitsLength(checkPath, versionSource.version);
|
|
1607
|
+
if (commitsLength === 0) {
|
|
1608
|
+
log(
|
|
1609
|
+
`No new commits found for ${name || "project"} since ${versionSource.version}, skipping version bump`,
|
|
1610
|
+
"info"
|
|
1582
1611
|
);
|
|
1612
|
+
return "";
|
|
1583
1613
|
}
|
|
1584
|
-
|
|
1585
|
-
}
|
|
1586
|
-
const checkPath = pkgPath || (0, import_node_process3.cwd)();
|
|
1587
|
-
const commitsLength = getCommitsLength(checkPath);
|
|
1588
|
-
if (commitsLength === 0) {
|
|
1614
|
+
} else if (versionSource && versionSource.source === "package") {
|
|
1589
1615
|
log(
|
|
1590
|
-
`
|
|
1591
|
-
"
|
|
1616
|
+
`Using package version ${versionSource.version} as base, letting conventional commits determine bump necessity`,
|
|
1617
|
+
"debug"
|
|
1592
1618
|
);
|
|
1593
|
-
return "";
|
|
1594
1619
|
}
|
|
1595
1620
|
if (!releaseTypeFromCommits) {
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1621
|
+
if (latestTag && latestTag.trim() !== "") {
|
|
1622
|
+
log(
|
|
1623
|
+
`No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`,
|
|
1624
|
+
"info"
|
|
1625
|
+
);
|
|
1626
|
+
} else {
|
|
1627
|
+
log(`No relevant commits found for ${name || "project"}, skipping version bump`, "info");
|
|
1628
|
+
}
|
|
1600
1629
|
return "";
|
|
1601
1630
|
}
|
|
1602
|
-
const currentVersion = import_semver3.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1603
1631
|
return bumpVersion(currentVersion, releaseTypeFromCommits, normalizedPrereleaseId);
|
|
1604
1632
|
} catch (error) {
|
|
1605
1633
|
log(`Failed to calculate version for ${name || "project"}`, "error");
|
|
@@ -1620,40 +1648,6 @@ To fix this mismatch:
|
|
|
1620
1648
|
throw error;
|
|
1621
1649
|
}
|
|
1622
1650
|
}
|
|
1623
|
-
function getPackageVersionFallback(pkgPath, name, releaseType, prereleaseIdentifier, initialVersion) {
|
|
1624
|
-
const packageDir = pkgPath || (0, import_node_process3.cwd)();
|
|
1625
|
-
const manifestResult = getVersionFromManifests(packageDir);
|
|
1626
|
-
if (manifestResult.manifestFound && manifestResult.version) {
|
|
1627
|
-
log(
|
|
1628
|
-
`No tags found for ${name || "package"}, using ${manifestResult.manifestType} version: ${manifestResult.version} as base`,
|
|
1629
|
-
"info"
|
|
1630
|
-
);
|
|
1631
|
-
return calculateNextVersion(
|
|
1632
|
-
manifestResult.version,
|
|
1633
|
-
manifestResult.manifestType || "manifest",
|
|
1634
|
-
name,
|
|
1635
|
-
releaseType,
|
|
1636
|
-
prereleaseIdentifier,
|
|
1637
|
-
initialVersion
|
|
1638
|
-
);
|
|
1639
|
-
}
|
|
1640
|
-
throwIfNoManifestsFound(packageDir);
|
|
1641
|
-
}
|
|
1642
|
-
function calculateNextVersion(version, manifestType, name, releaseType, prereleaseIdentifier, initialVersion) {
|
|
1643
|
-
log(
|
|
1644
|
-
`No tags found for ${name || "package"}, using ${manifestType} version: ${version} as base`,
|
|
1645
|
-
"info"
|
|
1646
|
-
);
|
|
1647
|
-
if (STANDARD_BUMP_TYPES.includes(releaseType) && (import_semver3.default.prerelease(version) || prereleaseIdentifier)) {
|
|
1648
|
-
log(
|
|
1649
|
-
prereleaseIdentifier ? `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${releaseType}` : `Cleaning prerelease identifier from ${version} for ${releaseType} bump`,
|
|
1650
|
-
"debug"
|
|
1651
|
-
);
|
|
1652
|
-
return bumpVersion(version, releaseType, prereleaseIdentifier);
|
|
1653
|
-
}
|
|
1654
|
-
const result = bumpVersion(version, releaseType, prereleaseIdentifier);
|
|
1655
|
-
return result || initialVersion;
|
|
1656
|
-
}
|
|
1657
1651
|
|
|
1658
1652
|
// src/utils/packageMatching.ts
|
|
1659
1653
|
var import_micromatch2 = __toESM(require("micromatch"), 1);
|
|
@@ -1803,14 +1797,14 @@ var PackageProcessor = class {
|
|
|
1803
1797
|
try {
|
|
1804
1798
|
let revisionRange;
|
|
1805
1799
|
if (latestTag) {
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
cwd: pkgPath,
|
|
1809
|
-
stdio: "ignore"
|
|
1810
|
-
});
|
|
1800
|
+
const verification = verifyTag(latestTag, pkgPath);
|
|
1801
|
+
if (verification.exists && verification.reachable) {
|
|
1811
1802
|
revisionRange = `${latestTag}..HEAD`;
|
|
1812
|
-
}
|
|
1813
|
-
log(
|
|
1803
|
+
} else {
|
|
1804
|
+
log(
|
|
1805
|
+
`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`,
|
|
1806
|
+
"debug"
|
|
1807
|
+
);
|
|
1814
1808
|
revisionRange = "HEAD";
|
|
1815
1809
|
}
|
|
1816
1810
|
} else {
|
|
@@ -1999,7 +1993,7 @@ function createSyncedStrategy(config) {
|
|
|
1999
1993
|
mainPackage
|
|
2000
1994
|
} = config;
|
|
2001
1995
|
const formattedPrefix = formatVersionPrefix(versionPrefix || "v");
|
|
2002
|
-
|
|
1996
|
+
let latestTag = await getLatestTag();
|
|
2003
1997
|
let mainPkgPath = packages.root;
|
|
2004
1998
|
let mainPkgName;
|
|
2005
1999
|
if (mainPackage) {
|
|
@@ -2022,6 +2016,21 @@ function createSyncedStrategy(config) {
|
|
|
2022
2016
|
"warning"
|
|
2023
2017
|
);
|
|
2024
2018
|
}
|
|
2019
|
+
if (mainPkgName) {
|
|
2020
|
+
const packageSpecificTag = await getLatestTagForPackage(mainPkgName, formattedPrefix, {
|
|
2021
|
+
tagTemplate,
|
|
2022
|
+
packageSpecificTags: config.packageSpecificTags
|
|
2023
|
+
});
|
|
2024
|
+
if (packageSpecificTag) {
|
|
2025
|
+
latestTag = packageSpecificTag;
|
|
2026
|
+
log(`Using package-specific tag for ${mainPkgName}: ${latestTag}`, "debug");
|
|
2027
|
+
} else {
|
|
2028
|
+
log(
|
|
2029
|
+
`No package-specific tag found for ${mainPkgName}, using global tag: ${latestTag}`,
|
|
2030
|
+
"debug"
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2025
2034
|
const nextVersion = await calculateVersion(config, {
|
|
2026
2035
|
latestTag,
|
|
2027
2036
|
versionPrefix: formattedPrefix,
|
|
@@ -2164,7 +2173,7 @@ function createSingleStrategy(config) {
|
|
|
2164
2173
|
let revisionRange;
|
|
2165
2174
|
if (latestTag) {
|
|
2166
2175
|
try {
|
|
2167
|
-
(0,
|
|
2176
|
+
(0, import_node_child_process4.execSync)(`git rev-parse --verify "${latestTag}"`, {
|
|
2168
2177
|
cwd: pkgPath,
|
|
2169
2178
|
stdio: "ignore"
|
|
2170
2179
|
});
|
|
@@ -2451,11 +2460,22 @@ async function run() {
|
|
|
2451
2460
|
program.command("version", { isDefault: true }).description("Version a package or packages based on configuration").option(
|
|
2452
2461
|
"-c, --config <path>",
|
|
2453
2462
|
"Path to config file (defaults to version.config.json in current directory)"
|
|
2454
|
-
).option("-d, --dry-run", "Dry run (no changes made)", false).option("-b, --bump <type>", "Specify bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --synced", "Use 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").action(async (options) => {
|
|
2463
|
+
).option("-d, --dry-run", "Dry run (no changes made)", false).option("-b, --bump <type>", "Specify bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --synced", "Use 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").option("--project-dir <path>", "Project directory to run commands in", process.cwd()).action(async (options) => {
|
|
2455
2464
|
if (options.json) {
|
|
2456
2465
|
enableJsonOutput(options.dryRun);
|
|
2457
2466
|
}
|
|
2458
2467
|
try {
|
|
2468
|
+
const originalCwd = process.cwd();
|
|
2469
|
+
if (options.projectDir && options.projectDir !== originalCwd) {
|
|
2470
|
+
try {
|
|
2471
|
+
process.chdir(options.projectDir);
|
|
2472
|
+
log(`Changed working directory to: ${options.projectDir}`, "debug");
|
|
2473
|
+
} catch (error) {
|
|
2474
|
+
throw new Error(
|
|
2475
|
+
`Failed to change to directory "${options.projectDir}": ${error instanceof Error ? error.message : String(error)}`
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2459
2479
|
const config = await loadConfig(options.config);
|
|
2460
2480
|
log(`Loaded configuration from ${options.config || "version.config.json"}`, "info");
|
|
2461
2481
|
if (options.dryRun) config.dryRun = true;
|
package/dist/index.js
CHANGED
|
@@ -726,7 +726,7 @@ function matchesPackageNamePattern(packageName, pattern) {
|
|
|
726
726
|
}
|
|
727
727
|
|
|
728
728
|
// src/core/versionStrategies.ts
|
|
729
|
-
import { execSync as
|
|
729
|
+
import { execSync as execSync4 } from "child_process";
|
|
730
730
|
import fs9 from "fs";
|
|
731
731
|
import * as path8 from "path";
|
|
732
732
|
|
|
@@ -1008,9 +1008,14 @@ function formatCommitMessage(template, version, packageName, additionalContext)
|
|
|
1008
1008
|
}
|
|
1009
1009
|
|
|
1010
1010
|
// src/git/tagsAndBranches.ts
|
|
1011
|
-
function getCommitsLength(pkgRoot) {
|
|
1011
|
+
function getCommitsLength(pkgRoot, sinceTag) {
|
|
1012
1012
|
try {
|
|
1013
|
-
|
|
1013
|
+
let gitCommand;
|
|
1014
|
+
if (sinceTag && sinceTag.trim() !== "") {
|
|
1015
|
+
gitCommand = `git rev-list --count ${sinceTag}..HEAD ${pkgRoot}`;
|
|
1016
|
+
} else {
|
|
1017
|
+
gitCommand = `git rev-list --count HEAD ^$(git describe --tags --abbrev=0) ${pkgRoot}`;
|
|
1018
|
+
}
|
|
1014
1019
|
const amount = execSync3(gitCommand).toString().trim();
|
|
1015
1020
|
return Number(amount);
|
|
1016
1021
|
} catch (error) {
|
|
@@ -1284,7 +1289,6 @@ function updatePackageVersion(packagePath, version) {
|
|
|
1284
1289
|
}
|
|
1285
1290
|
|
|
1286
1291
|
// src/package/packageProcessor.ts
|
|
1287
|
-
import { execSync as execSync4 } from "child_process";
|
|
1288
1292
|
import * as fs8 from "fs";
|
|
1289
1293
|
import path7 from "path";
|
|
1290
1294
|
import { exit } from "process";
|
|
@@ -1343,18 +1347,41 @@ function getVersionFromManifests(packageDir) {
|
|
|
1343
1347
|
manifestType: null
|
|
1344
1348
|
};
|
|
1345
1349
|
}
|
|
1346
|
-
function throwIfNoManifestsFound(packageDir) {
|
|
1347
|
-
const packageJsonPath = path6.join(packageDir, "package.json");
|
|
1348
|
-
const cargoTomlPath = path6.join(packageDir, "Cargo.toml");
|
|
1349
|
-
throw new Error(
|
|
1350
|
-
`Neither package.json nor Cargo.toml found at ${packageDir}. Checked paths: ${packageJsonPath}, ${cargoTomlPath}. Cannot determine version.`
|
|
1351
|
-
);
|
|
1352
|
-
}
|
|
1353
1350
|
|
|
1354
1351
|
// src/utils/versionUtils.ts
|
|
1355
1352
|
import fs7 from "fs";
|
|
1356
1353
|
import semver2 from "semver";
|
|
1357
1354
|
import * as TOML2 from "smol-toml";
|
|
1355
|
+
|
|
1356
|
+
// src/git/tagVerification.ts
|
|
1357
|
+
function verifyTag(tagName, cwd5) {
|
|
1358
|
+
if (!tagName || tagName.trim() === "") {
|
|
1359
|
+
return { exists: false, reachable: false, error: "Empty tag name" };
|
|
1360
|
+
}
|
|
1361
|
+
try {
|
|
1362
|
+
execSync3(`git rev-parse --verify "${tagName}"`, {
|
|
1363
|
+
cwd: cwd5,
|
|
1364
|
+
stdio: "ignore"
|
|
1365
|
+
});
|
|
1366
|
+
return { exists: true, reachable: true };
|
|
1367
|
+
} catch (error) {
|
|
1368
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1369
|
+
if (errorMessage.includes("unknown revision") || errorMessage.includes("bad revision") || errorMessage.includes("No such ref")) {
|
|
1370
|
+
return {
|
|
1371
|
+
exists: false,
|
|
1372
|
+
reachable: false,
|
|
1373
|
+
error: `Tag '${tagName}' not found in repository`
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
return {
|
|
1377
|
+
exists: false,
|
|
1378
|
+
reachable: false,
|
|
1379
|
+
error: `Git error: ${errorMessage}`
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
// src/utils/versionUtils.ts
|
|
1358
1385
|
var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
|
|
1359
1386
|
function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
1360
1387
|
if (prereleaseIdentifier === true) {
|
|
@@ -1388,6 +1415,59 @@ function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
|
|
|
1388
1415
|
}
|
|
1389
1416
|
return semver2.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
|
|
1390
1417
|
}
|
|
1418
|
+
async function getBestVersionSource(tagName, packageVersion, cwd5) {
|
|
1419
|
+
if (!(tagName == null ? void 0 : tagName.trim())) {
|
|
1420
|
+
return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
|
|
1421
|
+
}
|
|
1422
|
+
const verification = verifyTag(tagName, cwd5);
|
|
1423
|
+
if (!verification.exists || !verification.reachable) {
|
|
1424
|
+
if (packageVersion) {
|
|
1425
|
+
log(
|
|
1426
|
+
`Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
|
|
1427
|
+
"warning"
|
|
1428
|
+
);
|
|
1429
|
+
return { source: "package", version: packageVersion, reason: "Git tag unreachable" };
|
|
1430
|
+
}
|
|
1431
|
+
log(
|
|
1432
|
+
`Git tag '${tagName}' unreachable and no package version available, using initial version`,
|
|
1433
|
+
"warning"
|
|
1434
|
+
);
|
|
1435
|
+
return {
|
|
1436
|
+
source: "initial",
|
|
1437
|
+
version: "0.1.0",
|
|
1438
|
+
reason: "Git tag unreachable, no package version"
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
if (!packageVersion) {
|
|
1442
|
+
return {
|
|
1443
|
+
source: "git",
|
|
1444
|
+
version: tagName,
|
|
1445
|
+
reason: "Git tag exists, no package version to compare"
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
try {
|
|
1449
|
+
const cleanTagVersion = tagName.replace(/^.*?([0-9])/, "$1");
|
|
1450
|
+
const cleanPackageVersion = packageVersion;
|
|
1451
|
+
if (semver2.gt(cleanPackageVersion, cleanTagVersion)) {
|
|
1452
|
+
log(
|
|
1453
|
+
`Package version ${packageVersion} is newer than git tag ${tagName}, using package version`,
|
|
1454
|
+
"info"
|
|
1455
|
+
);
|
|
1456
|
+
return { source: "package", version: packageVersion, reason: "Package version is newer" };
|
|
1457
|
+
}
|
|
1458
|
+
if (semver2.gt(cleanTagVersion, cleanPackageVersion)) {
|
|
1459
|
+
log(
|
|
1460
|
+
`Git tag ${tagName} is newer than package version ${packageVersion}, using git tag`,
|
|
1461
|
+
"info"
|
|
1462
|
+
);
|
|
1463
|
+
return { source: "git", version: tagName, reason: "Git tag is newer" };
|
|
1464
|
+
}
|
|
1465
|
+
return { source: "git", version: tagName, reason: "Versions equal, using git tag" };
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
log(`Failed to compare versions, defaulting to git tag: ${error}`, "warning");
|
|
1468
|
+
return { source: "git", version: tagName, reason: "Version comparison failed" };
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1391
1471
|
|
|
1392
1472
|
// src/core/versionCalculator.ts
|
|
1393
1473
|
async function calculateVersion(config, options) {
|
|
@@ -1419,76 +1499,35 @@ async function calculateVersion(config, options) {
|
|
|
1419
1499
|
return `${packageName}@${prefix}`;
|
|
1420
1500
|
}, escapeRegExp3 = function(string) {
|
|
1421
1501
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1502
|
+
}, getCurrentVersionFromSource2 = function() {
|
|
1503
|
+
if (!versionSource) {
|
|
1504
|
+
if (hasNoTags) {
|
|
1505
|
+
return initialVersion;
|
|
1506
|
+
}
|
|
1507
|
+
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1508
|
+
return semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1509
|
+
}
|
|
1510
|
+
if (versionSource.source === "git") {
|
|
1511
|
+
const cleanedTag = semver3.clean(versionSource.version) || versionSource.version;
|
|
1512
|
+
return semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1513
|
+
}
|
|
1514
|
+
return versionSource.version;
|
|
1422
1515
|
};
|
|
1423
|
-
var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3;
|
|
1516
|
+
var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3, getCurrentVersionFromSource = getCurrentVersionFromSource2;
|
|
1424
1517
|
const originalPrefix = versionPrefix || "";
|
|
1425
1518
|
const tagSearchPattern = determineTagSearchPattern2(name, originalPrefix);
|
|
1426
1519
|
const escapedTagPattern = escapeRegExp3(tagSearchPattern);
|
|
1427
|
-
|
|
1520
|
+
let versionSource;
|
|
1521
|
+
if (pkgPath) {
|
|
1428
1522
|
const packageDir = pkgPath || cwd3();
|
|
1429
1523
|
const manifestResult = getVersionFromManifests(packageDir);
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
const packageVersion = manifestResult.version;
|
|
1434
|
-
if (semver3.gt(packageVersion, tagVersion)) {
|
|
1435
|
-
log(
|
|
1436
|
-
`Warning: Version mismatch detected!
|
|
1437
|
-
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
1438
|
-
\u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
|
|
1439
|
-
\u2022 Package version is AHEAD of Git tags
|
|
1440
|
-
|
|
1441
|
-
This usually happens when:
|
|
1442
|
-
\u2022 A version was released but the tag wasn't pushed to the remote repository
|
|
1443
|
-
\u2022 The ${manifestResult.manifestType} was manually updated without creating a corresponding tag
|
|
1444
|
-
\u2022 You're running in CI and the latest tag isn't available yet
|
|
1445
|
-
|
|
1446
|
-
The tool will use the Git tag version (${tagVersion}) as the base for calculation.
|
|
1447
|
-
Expected next version will be based on ${tagVersion}, not ${packageVersion}.
|
|
1448
|
-
|
|
1449
|
-
To fix this mismatch:
|
|
1450
|
-
\u2022 Push missing tags: git push origin --tags
|
|
1451
|
-
\u2022 Or use package version as base by ensuring tags are up to date`,
|
|
1452
|
-
"warning"
|
|
1453
|
-
);
|
|
1454
|
-
} else if (semver3.gt(tagVersion, packageVersion)) {
|
|
1455
|
-
log(
|
|
1456
|
-
`Warning: Version mismatch detected!
|
|
1457
|
-
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
1458
|
-
\u2022 Latest Git tag version: ${tagVersion} (from ${latestTag})
|
|
1459
|
-
\u2022 Git tag version is AHEAD of package version
|
|
1460
|
-
|
|
1461
|
-
This usually happens when:
|
|
1462
|
-
\u2022 A release was tagged but the ${manifestResult.manifestType} wasn't updated
|
|
1463
|
-
\u2022 You're on an older branch that hasn't been updated with the latest version
|
|
1464
|
-
\u2022 Automated release process created tags but didn't update manifest files
|
|
1465
|
-
\u2022 You pulled tags but not the corresponding commits that update the package version
|
|
1466
|
-
|
|
1467
|
-
The tool will use the Git tag version (${tagVersion}) as the base for calculation.
|
|
1468
|
-
This will likely result in a version that's already been released.
|
|
1469
|
-
|
|
1470
|
-
To fix this mismatch:
|
|
1471
|
-
\u2022 Update ${manifestResult.manifestType}: Set version to ${tagVersion} or higher
|
|
1472
|
-
\u2022 Or checkout the branch/commit that corresponds to the tag
|
|
1473
|
-
\u2022 Or ensure your branch is up to date with the latest changes`,
|
|
1474
|
-
"warning"
|
|
1475
|
-
);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1524
|
+
const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
|
|
1525
|
+
versionSource = await getBestVersionSource(latestTag, packageVersion, packageDir);
|
|
1526
|
+
log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
|
|
1478
1527
|
}
|
|
1479
1528
|
const specifiedType = type;
|
|
1480
1529
|
if (specifiedType) {
|
|
1481
|
-
|
|
1482
|
-
return getPackageVersionFallback(
|
|
1483
|
-
pkgPath,
|
|
1484
|
-
name,
|
|
1485
|
-
specifiedType,
|
|
1486
|
-
normalizedPrereleaseId,
|
|
1487
|
-
initialVersion
|
|
1488
|
-
);
|
|
1489
|
-
}
|
|
1490
|
-
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1491
|
-
const currentVersion = semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1530
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1492
1531
|
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (semver3.prerelease(currentVersion) || normalizedPrereleaseId)) {
|
|
1493
1532
|
log(
|
|
1494
1533
|
normalizedPrereleaseId ? `Creating prerelease version with identifier '${normalizedPrereleaseId}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
|
|
@@ -1518,17 +1557,7 @@ To fix this mismatch:
|
|
|
1518
1557
|
}
|
|
1519
1558
|
}
|
|
1520
1559
|
if (branchVersionType) {
|
|
1521
|
-
|
|
1522
|
-
return getPackageVersionFallback(
|
|
1523
|
-
pkgPath,
|
|
1524
|
-
name,
|
|
1525
|
-
branchVersionType,
|
|
1526
|
-
normalizedPrereleaseId,
|
|
1527
|
-
initialVersion
|
|
1528
|
-
);
|
|
1529
|
-
}
|
|
1530
|
-
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1531
|
-
const currentVersion = semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1560
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1532
1561
|
log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
|
|
1533
1562
|
return bumpVersion(currentVersion, branchVersionType, normalizedPrereleaseId);
|
|
1534
1563
|
}
|
|
@@ -1538,35 +1567,34 @@ To fix this mismatch:
|
|
|
1538
1567
|
bumper.loadPreset(preset);
|
|
1539
1568
|
const recommendedBump = await bumper.bump();
|
|
1540
1569
|
const releaseTypeFromCommits = recommendedBump && "releaseType" in recommendedBump ? recommendedBump.releaseType : void 0;
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1570
|
+
const currentVersion = getCurrentVersionFromSource2();
|
|
1571
|
+
if (versionSource && versionSource.source === "git") {
|
|
1572
|
+
const checkPath = pkgPath || cwd3();
|
|
1573
|
+
const commitsLength = getCommitsLength(checkPath, versionSource.version);
|
|
1574
|
+
if (commitsLength === 0) {
|
|
1575
|
+
log(
|
|
1576
|
+
`No new commits found for ${name || "project"} since ${versionSource.version}, skipping version bump`,
|
|
1577
|
+
"info"
|
|
1549
1578
|
);
|
|
1579
|
+
return "";
|
|
1550
1580
|
}
|
|
1551
|
-
|
|
1552
|
-
}
|
|
1553
|
-
const checkPath = pkgPath || cwd3();
|
|
1554
|
-
const commitsLength = getCommitsLength(checkPath);
|
|
1555
|
-
if (commitsLength === 0) {
|
|
1581
|
+
} else if (versionSource && versionSource.source === "package") {
|
|
1556
1582
|
log(
|
|
1557
|
-
`
|
|
1558
|
-
"
|
|
1583
|
+
`Using package version ${versionSource.version} as base, letting conventional commits determine bump necessity`,
|
|
1584
|
+
"debug"
|
|
1559
1585
|
);
|
|
1560
|
-
return "";
|
|
1561
1586
|
}
|
|
1562
1587
|
if (!releaseTypeFromCommits) {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1588
|
+
if (latestTag && latestTag.trim() !== "") {
|
|
1589
|
+
log(
|
|
1590
|
+
`No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`,
|
|
1591
|
+
"info"
|
|
1592
|
+
);
|
|
1593
|
+
} else {
|
|
1594
|
+
log(`No relevant commits found for ${name || "project"}, skipping version bump`, "info");
|
|
1595
|
+
}
|
|
1567
1596
|
return "";
|
|
1568
1597
|
}
|
|
1569
|
-
const currentVersion = semver3.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1570
1598
|
return bumpVersion(currentVersion, releaseTypeFromCommits, normalizedPrereleaseId);
|
|
1571
1599
|
} catch (error) {
|
|
1572
1600
|
log(`Failed to calculate version for ${name || "project"}`, "error");
|
|
@@ -1587,40 +1615,6 @@ To fix this mismatch:
|
|
|
1587
1615
|
throw error;
|
|
1588
1616
|
}
|
|
1589
1617
|
}
|
|
1590
|
-
function getPackageVersionFallback(pkgPath, name, releaseType, prereleaseIdentifier, initialVersion) {
|
|
1591
|
-
const packageDir = pkgPath || cwd3();
|
|
1592
|
-
const manifestResult = getVersionFromManifests(packageDir);
|
|
1593
|
-
if (manifestResult.manifestFound && manifestResult.version) {
|
|
1594
|
-
log(
|
|
1595
|
-
`No tags found for ${name || "package"}, using ${manifestResult.manifestType} version: ${manifestResult.version} as base`,
|
|
1596
|
-
"info"
|
|
1597
|
-
);
|
|
1598
|
-
return calculateNextVersion(
|
|
1599
|
-
manifestResult.version,
|
|
1600
|
-
manifestResult.manifestType || "manifest",
|
|
1601
|
-
name,
|
|
1602
|
-
releaseType,
|
|
1603
|
-
prereleaseIdentifier,
|
|
1604
|
-
initialVersion
|
|
1605
|
-
);
|
|
1606
|
-
}
|
|
1607
|
-
throwIfNoManifestsFound(packageDir);
|
|
1608
|
-
}
|
|
1609
|
-
function calculateNextVersion(version, manifestType, name, releaseType, prereleaseIdentifier, initialVersion) {
|
|
1610
|
-
log(
|
|
1611
|
-
`No tags found for ${name || "package"}, using ${manifestType} version: ${version} as base`,
|
|
1612
|
-
"info"
|
|
1613
|
-
);
|
|
1614
|
-
if (STANDARD_BUMP_TYPES.includes(releaseType) && (semver3.prerelease(version) || prereleaseIdentifier)) {
|
|
1615
|
-
log(
|
|
1616
|
-
prereleaseIdentifier ? `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${releaseType}` : `Cleaning prerelease identifier from ${version} for ${releaseType} bump`,
|
|
1617
|
-
"debug"
|
|
1618
|
-
);
|
|
1619
|
-
return bumpVersion(version, releaseType, prereleaseIdentifier);
|
|
1620
|
-
}
|
|
1621
|
-
const result = bumpVersion(version, releaseType, prereleaseIdentifier);
|
|
1622
|
-
return result || initialVersion;
|
|
1623
|
-
}
|
|
1624
1618
|
|
|
1625
1619
|
// src/utils/packageMatching.ts
|
|
1626
1620
|
import micromatch2 from "micromatch";
|
|
@@ -1770,14 +1764,14 @@ var PackageProcessor = class {
|
|
|
1770
1764
|
try {
|
|
1771
1765
|
let revisionRange;
|
|
1772
1766
|
if (latestTag) {
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
cwd: pkgPath,
|
|
1776
|
-
stdio: "ignore"
|
|
1777
|
-
});
|
|
1767
|
+
const verification = verifyTag(latestTag, pkgPath);
|
|
1768
|
+
if (verification.exists && verification.reachable) {
|
|
1778
1769
|
revisionRange = `${latestTag}..HEAD`;
|
|
1779
|
-
}
|
|
1780
|
-
log(
|
|
1770
|
+
} else {
|
|
1771
|
+
log(
|
|
1772
|
+
`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`,
|
|
1773
|
+
"debug"
|
|
1774
|
+
);
|
|
1781
1775
|
revisionRange = "HEAD";
|
|
1782
1776
|
}
|
|
1783
1777
|
} else {
|
|
@@ -1966,7 +1960,7 @@ function createSyncedStrategy(config) {
|
|
|
1966
1960
|
mainPackage
|
|
1967
1961
|
} = config;
|
|
1968
1962
|
const formattedPrefix = formatVersionPrefix(versionPrefix || "v");
|
|
1969
|
-
|
|
1963
|
+
let latestTag = await getLatestTag();
|
|
1970
1964
|
let mainPkgPath = packages.root;
|
|
1971
1965
|
let mainPkgName;
|
|
1972
1966
|
if (mainPackage) {
|
|
@@ -1989,6 +1983,21 @@ function createSyncedStrategy(config) {
|
|
|
1989
1983
|
"warning"
|
|
1990
1984
|
);
|
|
1991
1985
|
}
|
|
1986
|
+
if (mainPkgName) {
|
|
1987
|
+
const packageSpecificTag = await getLatestTagForPackage(mainPkgName, formattedPrefix, {
|
|
1988
|
+
tagTemplate,
|
|
1989
|
+
packageSpecificTags: config.packageSpecificTags
|
|
1990
|
+
});
|
|
1991
|
+
if (packageSpecificTag) {
|
|
1992
|
+
latestTag = packageSpecificTag;
|
|
1993
|
+
log(`Using package-specific tag for ${mainPkgName}: ${latestTag}`, "debug");
|
|
1994
|
+
} else {
|
|
1995
|
+
log(
|
|
1996
|
+
`No package-specific tag found for ${mainPkgName}, using global tag: ${latestTag}`,
|
|
1997
|
+
"debug"
|
|
1998
|
+
);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1992
2001
|
const nextVersion = await calculateVersion(config, {
|
|
1993
2002
|
latestTag,
|
|
1994
2003
|
versionPrefix: formattedPrefix,
|
|
@@ -2131,7 +2140,7 @@ function createSingleStrategy(config) {
|
|
|
2131
2140
|
let revisionRange;
|
|
2132
2141
|
if (latestTag) {
|
|
2133
2142
|
try {
|
|
2134
|
-
|
|
2143
|
+
execSync4(`git rev-parse --verify "${latestTag}"`, {
|
|
2135
2144
|
cwd: pkgPath,
|
|
2136
2145
|
stdio: "ignore"
|
|
2137
2146
|
});
|
|
@@ -2417,11 +2426,22 @@ async function run() {
|
|
|
2417
2426
|
program.command("version", { isDefault: true }).description("Version a package or packages based on configuration").option(
|
|
2418
2427
|
"-c, --config <path>",
|
|
2419
2428
|
"Path to config file (defaults to version.config.json in current directory)"
|
|
2420
|
-
).option("-d, --dry-run", "Dry run (no changes made)", false).option("-b, --bump <type>", "Specify bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --synced", "Use 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").action(async (options) => {
|
|
2429
|
+
).option("-d, --dry-run", "Dry run (no changes made)", false).option("-b, --bump <type>", "Specify bump type (patch|minor|major)").option("-p, --prerelease [identifier]", "Create prerelease version").option("-s, --synced", "Use 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").option("--project-dir <path>", "Project directory to run commands in", process.cwd()).action(async (options) => {
|
|
2421
2430
|
if (options.json) {
|
|
2422
2431
|
enableJsonOutput(options.dryRun);
|
|
2423
2432
|
}
|
|
2424
2433
|
try {
|
|
2434
|
+
const originalCwd = process.cwd();
|
|
2435
|
+
if (options.projectDir && options.projectDir !== originalCwd) {
|
|
2436
|
+
try {
|
|
2437
|
+
process.chdir(options.projectDir);
|
|
2438
|
+
log(`Changed working directory to: ${options.projectDir}`, "debug");
|
|
2439
|
+
} catch (error) {
|
|
2440
|
+
throw new Error(
|
|
2441
|
+
`Failed to change to directory "${options.projectDir}": ${error instanceof Error ? error.message : String(error)}`
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2425
2445
|
const config = await loadConfig(options.config);
|
|
2426
2446
|
log(`Loaded configuration from ${options.config || "version.config.json"}`, "info");
|
|
2427
2447
|
if (options.dryRun) config.dryRun = true;
|
|
@@ -180,4 +180,5 @@ Each CI system might have slightly different syntax, so check your CI provider's
|
|
|
180
180
|
2. **Use the `fetch-depth: 0`** option in GitHub Actions (or equivalent in other CIs) to ensure access to the full Git history
|
|
181
181
|
3. **Store the JSON output** as a build artifact for debugging and auditing
|
|
182
182
|
4. **Consider dry runs** in your preview/staging branches to validate version changes before they're applied
|
|
183
|
-
5. **
|
|
183
|
+
5. **Use `--project-dir`** when running from a different directory than your project root
|
|
184
|
+
6. **Be mindful of Git credentials** - ensure your CI has proper permissions for creating commits and tags
|
package/docs/changelogs.md
CHANGED
|
@@ -52,7 +52,14 @@ You can configure the preferred format in your `version.config.json`:
|
|
|
52
52
|
For projects with existing history, you can regenerate a complete changelog from scratch using the CLI:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
+
# Generate changelog in current directory
|
|
55
56
|
npx package-versioner changelog --regenerate
|
|
57
|
+
|
|
58
|
+
# Generate changelog in a specific directory
|
|
59
|
+
npx package-versioner changelog --regenerate --project-dir /path/to/project
|
|
60
|
+
|
|
61
|
+
# Customize output path and format
|
|
62
|
+
npx package-versioner changelog --regenerate --output CHANGELOG.md --format keep-a-changelog
|
|
56
63
|
```
|
|
57
64
|
|
|
58
65
|
This will scan your entire git history and create a comprehensive changelog based on all version tags found in your repository.
|
package/docs/versioning.md
CHANGED
|
@@ -65,78 +65,6 @@ You define patterns in the `branchPattern` array in `version.config.json`. Each
|
|
|
65
65
|
|
|
66
66
|
This allows you to enforce version bumps based on your branching workflow (e.g., all branches starting with `feature/` result in a minor bump).
|
|
67
67
|
|
|
68
|
-
## Monorepo Versioning Modes
|
|
69
|
-
|
|
70
|
-
While primarily used for single packages now, `package-versioner` retains options for monorepo workflows, controlled mainly by the `synced` flag in `version.config.json`.
|
|
71
|
-
|
|
72
|
-
### Synced Mode (`synced: true`)
|
|
73
|
-
|
|
74
|
-
This is the default if the `synced` flag is present and true.
|
|
75
|
-
|
|
76
|
-
- **Behaviour:** The tool calculates **one** version bump based on the overall history (or branch pattern). This single new version is applied to **all** packages within the repository (or just the root `package.json` if not a structured monorepo). A single Git tag is created.
|
|
77
|
-
- **Tag Behaviour:**
|
|
78
|
-
- In **multi-package monorepos**: Creates global tags like `v1.2.3` regardless of `packageSpecificTags` setting
|
|
79
|
-
- In **single-package repositories**: Respects the `packageSpecificTags` setting - can create either `v1.2.3` or `package-name@v1.2.3`
|
|
80
|
-
- **Use Case:** Suitable for monorepos where all packages are tightly coupled and released together with the same version number. Also the effective mode for single-package repositories.
|
|
81
|
-
|
|
82
|
-
### Async Mode (`synced: false`)
|
|
83
|
-
|
|
84
|
-
*(Note: This mode relies heavily on monorepo tooling and structure, like `pnpm workspaces` and correctly configured package dependencies.)*
|
|
85
|
-
|
|
86
|
-
- **Behaviour (Default - No `-t` flag):** The tool analyzes commits to determine which specific packages within the monorepo have changed since the last relevant commit/tag.
|
|
87
|
-
- It calculates an appropriate version bump **independently for each changed package** based on the commits affecting that package.
|
|
88
|
-
- Only the `package.json` files of the changed packages are updated.
|
|
89
|
-
- A **single commit** is created grouping all the version bumps, using the commit message template. **No Git tags are created** in this mode.
|
|
90
|
-
- **Use Case:** Suitable for monorepos where packages are versioned independently, but a single commit represents the batch of updates for traceability.
|
|
91
|
-
|
|
92
|
-
- **Behaviour (Targeted - With `-t` flag):** When using the `-t, --target <targets>` flag:
|
|
93
|
-
- Only the specified packages (respecting the `skip` list) are considered for versioning.
|
|
94
|
-
- It calculates an appropriate version bump **independently for each targeted package** based on its commit history.
|
|
95
|
-
- The `package.json` file of each successfully updated targeted package is modified.
|
|
96
|
-
- An **individual Git tag** (e.g., `packageName@1.2.3`) is created **for each successfully updated package** immediately after its version is bumped.
|
|
97
|
-
- Finally, a **single commit** is created including all the updated `package.json` files, using a summary commit message (e.g., `chore(release): pkg-a, pkg-b 1.2.3 [skip-ci]`).
|
|
98
|
-
- **Important:** Only package-specific tags are created. The global tag (e.g., `v1.2.3`) is **not** automatically generated in this mode. If your release process (like GitHub Releases) depends on a global tag, you'll need to create it manually in your CI/CD script *after* `package-versioner` completes.
|
|
99
|
-
- **Use Case:** Releasing specific packages independently while still tagging each released package individually.
|
|
100
|
-
|
|
101
|
-
## Prerelease Handling
|
|
102
|
-
|
|
103
|
-
`package-versioner` provides flexible handling for prerelease versions, allowing both creation of prereleases and promotion to stable releases.
|
|
104
|
-
|
|
105
|
-
### Creating Prereleases
|
|
106
|
-
|
|
107
|
-
Use the `--prerelease` flag with an identifier to create a prerelease version:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
# Create a beta prerelease
|
|
111
|
-
npx package-versioner --bump minor --prerelease beta
|
|
112
|
-
# Result: 1.0.0 -> 1.1.0-beta.0
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
You can also set a default prerelease identifier in your `version.config.json`:
|
|
116
|
-
|
|
117
|
-
```json
|
|
118
|
-
{
|
|
119
|
-
"prereleaseIdentifier": "beta"
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
### Promoting Prereleases to Stable Releases
|
|
124
|
-
|
|
125
|
-
When using standard bump types (`major`, `minor`, `patch`) with the `--bump` flag on a prerelease version, `package-versioner` will automatically clean the prerelease identifier:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
# Starting from version 1.0.0-beta.1
|
|
129
|
-
npx package-versioner --bump major
|
|
130
|
-
# Result: 1.0.0-beta.1 -> 2.0.0 (not 2.0.0-beta.0)
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
This intuitive behaviour means you don't need to use an empty prerelease identifier (`--prerelease ""`) to promote a prerelease to a stable version. Simply specify the standard bump type and the tool will automatically produce a clean version number.
|
|
134
|
-
|
|
135
|
-
This applies to all standard bump types:
|
|
136
|
-
- `--bump major`: 1.0.0-beta.1 -> 2.0.0
|
|
137
|
-
- `--bump minor`: 1.0.0-beta.1 -> 1.1.0
|
|
138
|
-
- `--bump patch`: 1.0.0-beta.1 -> 1.0.1
|
|
139
|
-
|
|
140
68
|
## Package Type Support
|
|
141
69
|
|
|
142
70
|
`package-versioner` supports both JavaScript/TypeScript projects using `package.json` and Rust projects using `Cargo.toml`:
|
|
@@ -149,11 +77,6 @@ For JavaScript/TypeScript projects, the tool looks for and updates the `version`
|
|
|
149
77
|
|
|
150
78
|
For Rust projects, the tool looks for and updates the `package.version` field in `Cargo.toml` files using the same versioning strategies.
|
|
151
79
|
|
|
152
|
-
When no tags are found for a project, `package-versioner` will:
|
|
153
|
-
1. Look for the `version` in `package.json` if it exists
|
|
154
|
-
2. Look for the `package.version` in `Cargo.toml` if it exists
|
|
155
|
-
3. Fall back to the configured `initialVersion` (default: "0.1.0")
|
|
156
|
-
|
|
157
80
|
### Mixed Projects with Both Manifests
|
|
158
81
|
|
|
159
82
|
When both `package.json` and `Cargo.toml` exist in the same directory, `package-versioner` will:
|
|
@@ -164,7 +87,48 @@ When both `package.json` and `Cargo.toml` exist in the same directory, `package-
|
|
|
164
87
|
|
|
165
88
|
This allows you to maintain consistent versioning across JavaScript and Rust components in the same package.
|
|
166
89
|
|
|
167
|
-
|
|
90
|
+
## Version Source Selection
|
|
91
|
+
|
|
92
|
+
`package-versioner` uses a smart version source selection strategy to determine the base version for calculating the next version:
|
|
93
|
+
|
|
94
|
+
1. First, it checks for Git tags:
|
|
95
|
+
- In normal mode: Uses the latest reachable tag, falling back to unreachable tags if needed
|
|
96
|
+
- In strict mode (`--strict-reachable`): Only uses reachable tags
|
|
97
|
+
|
|
98
|
+
2. Then, it checks manifest files (package.json, Cargo.toml):
|
|
99
|
+
- Reads version from package.json if it exists
|
|
100
|
+
- Falls back to Cargo.toml if package.json doesn't exist or has no version
|
|
101
|
+
|
|
102
|
+
3. Finally, it compares the versions:
|
|
103
|
+
- If both Git tag and manifest versions exist, it uses the newer version
|
|
104
|
+
- If the versions are equal, it prefers the Git tag for better history tracking
|
|
105
|
+
- If only one source has a version, it uses that
|
|
106
|
+
- If no version is found, it uses the default initial version (0.1.0)
|
|
107
|
+
|
|
108
|
+
This strategy ensures that:
|
|
109
|
+
- Version numbers never go backwards
|
|
110
|
+
- Git history is respected when possible
|
|
111
|
+
- Manifest files are considered as valid version sources
|
|
112
|
+
- The tool always has a valid base version to work from
|
|
113
|
+
|
|
114
|
+
For example:
|
|
115
|
+
```
|
|
116
|
+
Scenario 1:
|
|
117
|
+
- Git tag: v1.0.0
|
|
118
|
+
- package.json: 1.1.0
|
|
119
|
+
Result: Uses 1.1.0 as base (package.json is newer)
|
|
120
|
+
|
|
121
|
+
Scenario 2:
|
|
122
|
+
- Git tag: v1.0.0
|
|
123
|
+
- package.json: 1.0.0
|
|
124
|
+
Result: Uses v1.0.0 as base (versions equal, prefer Git)
|
|
125
|
+
|
|
126
|
+
Scenario 3:
|
|
127
|
+
- Git tag: unreachable v2.0.0
|
|
128
|
+
- package.json: 1.0.0
|
|
129
|
+
Result: Uses 2.0.0 as base in normal mode (unreachable tag is newer)
|
|
130
|
+
Uses 1.0.0 as base in strict mode (unreachable tag ignored)
|
|
131
|
+
```
|
|
168
132
|
|
|
169
133
|
## Package Targeting in Monorepos
|
|
170
134
|
|
|
@@ -430,3 +394,75 @@ For global commit messages, use templates without `${packageName}`:
|
|
|
430
394
|
"commitMessage": "chore: release ${version}"
|
|
431
395
|
}
|
|
432
396
|
```
|
|
397
|
+
|
|
398
|
+
## Monorepo Versioning Modes
|
|
399
|
+
|
|
400
|
+
While primarily used for single packages now, `package-versioner` retains options for monorepo workflows, controlled mainly by the `synced` flag in `version.config.json`.
|
|
401
|
+
|
|
402
|
+
### Synced Mode (`synced: true`)
|
|
403
|
+
|
|
404
|
+
This is the default if the `synced` flag is present and true.
|
|
405
|
+
|
|
406
|
+
- **Behaviour:** The tool calculates **one** version bump based on the overall history (or branch pattern). This single new version is applied to **all** packages within the repository (or just the root `package.json` if not a structured monorepo). A single Git tag is created.
|
|
407
|
+
- **Tag Behaviour:**
|
|
408
|
+
- In **multi-package monorepos**: Creates global tags like `v1.2.3` regardless of `packageSpecificTags` setting
|
|
409
|
+
- In **single-package repositories**: Respects the `packageSpecificTags` setting - can create either `v1.2.3` or `package-name@v1.2.3`
|
|
410
|
+
- **Use Case:** Suitable for monorepos where all packages are tightly coupled and released together with the same version number. Also the effective mode for single-package repositories.
|
|
411
|
+
|
|
412
|
+
### Async Mode (`synced: false`)
|
|
413
|
+
|
|
414
|
+
*(Note: This mode relies heavily on monorepo tooling and structure, like `pnpm workspaces` and correctly configured package dependencies.)*
|
|
415
|
+
|
|
416
|
+
- **Behaviour (Default - No `-t` flag):** The tool analyzes commits to determine which specific packages within the monorepo have changed since the last relevant commit/tag.
|
|
417
|
+
- It calculates an appropriate version bump **independently for each changed package** based on the commits affecting that package.
|
|
418
|
+
- Only the `package.json` files of the changed packages are updated.
|
|
419
|
+
- A **single commit** is created grouping all the version bumps, using the commit message template. **No Git tags are created** in this mode.
|
|
420
|
+
- **Use Case:** Suitable for monorepos where packages are versioned independently, but a single commit represents the batch of updates for traceability.
|
|
421
|
+
|
|
422
|
+
- **Behaviour (Targeted - With `-t` flag):** When using the `-t, --target <targets>` flag:
|
|
423
|
+
- Only the specified packages (respecting the `skip` list) are considered for versioning.
|
|
424
|
+
- It calculates an appropriate version bump **independently for each targeted package** based on its commit history.
|
|
425
|
+
- The `package.json` file of each successfully updated targeted package is modified.
|
|
426
|
+
- An **individual Git tag** (e.g., `packageName@1.2.3`) is created **for each successfully updated package** immediately after its version is bumped.
|
|
427
|
+
- Finally, a **single commit** is created including all the updated `package.json` files, using a summary commit message (e.g., `chore(release): pkg-a, pkg-b 1.2.3 [skip-ci]`).
|
|
428
|
+
- **Important:** Only package-specific tags are created. The global tag (e.g., `v1.2.3`) is **not** automatically generated in this mode. If your release process (like GitHub Releases) depends on a global tag, you'll need to create it manually in your CI/CD script *after* `package-versioner` completes.
|
|
429
|
+
- **Use Case:** Releasing specific packages independently while still tagging each released package individually.
|
|
430
|
+
|
|
431
|
+
## Prerelease Handling
|
|
432
|
+
|
|
433
|
+
`package-versioner` provides flexible handling for prerelease versions, allowing both creation of prereleases and promotion to stable releases.
|
|
434
|
+
|
|
435
|
+
### Creating Prereleases
|
|
436
|
+
|
|
437
|
+
Use the `--prerelease` flag with an identifier to create a prerelease version:
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Create a beta prerelease
|
|
441
|
+
npx package-versioner --bump minor --prerelease beta
|
|
442
|
+
# Result: 1.0.0 -> 1.1.0-beta.0
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
You can also set a default prerelease identifier in your `version.config.json`:
|
|
446
|
+
|
|
447
|
+
```json
|
|
448
|
+
{
|
|
449
|
+
"prereleaseIdentifier": "beta"
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Promoting Prereleases to Stable Releases
|
|
454
|
+
|
|
455
|
+
When using standard bump types (`major`, `minor`, `patch`) with the `--bump` flag on a prerelease version, `package-versioner` will automatically clean the prerelease identifier:
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# Starting from version 1.0.0-beta.1
|
|
459
|
+
npx package-versioner --bump major
|
|
460
|
+
# Result: 1.0.0-beta.1 -> 2.0.0 (not 2.0.0-beta.0)
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
This intuitive behaviour means you don't need to use an empty prerelease identifier (`--prerelease ""`) to promote a prerelease to a stable version. Simply specify the standard bump type and the tool will automatically produce a clean version number.
|
|
464
|
+
|
|
465
|
+
This applies to all standard bump types:
|
|
466
|
+
- `--bump major`: 1.0.0-beta.1 -> 2.0.0
|
|
467
|
+
- `--bump minor`: 1.0.0-beta.1 -> 1.1.0
|
|
468
|
+
- `--bump patch`: 1.0.0-beta.1 -> 1.0.1
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"minLength": 1
|
|
54
54
|
},
|
|
55
55
|
"default": [],
|
|
56
|
-
"description": "Array of package names or patterns that determines which packages will be processed for versioning. When specified, only packages matching these patterns will be versioned. When empty or not specified, all workspace packages will be processed. Supports exact names (e.g., '@scope/package-a'), scope wildcards (e.g., '@scope/*'), and global wildcards (e.g., '*')"
|
|
56
|
+
"description": "Array of package names or patterns that determines which packages will be processed for versioning. When specified, only packages matching these patterns will be versioned. When empty or not specified, all workspace packages will be processed. Supports exact names (e.g., '@scope/package-a'), scope wildcards (e.g., '@scope/*'), path patterns (e.g., 'packages/**/*', 'examples/**'), and global wildcards (e.g., '*')."
|
|
57
57
|
},
|
|
58
58
|
"mainPackage": {
|
|
59
59
|
"type": "string",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "package-versioner",
|
|
3
3
|
"description": "A lightweight yet powerful CLI tool for automated semantic versioning based on Git history and conventional commits.",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|