package-versioner 0.7.2 → 0.8.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/README.md +25 -0
- package/dist/index.cjs +123 -34
- package/dist/index.js +123 -34
- package/package-versioner.schema.json +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,9 @@ npx package-versioner -t @scope/package-a,@scope/package-b
|
|
|
58
58
|
# Perform a dry run: calculates version, logs actions, but makes no file changes or Git commits/tags
|
|
59
59
|
npx package-versioner --dry-run
|
|
60
60
|
|
|
61
|
+
# Only use reachable tags (Git-semantic mode, no fallback to unreachable tags)
|
|
62
|
+
npx package-versioner --strict-reachable
|
|
63
|
+
|
|
61
64
|
# Output results as JSON (useful for CI/CD scripts)
|
|
62
65
|
npx package-versioner --json
|
|
63
66
|
|
|
@@ -67,6 +70,26 @@ npx package-versioner --dry-run --json
|
|
|
67
70
|
|
|
68
71
|
**Note on Targeting:** Using the `-t` flag creates package-specific tags (e.g., `@scope/package-a@1.2.0`) but *not* a global tag (like `v1.2.0`). If needed, create the global tag manually in your CI/CD script after this command.
|
|
69
72
|
|
|
73
|
+
### Git Tag Reachability
|
|
74
|
+
|
|
75
|
+
By default, `package-versioner` intelligently handles Git tag reachability to provide the best user experience:
|
|
76
|
+
|
|
77
|
+
- **Default behavior**: Uses reachable tags when available, but falls back to the latest repository tag if needed (common in feature branches)
|
|
78
|
+
- **Strict mode (`--strict-reachable`)**: Only uses tags reachable from the current commit, following strict Git semantics
|
|
79
|
+
|
|
80
|
+
This is particularly useful when working on feature branches that have diverged from the main branch where newer tags exist. The tool will automatically detect the Git context and provide helpful guidance:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# On a feature branch with unreachable tags
|
|
84
|
+
npx package-versioner --dry-run
|
|
85
|
+
# Output: "No tags reachable from current branch 'feature-x'. Using latest repository tag v1.2.3 as version base."
|
|
86
|
+
# Tip: Consider 'git merge main' or 'git rebase main' to include tag history in your branch.
|
|
87
|
+
|
|
88
|
+
# Force strict Git semantics
|
|
89
|
+
npx package-versioner --dry-run --strict-reachable
|
|
90
|
+
# Output: Uses only reachable tags, may result in "No reachable tags found"
|
|
91
|
+
```
|
|
92
|
+
|
|
70
93
|
## JSON Output
|
|
71
94
|
|
|
72
95
|
When using the `--json` flag, normal console output is suppressed and the tool outputs a structured JSON object that includes information about the versioning operation.
|
|
@@ -103,6 +126,7 @@ Customize behaviour by creating a `version.config.json` file in your project roo
|
|
|
103
126
|
"commitMessage": "chore: release ${packageName}@${version} [skip ci]",
|
|
104
127
|
"updateChangelog": true,
|
|
105
128
|
"changelogFormat": "keep-a-changelog",
|
|
129
|
+
"strictReachable": false,
|
|
106
130
|
"synced": true,
|
|
107
131
|
"skip": [
|
|
108
132
|
"docs",
|
|
@@ -126,6 +150,7 @@ Customize behaviour by creating a `version.config.json` file in your project roo
|
|
|
126
150
|
- `commitMessage`: Template for commit messages (default: "chore(release): ${version}")
|
|
127
151
|
- `updateChangelog`: Whether to automatically update changelogs (default: true)
|
|
128
152
|
- `changelogFormat`: Format for changelogs - "keep-a-changelog" or "angular" (default: "keep-a-changelog")
|
|
153
|
+
- `strictReachable`: Only use reachable tags, no fallback to unreachable tags (default: false)
|
|
129
154
|
- `cargo`: Options for Rust projects:
|
|
130
155
|
- `enabled`: Whether to handle Cargo.toml files (default: true)
|
|
131
156
|
- `paths`: Directories to search for Cargo.toml files (optional)
|
package/dist/index.cjs
CHANGED
|
@@ -799,6 +799,7 @@ async function createGitCommitAndTag(files, nextTag, commitMessage, skipHooks, d
|
|
|
799
799
|
|
|
800
800
|
// src/git/tagsAndBranches.ts
|
|
801
801
|
var import_git_semver_tags = require("git-semver-tags");
|
|
802
|
+
var import_semver = __toESM(require("semver"), 1);
|
|
802
803
|
|
|
803
804
|
// src/utils/formatting.ts
|
|
804
805
|
function escapeRegExp(string) {
|
|
@@ -854,10 +855,29 @@ function getCommitsLength(pkgRoot) {
|
|
|
854
855
|
return 0;
|
|
855
856
|
}
|
|
856
857
|
}
|
|
857
|
-
async function getLatestTag() {
|
|
858
|
+
async function getLatestTag(versionPrefix) {
|
|
858
859
|
try {
|
|
859
|
-
const tags = await (0, import_git_semver_tags.getSemverTags)({
|
|
860
|
-
|
|
860
|
+
const tags = await (0, import_git_semver_tags.getSemverTags)({
|
|
861
|
+
tagPrefix: versionPrefix
|
|
862
|
+
});
|
|
863
|
+
if (tags.length === 0) {
|
|
864
|
+
return "";
|
|
865
|
+
}
|
|
866
|
+
const chronologicalLatest = tags[0];
|
|
867
|
+
const sortedTags = [...tags].sort((a, b) => {
|
|
868
|
+
const versionA = import_semver.default.clean(a) || "0.0.0";
|
|
869
|
+
const versionB = import_semver.default.clean(b) || "0.0.0";
|
|
870
|
+
return import_semver.default.rcompare(versionA, versionB);
|
|
871
|
+
});
|
|
872
|
+
const semanticLatest = sortedTags[0];
|
|
873
|
+
if (semanticLatest !== chronologicalLatest) {
|
|
874
|
+
log(
|
|
875
|
+
`Tag ordering differs: chronological latest is ${chronologicalLatest}, semantic latest is ${semanticLatest}`,
|
|
876
|
+
"debug"
|
|
877
|
+
);
|
|
878
|
+
log(`Using semantic latest (${semanticLatest}) to handle out-of-order tag creation`, "info");
|
|
879
|
+
}
|
|
880
|
+
return semanticLatest;
|
|
861
881
|
} catch (error) {
|
|
862
882
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
863
883
|
log(`Failed to get latest tag: ${errorMessage}`, "error");
|
|
@@ -902,37 +922,82 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
|
902
922
|
const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
|
|
903
923
|
let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
|
|
904
924
|
if (packageTags.length > 0) {
|
|
925
|
+
const chronologicalFirst = packageTags[0];
|
|
926
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
927
|
+
let versionA = "";
|
|
928
|
+
let versionB = "";
|
|
929
|
+
if (a.includes("@")) {
|
|
930
|
+
const afterAt = a.split("@")[1] || "";
|
|
931
|
+
versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
932
|
+
} else {
|
|
933
|
+
versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
934
|
+
}
|
|
935
|
+
if (b.includes("@")) {
|
|
936
|
+
const afterAtB = b.split("@")[1] || "";
|
|
937
|
+
versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
938
|
+
} else {
|
|
939
|
+
versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
940
|
+
}
|
|
941
|
+
const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
|
|
942
|
+
const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
|
|
943
|
+
return import_semver.default.rcompare(cleanVersionA, cleanVersionB);
|
|
944
|
+
});
|
|
905
945
|
log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
|
|
906
|
-
log(`Using tag: ${
|
|
907
|
-
|
|
946
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
947
|
+
if (sortedPackageTags2[0] !== chronologicalFirst) {
|
|
948
|
+
log(
|
|
949
|
+
`Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
|
|
950
|
+
"debug"
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
return sortedPackageTags2[0];
|
|
908
954
|
}
|
|
909
955
|
if (versionPrefix) {
|
|
910
956
|
const pattern1 = new RegExp(`^${escapedPackageName}@${escapeRegExp(versionPrefix)}`);
|
|
911
957
|
packageTags = allTags.filter((tag) => pattern1.test(tag));
|
|
912
958
|
if (packageTags.length > 0) {
|
|
959
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
960
|
+
const afterAt = a.split("@")[1] || "";
|
|
961
|
+
const versionA = afterAt.replace(
|
|
962
|
+
new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
|
|
963
|
+
""
|
|
964
|
+
);
|
|
965
|
+
const afterAtB = b.split("@")[1] || "";
|
|
966
|
+
const versionB = afterAtB.replace(
|
|
967
|
+
new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
|
|
968
|
+
""
|
|
969
|
+
);
|
|
970
|
+
const cleanVersionA = import_semver.default.clean(versionA) || "0.0.0";
|
|
971
|
+
const cleanVersionB = import_semver.default.clean(versionB) || "0.0.0";
|
|
972
|
+
return import_semver.default.rcompare(cleanVersionA, cleanVersionB);
|
|
973
|
+
});
|
|
913
974
|
log(
|
|
914
975
|
`Found ${packageTags.length} package tags using pattern: packageName@${versionPrefix}...`,
|
|
915
976
|
"debug"
|
|
916
977
|
);
|
|
917
|
-
log(`Using tag: ${
|
|
918
|
-
return
|
|
978
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
979
|
+
return sortedPackageTags2[0];
|
|
919
980
|
}
|
|
920
981
|
}
|
|
921
982
|
if (versionPrefix) {
|
|
922
983
|
const pattern2 = new RegExp(`^${escapeRegExp(versionPrefix)}${escapedPackageName}@`);
|
|
923
984
|
packageTags = allTags.filter((tag) => pattern2.test(tag));
|
|
924
985
|
if (packageTags.length > 0) {
|
|
986
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
987
|
+
const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
|
|
988
|
+
const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
|
|
989
|
+
return import_semver.default.rcompare(versionA, versionB);
|
|
990
|
+
});
|
|
925
991
|
log(
|
|
926
992
|
`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`,
|
|
927
993
|
"debug"
|
|
928
994
|
);
|
|
929
|
-
log(`Using tag: ${
|
|
930
|
-
return
|
|
995
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
996
|
+
return sortedPackageTags2[0];
|
|
931
997
|
}
|
|
932
998
|
}
|
|
933
999
|
const pattern3 = new RegExp(`^${escapedPackageName}@`);
|
|
934
1000
|
packageTags = allTags.filter((tag) => pattern3.test(tag));
|
|
935
|
-
log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
|
|
936
1001
|
if (packageTags.length === 0) {
|
|
937
1002
|
log("No matching tags found for pattern: packageName@version", "debug");
|
|
938
1003
|
if (allTags.length > 0) {
|
|
@@ -940,10 +1005,16 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
|
940
1005
|
} else {
|
|
941
1006
|
log("No tags available in the repository", "debug");
|
|
942
1007
|
}
|
|
943
|
-
|
|
944
|
-
log(`Using tag: ${packageTags[0]}`, "debug");
|
|
1008
|
+
return "";
|
|
945
1009
|
}
|
|
946
|
-
|
|
1010
|
+
const sortedPackageTags = [...packageTags].sort((a, b) => {
|
|
1011
|
+
const versionA = import_semver.default.clean(a.split("@")[1] || "") || "0.0.0";
|
|
1012
|
+
const versionB = import_semver.default.clean(b.split("@")[1] || "") || "0.0.0";
|
|
1013
|
+
return import_semver.default.rcompare(versionA, versionB);
|
|
1014
|
+
});
|
|
1015
|
+
log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
|
|
1016
|
+
log(`Using semantically latest tag: ${sortedPackageTags[0]}`, "debug");
|
|
1017
|
+
return sortedPackageTags[0];
|
|
947
1018
|
}
|
|
948
1019
|
log(`Package-specific tags disabled for ${packageName}, falling back to global tags`, "debug");
|
|
949
1020
|
return "";
|
|
@@ -1049,6 +1120,7 @@ function updatePackageVersion(packagePath, version) {
|
|
|
1049
1120
|
}
|
|
1050
1121
|
|
|
1051
1122
|
// src/package/packageProcessor.ts
|
|
1123
|
+
var import_node_child_process4 = require("child_process");
|
|
1052
1124
|
var fs8 = __toESM(require("fs"), 1);
|
|
1053
1125
|
var import_node_path6 = __toESM(require("path"), 1);
|
|
1054
1126
|
var import_node_process4 = require("process");
|
|
@@ -1381,7 +1453,7 @@ function capitalizeFirstLetter(input) {
|
|
|
1381
1453
|
// src/core/versionCalculator.ts
|
|
1382
1454
|
var import_node_process3 = require("process");
|
|
1383
1455
|
var import_conventional_recommended_bump = require("conventional-recommended-bump");
|
|
1384
|
-
var
|
|
1456
|
+
var import_semver3 = __toESM(require("semver"), 1);
|
|
1385
1457
|
|
|
1386
1458
|
// src/utils/manifestHelpers.ts
|
|
1387
1459
|
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
@@ -1442,7 +1514,7 @@ function throwIfNoManifestsFound(packageDir) {
|
|
|
1442
1514
|
|
|
1443
1515
|
// src/utils/versionUtils.ts
|
|
1444
1516
|
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
1445
|
-
var
|
|
1517
|
+
var import_semver2 = __toESM(require("semver"), 1);
|
|
1446
1518
|
var TOML2 = __toESM(require("smol-toml"), 1);
|
|
1447
1519
|
var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
|
|
1448
1520
|
function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
@@ -1455,27 +1527,27 @@ function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
|
1455
1527
|
return void 0;
|
|
1456
1528
|
}
|
|
1457
1529
|
function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
|
|
1458
|
-
if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !
|
|
1530
|
+
if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !import_semver2.default.prerelease(currentVersion)) {
|
|
1459
1531
|
const preBumpType = `pre${bumpType}`;
|
|
1460
1532
|
log(
|
|
1461
1533
|
`Creating prerelease version with identifier '${prereleaseIdentifier}' using ${preBumpType}`,
|
|
1462
1534
|
"debug"
|
|
1463
1535
|
);
|
|
1464
|
-
return
|
|
1536
|
+
return import_semver2.default.inc(currentVersion, preBumpType, prereleaseIdentifier) || "";
|
|
1465
1537
|
}
|
|
1466
|
-
if (
|
|
1467
|
-
const parsed =
|
|
1538
|
+
if (import_semver2.default.prerelease(currentVersion) && STANDARD_BUMP_TYPES.includes(bumpType)) {
|
|
1539
|
+
const parsed = import_semver2.default.parse(currentVersion);
|
|
1468
1540
|
if (!parsed) {
|
|
1469
|
-
return
|
|
1541
|
+
return import_semver2.default.inc(currentVersion, bumpType) || "";
|
|
1470
1542
|
}
|
|
1471
1543
|
if (bumpType === "major" && parsed.minor === 0 && parsed.patch === 0 || bumpType === "minor" && parsed.patch === 0 || bumpType === "patch") {
|
|
1472
1544
|
log(`Cleaning prerelease identifier from ${currentVersion} for ${bumpType} bump`, "debug");
|
|
1473
1545
|
return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
1474
1546
|
}
|
|
1475
1547
|
log(`Standard increment for ${currentVersion} with ${bumpType} bump`, "debug");
|
|
1476
|
-
return
|
|
1548
|
+
return import_semver2.default.inc(currentVersion, bumpType) || "";
|
|
1477
1549
|
}
|
|
1478
|
-
return
|
|
1550
|
+
return import_semver2.default.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
|
|
1479
1551
|
}
|
|
1480
1552
|
|
|
1481
1553
|
// src/core/versionCalculator.ts
|
|
@@ -1517,10 +1589,10 @@ async function calculateVersion(config, options) {
|
|
|
1517
1589
|
const packageDir = pkgPath || (0, import_node_process3.cwd)();
|
|
1518
1590
|
const manifestResult = getVersionFromManifests(packageDir);
|
|
1519
1591
|
if (manifestResult.manifestFound && manifestResult.version) {
|
|
1520
|
-
const cleanedTag =
|
|
1521
|
-
const tagVersion =
|
|
1592
|
+
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1593
|
+
const tagVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1522
1594
|
const packageVersion = manifestResult.version;
|
|
1523
|
-
if (
|
|
1595
|
+
if (import_semver3.default.gt(packageVersion, tagVersion)) {
|
|
1524
1596
|
log(
|
|
1525
1597
|
`Warning: Version mismatch detected!
|
|
1526
1598
|
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
@@ -1540,7 +1612,7 @@ To fix this mismatch:
|
|
|
1540
1612
|
\u2022 Or use package version as base by ensuring tags are up to date`,
|
|
1541
1613
|
"warning"
|
|
1542
1614
|
);
|
|
1543
|
-
} else if (
|
|
1615
|
+
} else if (import_semver3.default.gt(tagVersion, packageVersion)) {
|
|
1544
1616
|
log(
|
|
1545
1617
|
`Warning: Version mismatch detected!
|
|
1546
1618
|
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
@@ -1576,9 +1648,9 @@ To fix this mismatch:
|
|
|
1576
1648
|
initialVersion
|
|
1577
1649
|
);
|
|
1578
1650
|
}
|
|
1579
|
-
const cleanedTag =
|
|
1580
|
-
const currentVersion =
|
|
1581
|
-
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (
|
|
1651
|
+
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1652
|
+
const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1653
|
+
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (import_semver3.default.prerelease(currentVersion) || normalizedPrereleaseId)) {
|
|
1582
1654
|
log(
|
|
1583
1655
|
normalizedPrereleaseId ? `Creating prerelease version with identifier '${normalizedPrereleaseId}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
|
|
1584
1656
|
"debug"
|
|
@@ -1616,8 +1688,8 @@ To fix this mismatch:
|
|
|
1616
1688
|
initialVersion
|
|
1617
1689
|
);
|
|
1618
1690
|
}
|
|
1619
|
-
const cleanedTag =
|
|
1620
|
-
const currentVersion =
|
|
1691
|
+
const cleanedTag = import_semver3.default.clean(latestTag) || latestTag;
|
|
1692
|
+
const currentVersion = import_semver3.default.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1621
1693
|
log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
|
|
1622
1694
|
return bumpVersion(currentVersion, branchVersionType, normalizedPrereleaseId);
|
|
1623
1695
|
}
|
|
@@ -1655,7 +1727,7 @@ To fix this mismatch:
|
|
|
1655
1727
|
);
|
|
1656
1728
|
return "";
|
|
1657
1729
|
}
|
|
1658
|
-
const currentVersion =
|
|
1730
|
+
const currentVersion = import_semver3.default.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1659
1731
|
return bumpVersion(currentVersion, releaseTypeFromCommits, normalizedPrereleaseId);
|
|
1660
1732
|
} catch (error) {
|
|
1661
1733
|
log(`Failed to calculate version for ${name || "project"}`, "error");
|
|
@@ -1700,7 +1772,7 @@ function calculateNextVersion(version, manifestType, name, releaseType, prerelea
|
|
|
1700
1772
|
`No tags found for ${name || "package"}, using ${manifestType} version: ${version} as base`,
|
|
1701
1773
|
"info"
|
|
1702
1774
|
);
|
|
1703
|
-
if (STANDARD_BUMP_TYPES.includes(releaseType) && (
|
|
1775
|
+
if (STANDARD_BUMP_TYPES.includes(releaseType) && (import_semver3.default.prerelease(version) || prereleaseIdentifier)) {
|
|
1704
1776
|
log(
|
|
1705
1777
|
prereleaseIdentifier ? `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${releaseType}` : `Cleaning prerelease identifier from ${version} for ${releaseType} bump`,
|
|
1706
1778
|
"debug"
|
|
@@ -1861,7 +1933,24 @@ var PackageProcessor = class {
|
|
|
1861
1933
|
if (this.fullConfig.updateChangelog !== false) {
|
|
1862
1934
|
let changelogEntries = [];
|
|
1863
1935
|
try {
|
|
1864
|
-
|
|
1936
|
+
let revisionRange = latestTag;
|
|
1937
|
+
if (latestTag) {
|
|
1938
|
+
try {
|
|
1939
|
+
(0, import_node_child_process4.execSync)(`git rev-parse --verify "${latestTag}"`, {
|
|
1940
|
+
cwd: pkgPath,
|
|
1941
|
+
stdio: "ignore"
|
|
1942
|
+
});
|
|
1943
|
+
} catch {
|
|
1944
|
+
log(
|
|
1945
|
+
`Tag ${latestTag} doesn't exist, using recent commits from HEAD for changelog`,
|
|
1946
|
+
"debug"
|
|
1947
|
+
);
|
|
1948
|
+
revisionRange = "HEAD~10..HEAD";
|
|
1949
|
+
}
|
|
1950
|
+
} else {
|
|
1951
|
+
revisionRange = "HEAD~10..HEAD";
|
|
1952
|
+
}
|
|
1953
|
+
changelogEntries = extractChangelogEntriesFromCommits(pkgPath, revisionRange);
|
|
1865
1954
|
if (changelogEntries.length === 0) {
|
|
1866
1955
|
changelogEntries = [
|
|
1867
1956
|
{
|
package/dist/index.js
CHANGED
|
@@ -766,6 +766,7 @@ async function createGitCommitAndTag(files, nextTag, commitMessage, skipHooks, d
|
|
|
766
766
|
|
|
767
767
|
// src/git/tagsAndBranches.ts
|
|
768
768
|
import { getSemverTags } from "git-semver-tags";
|
|
769
|
+
import semver from "semver";
|
|
769
770
|
|
|
770
771
|
// src/utils/formatting.ts
|
|
771
772
|
function escapeRegExp(string) {
|
|
@@ -821,10 +822,29 @@ function getCommitsLength(pkgRoot) {
|
|
|
821
822
|
return 0;
|
|
822
823
|
}
|
|
823
824
|
}
|
|
824
|
-
async function getLatestTag() {
|
|
825
|
+
async function getLatestTag(versionPrefix) {
|
|
825
826
|
try {
|
|
826
|
-
const tags = await getSemverTags({
|
|
827
|
-
|
|
827
|
+
const tags = await getSemverTags({
|
|
828
|
+
tagPrefix: versionPrefix
|
|
829
|
+
});
|
|
830
|
+
if (tags.length === 0) {
|
|
831
|
+
return "";
|
|
832
|
+
}
|
|
833
|
+
const chronologicalLatest = tags[0];
|
|
834
|
+
const sortedTags = [...tags].sort((a, b) => {
|
|
835
|
+
const versionA = semver.clean(a) || "0.0.0";
|
|
836
|
+
const versionB = semver.clean(b) || "0.0.0";
|
|
837
|
+
return semver.rcompare(versionA, versionB);
|
|
838
|
+
});
|
|
839
|
+
const semanticLatest = sortedTags[0];
|
|
840
|
+
if (semanticLatest !== chronologicalLatest) {
|
|
841
|
+
log(
|
|
842
|
+
`Tag ordering differs: chronological latest is ${chronologicalLatest}, semantic latest is ${semanticLatest}`,
|
|
843
|
+
"debug"
|
|
844
|
+
);
|
|
845
|
+
log(`Using semantic latest (${semanticLatest}) to handle out-of-order tag creation`, "info");
|
|
846
|
+
}
|
|
847
|
+
return semanticLatest;
|
|
828
848
|
} catch (error) {
|
|
829
849
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
830
850
|
log(`Failed to get latest tag: ${errorMessage}`, "error");
|
|
@@ -869,37 +889,82 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
|
869
889
|
const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
|
|
870
890
|
let packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
|
|
871
891
|
if (packageTags.length > 0) {
|
|
892
|
+
const chronologicalFirst = packageTags[0];
|
|
893
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
894
|
+
let versionA = "";
|
|
895
|
+
let versionB = "";
|
|
896
|
+
if (a.includes("@")) {
|
|
897
|
+
const afterAt = a.split("@")[1] || "";
|
|
898
|
+
versionA = afterAt.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
899
|
+
} else {
|
|
900
|
+
versionA = a.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
901
|
+
}
|
|
902
|
+
if (b.includes("@")) {
|
|
903
|
+
const afterAtB = b.split("@")[1] || "";
|
|
904
|
+
versionB = afterAtB.replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
905
|
+
} else {
|
|
906
|
+
versionB = b.replace(new RegExp(`^${escapeRegExp(packageName)}`), "").replace(new RegExp(`^${escapeRegExp(versionPrefix || "")}`), "");
|
|
907
|
+
}
|
|
908
|
+
const cleanVersionA = semver.clean(versionA) || "0.0.0";
|
|
909
|
+
const cleanVersionB = semver.clean(versionB) || "0.0.0";
|
|
910
|
+
return semver.rcompare(cleanVersionA, cleanVersionB);
|
|
911
|
+
});
|
|
872
912
|
log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
|
|
873
|
-
log(`Using tag: ${
|
|
874
|
-
|
|
913
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
914
|
+
if (sortedPackageTags2[0] !== chronologicalFirst) {
|
|
915
|
+
log(
|
|
916
|
+
`Package tag ordering differs: chronological first is ${chronologicalFirst}, semantic latest is ${sortedPackageTags2[0]}`,
|
|
917
|
+
"debug"
|
|
918
|
+
);
|
|
919
|
+
}
|
|
920
|
+
return sortedPackageTags2[0];
|
|
875
921
|
}
|
|
876
922
|
if (versionPrefix) {
|
|
877
923
|
const pattern1 = new RegExp(`^${escapedPackageName}@${escapeRegExp(versionPrefix)}`);
|
|
878
924
|
packageTags = allTags.filter((tag) => pattern1.test(tag));
|
|
879
925
|
if (packageTags.length > 0) {
|
|
926
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
927
|
+
const afterAt = a.split("@")[1] || "";
|
|
928
|
+
const versionA = afterAt.replace(
|
|
929
|
+
new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
|
|
930
|
+
""
|
|
931
|
+
);
|
|
932
|
+
const afterAtB = b.split("@")[1] || "";
|
|
933
|
+
const versionB = afterAtB.replace(
|
|
934
|
+
new RegExp(`^${escapeRegExp(versionPrefix || "")}`),
|
|
935
|
+
""
|
|
936
|
+
);
|
|
937
|
+
const cleanVersionA = semver.clean(versionA) || "0.0.0";
|
|
938
|
+
const cleanVersionB = semver.clean(versionB) || "0.0.0";
|
|
939
|
+
return semver.rcompare(cleanVersionA, cleanVersionB);
|
|
940
|
+
});
|
|
880
941
|
log(
|
|
881
942
|
`Found ${packageTags.length} package tags using pattern: packageName@${versionPrefix}...`,
|
|
882
943
|
"debug"
|
|
883
944
|
);
|
|
884
|
-
log(`Using tag: ${
|
|
885
|
-
return
|
|
945
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
946
|
+
return sortedPackageTags2[0];
|
|
886
947
|
}
|
|
887
948
|
}
|
|
888
949
|
if (versionPrefix) {
|
|
889
950
|
const pattern2 = new RegExp(`^${escapeRegExp(versionPrefix)}${escapedPackageName}@`);
|
|
890
951
|
packageTags = allTags.filter((tag) => pattern2.test(tag));
|
|
891
952
|
if (packageTags.length > 0) {
|
|
953
|
+
const sortedPackageTags2 = [...packageTags].sort((a, b) => {
|
|
954
|
+
const versionA = semver.clean(a.split("@")[1] || "") || "0.0.0";
|
|
955
|
+
const versionB = semver.clean(b.split("@")[1] || "") || "0.0.0";
|
|
956
|
+
return semver.rcompare(versionA, versionB);
|
|
957
|
+
});
|
|
892
958
|
log(
|
|
893
959
|
`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`,
|
|
894
960
|
"debug"
|
|
895
961
|
);
|
|
896
|
-
log(`Using tag: ${
|
|
897
|
-
return
|
|
962
|
+
log(`Using semantically latest tag: ${sortedPackageTags2[0]}`, "debug");
|
|
963
|
+
return sortedPackageTags2[0];
|
|
898
964
|
}
|
|
899
965
|
}
|
|
900
966
|
const pattern3 = new RegExp(`^${escapedPackageName}@`);
|
|
901
967
|
packageTags = allTags.filter((tag) => pattern3.test(tag));
|
|
902
|
-
log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
|
|
903
968
|
if (packageTags.length === 0) {
|
|
904
969
|
log("No matching tags found for pattern: packageName@version", "debug");
|
|
905
970
|
if (allTags.length > 0) {
|
|
@@ -907,10 +972,16 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
|
907
972
|
} else {
|
|
908
973
|
log("No tags available in the repository", "debug");
|
|
909
974
|
}
|
|
910
|
-
|
|
911
|
-
log(`Using tag: ${packageTags[0]}`, "debug");
|
|
975
|
+
return "";
|
|
912
976
|
}
|
|
913
|
-
|
|
977
|
+
const sortedPackageTags = [...packageTags].sort((a, b) => {
|
|
978
|
+
const versionA = semver.clean(a.split("@")[1] || "") || "0.0.0";
|
|
979
|
+
const versionB = semver.clean(b.split("@")[1] || "") || "0.0.0";
|
|
980
|
+
return semver.rcompare(versionA, versionB);
|
|
981
|
+
});
|
|
982
|
+
log(`Found ${packageTags.length} package tags for ${packageName}`, "debug");
|
|
983
|
+
log(`Using semantically latest tag: ${sortedPackageTags[0]}`, "debug");
|
|
984
|
+
return sortedPackageTags[0];
|
|
914
985
|
}
|
|
915
986
|
log(`Package-specific tags disabled for ${packageName}, falling back to global tags`, "debug");
|
|
916
987
|
return "";
|
|
@@ -1016,6 +1087,7 @@ function updatePackageVersion(packagePath, version) {
|
|
|
1016
1087
|
}
|
|
1017
1088
|
|
|
1018
1089
|
// src/package/packageProcessor.ts
|
|
1090
|
+
import { execSync as execSync4 } from "child_process";
|
|
1019
1091
|
import * as fs8 from "fs";
|
|
1020
1092
|
import path6 from "path";
|
|
1021
1093
|
import { exit } from "process";
|
|
@@ -1348,7 +1420,7 @@ function capitalizeFirstLetter(input) {
|
|
|
1348
1420
|
// src/core/versionCalculator.ts
|
|
1349
1421
|
import { cwd as cwd3 } from "process";
|
|
1350
1422
|
import { Bumper } from "conventional-recommended-bump";
|
|
1351
|
-
import
|
|
1423
|
+
import semver3 from "semver";
|
|
1352
1424
|
|
|
1353
1425
|
// src/utils/manifestHelpers.ts
|
|
1354
1426
|
import fs6 from "fs";
|
|
@@ -1409,7 +1481,7 @@ function throwIfNoManifestsFound(packageDir) {
|
|
|
1409
1481
|
|
|
1410
1482
|
// src/utils/versionUtils.ts
|
|
1411
1483
|
import fs7 from "fs";
|
|
1412
|
-
import
|
|
1484
|
+
import semver2 from "semver";
|
|
1413
1485
|
import * as TOML2 from "smol-toml";
|
|
1414
1486
|
var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
|
|
1415
1487
|
function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
@@ -1422,27 +1494,27 @@ function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
|
|
|
1422
1494
|
return void 0;
|
|
1423
1495
|
}
|
|
1424
1496
|
function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
|
|
1425
|
-
if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !
|
|
1497
|
+
if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !semver2.prerelease(currentVersion)) {
|
|
1426
1498
|
const preBumpType = `pre${bumpType}`;
|
|
1427
1499
|
log(
|
|
1428
1500
|
`Creating prerelease version with identifier '${prereleaseIdentifier}' using ${preBumpType}`,
|
|
1429
1501
|
"debug"
|
|
1430
1502
|
);
|
|
1431
|
-
return
|
|
1503
|
+
return semver2.inc(currentVersion, preBumpType, prereleaseIdentifier) || "";
|
|
1432
1504
|
}
|
|
1433
|
-
if (
|
|
1434
|
-
const parsed =
|
|
1505
|
+
if (semver2.prerelease(currentVersion) && STANDARD_BUMP_TYPES.includes(bumpType)) {
|
|
1506
|
+
const parsed = semver2.parse(currentVersion);
|
|
1435
1507
|
if (!parsed) {
|
|
1436
|
-
return
|
|
1508
|
+
return semver2.inc(currentVersion, bumpType) || "";
|
|
1437
1509
|
}
|
|
1438
1510
|
if (bumpType === "major" && parsed.minor === 0 && parsed.patch === 0 || bumpType === "minor" && parsed.patch === 0 || bumpType === "patch") {
|
|
1439
1511
|
log(`Cleaning prerelease identifier from ${currentVersion} for ${bumpType} bump`, "debug");
|
|
1440
1512
|
return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
1441
1513
|
}
|
|
1442
1514
|
log(`Standard increment for ${currentVersion} with ${bumpType} bump`, "debug");
|
|
1443
|
-
return
|
|
1515
|
+
return semver2.inc(currentVersion, bumpType) || "";
|
|
1444
1516
|
}
|
|
1445
|
-
return
|
|
1517
|
+
return semver2.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
|
|
1446
1518
|
}
|
|
1447
1519
|
|
|
1448
1520
|
// src/core/versionCalculator.ts
|
|
@@ -1484,10 +1556,10 @@ async function calculateVersion(config, options) {
|
|
|
1484
1556
|
const packageDir = pkgPath || cwd3();
|
|
1485
1557
|
const manifestResult = getVersionFromManifests(packageDir);
|
|
1486
1558
|
if (manifestResult.manifestFound && manifestResult.version) {
|
|
1487
|
-
const cleanedTag =
|
|
1488
|
-
const tagVersion =
|
|
1559
|
+
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1560
|
+
const tagVersion = semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1489
1561
|
const packageVersion = manifestResult.version;
|
|
1490
|
-
if (
|
|
1562
|
+
if (semver3.gt(packageVersion, tagVersion)) {
|
|
1491
1563
|
log(
|
|
1492
1564
|
`Warning: Version mismatch detected!
|
|
1493
1565
|
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
@@ -1507,7 +1579,7 @@ To fix this mismatch:
|
|
|
1507
1579
|
\u2022 Or use package version as base by ensuring tags are up to date`,
|
|
1508
1580
|
"warning"
|
|
1509
1581
|
);
|
|
1510
|
-
} else if (
|
|
1582
|
+
} else if (semver3.gt(tagVersion, packageVersion)) {
|
|
1511
1583
|
log(
|
|
1512
1584
|
`Warning: Version mismatch detected!
|
|
1513
1585
|
\u2022 ${manifestResult.manifestType} version: ${packageVersion}
|
|
@@ -1543,9 +1615,9 @@ To fix this mismatch:
|
|
|
1543
1615
|
initialVersion
|
|
1544
1616
|
);
|
|
1545
1617
|
}
|
|
1546
|
-
const cleanedTag =
|
|
1547
|
-
const currentVersion =
|
|
1548
|
-
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (
|
|
1618
|
+
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1619
|
+
const currentVersion = semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1620
|
+
if (STANDARD_BUMP_TYPES.includes(specifiedType) && (semver3.prerelease(currentVersion) || normalizedPrereleaseId)) {
|
|
1549
1621
|
log(
|
|
1550
1622
|
normalizedPrereleaseId ? `Creating prerelease version with identifier '${normalizedPrereleaseId}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
|
|
1551
1623
|
"debug"
|
|
@@ -1583,8 +1655,8 @@ To fix this mismatch:
|
|
|
1583
1655
|
initialVersion
|
|
1584
1656
|
);
|
|
1585
1657
|
}
|
|
1586
|
-
const cleanedTag =
|
|
1587
|
-
const currentVersion =
|
|
1658
|
+
const cleanedTag = semver3.clean(latestTag) || latestTag;
|
|
1659
|
+
const currentVersion = semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1588
1660
|
log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
|
|
1589
1661
|
return bumpVersion(currentVersion, branchVersionType, normalizedPrereleaseId);
|
|
1590
1662
|
}
|
|
@@ -1622,7 +1694,7 @@ To fix this mismatch:
|
|
|
1622
1694
|
);
|
|
1623
1695
|
return "";
|
|
1624
1696
|
}
|
|
1625
|
-
const currentVersion =
|
|
1697
|
+
const currentVersion = semver3.clean(latestTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
|
|
1626
1698
|
return bumpVersion(currentVersion, releaseTypeFromCommits, normalizedPrereleaseId);
|
|
1627
1699
|
} catch (error) {
|
|
1628
1700
|
log(`Failed to calculate version for ${name || "project"}`, "error");
|
|
@@ -1667,7 +1739,7 @@ function calculateNextVersion(version, manifestType, name, releaseType, prerelea
|
|
|
1667
1739
|
`No tags found for ${name || "package"}, using ${manifestType} version: ${version} as base`,
|
|
1668
1740
|
"info"
|
|
1669
1741
|
);
|
|
1670
|
-
if (STANDARD_BUMP_TYPES.includes(releaseType) && (
|
|
1742
|
+
if (STANDARD_BUMP_TYPES.includes(releaseType) && (semver3.prerelease(version) || prereleaseIdentifier)) {
|
|
1671
1743
|
log(
|
|
1672
1744
|
prereleaseIdentifier ? `Creating prerelease version with identifier '${prereleaseIdentifier}' using ${releaseType}` : `Cleaning prerelease identifier from ${version} for ${releaseType} bump`,
|
|
1673
1745
|
"debug"
|
|
@@ -1828,7 +1900,24 @@ var PackageProcessor = class {
|
|
|
1828
1900
|
if (this.fullConfig.updateChangelog !== false) {
|
|
1829
1901
|
let changelogEntries = [];
|
|
1830
1902
|
try {
|
|
1831
|
-
|
|
1903
|
+
let revisionRange = latestTag;
|
|
1904
|
+
if (latestTag) {
|
|
1905
|
+
try {
|
|
1906
|
+
execSync4(`git rev-parse --verify "${latestTag}"`, {
|
|
1907
|
+
cwd: pkgPath,
|
|
1908
|
+
stdio: "ignore"
|
|
1909
|
+
});
|
|
1910
|
+
} catch {
|
|
1911
|
+
log(
|
|
1912
|
+
`Tag ${latestTag} doesn't exist, using recent commits from HEAD for changelog`,
|
|
1913
|
+
"debug"
|
|
1914
|
+
);
|
|
1915
|
+
revisionRange = "HEAD~10..HEAD";
|
|
1916
|
+
}
|
|
1917
|
+
} else {
|
|
1918
|
+
revisionRange = "HEAD~10..HEAD";
|
|
1919
|
+
}
|
|
1920
|
+
changelogEntries = extractChangelogEntriesFromCommits(pkgPath, revisionRange);
|
|
1832
1921
|
if (changelogEntries.length === 0) {
|
|
1833
1922
|
changelogEntries = [
|
|
1834
1923
|
{
|
|
@@ -111,6 +111,11 @@
|
|
|
111
111
|
"default": true,
|
|
112
112
|
"description": "Whether to automatically generate and update changelogs"
|
|
113
113
|
},
|
|
114
|
+
"strictReachable": {
|
|
115
|
+
"type": "boolean",
|
|
116
|
+
"default": false,
|
|
117
|
+
"description": "Only use reachable tags (no fallback to unreachable tags)"
|
|
118
|
+
},
|
|
114
119
|
"cargo": {
|
|
115
120
|
"type": "object",
|
|
116
121
|
"properties": {
|
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.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.mjs",
|