@releasekit/version 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/dist/{chunk-V6S7BEBD.js → chunk-ZTFI7TXV.js} +52 -61
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/docs/versioning.md +8 -8
- package/package.json +3 -3
- package/docs/CI_CD_INTEGRATION.md +0 -197
package/README.md
CHANGED
|
@@ -133,10 +133,19 @@ Configure via `releasekit.config.json`:
|
|
|
133
133
|
| `cargo.enabled` | Update Cargo.toml files | `true` |
|
|
134
134
|
| `cargo.paths` | Directories containing Cargo.toml | auto-detect |
|
|
135
135
|
|
|
136
|
+
## Using in CI
|
|
137
|
+
|
|
138
|
+
A few things to keep in mind when running `releasekit-version` in a pipeline:
|
|
139
|
+
|
|
140
|
+
- **Always pass `fetch-depth: 0`** on checkout — the tool reads git history to determine the version bump and will produce incorrect results on a shallow clone.
|
|
141
|
+
- **Use `--json`** for reliable downstream parsing. Text output format can change; the JSON schema is stable.
|
|
142
|
+
- **`NO_COLOR=1`** disables ANSI colour codes in log output. Most CI environments set `CI=true` automatically, which the tool detects and adjusts for.
|
|
143
|
+
|
|
144
|
+
If you are running the full release pipeline (version + changelog + publish), use `@releasekit/release` instead of invoking `releasekit-version` directly. See the [CI setup guide](../release/docs/ci-setup.md).
|
|
145
|
+
|
|
136
146
|
## Documentation
|
|
137
147
|
|
|
138
148
|
- [Versioning Strategies and Concepts](./docs/versioning.md)
|
|
139
|
-
- [CI/CD Integration](./docs/CI_CD_INTEGRATION.md)
|
|
140
149
|
|
|
141
150
|
## Acknowledgements
|
|
142
151
|
|
|
@@ -122,14 +122,14 @@ var GitHubReleaseConfigSchema = z.object({
|
|
|
122
122
|
perPackage: z.boolean().default(true),
|
|
123
123
|
prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
|
|
124
124
|
/**
|
|
125
|
-
* Controls
|
|
126
|
-
* - 'auto': Use
|
|
127
|
-
*
|
|
128
|
-
* - '
|
|
129
|
-
* - '
|
|
130
|
-
* -
|
|
125
|
+
* Controls the source for the GitHub release body.
|
|
126
|
+
* - 'auto': Use release notes if enabled, else changelog, else GitHub auto-generated.
|
|
127
|
+
* - 'releaseNotes': Use LLM-generated release notes (requires notes.releaseNotes.enabled: true).
|
|
128
|
+
* - 'changelog': Use formatted changelog entries.
|
|
129
|
+
* - 'generated': Use GitHub's auto-generated notes.
|
|
130
|
+
* - 'none': No body.
|
|
131
131
|
*/
|
|
132
|
-
|
|
132
|
+
body: z.enum(["auto", "releaseNotes", "changelog", "generated", "none"]).default("auto")
|
|
133
133
|
});
|
|
134
134
|
var VerifyRegistryConfigSchema = z.object({
|
|
135
135
|
enabled: z.boolean().default(true),
|
|
@@ -173,7 +173,7 @@ var PublishConfigSchema = z.object({
|
|
|
173
173
|
draft: true,
|
|
174
174
|
perPackage: true,
|
|
175
175
|
prerelease: "auto",
|
|
176
|
-
|
|
176
|
+
body: "auto"
|
|
177
177
|
}),
|
|
178
178
|
verify: VerifyConfigSchema.default({
|
|
179
179
|
npm: {
|
|
@@ -194,10 +194,10 @@ var TemplateConfigSchema = z.object({
|
|
|
194
194
|
path: z.string().optional(),
|
|
195
195
|
engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
|
|
196
196
|
});
|
|
197
|
-
var
|
|
198
|
-
|
|
197
|
+
var LocationModeSchema = z.enum(["root", "packages", "both"]);
|
|
198
|
+
var ChangelogConfigSchema = z.object({
|
|
199
|
+
mode: LocationModeSchema.optional(),
|
|
199
200
|
file: z.string().optional(),
|
|
200
|
-
options: z.record(z.string(), z.unknown()).optional(),
|
|
201
201
|
templates: TemplateConfigSchema.optional()
|
|
202
202
|
});
|
|
203
203
|
var LLMOptionsSchema = z.object({
|
|
@@ -257,17 +257,20 @@ var LLMConfigSchema = z.object({
|
|
|
257
257
|
scopes: ScopeConfigSchema.optional(),
|
|
258
258
|
prompts: LLMPromptsConfigSchema.optional()
|
|
259
259
|
});
|
|
260
|
+
var ReleaseNotesConfigSchema = z.object({
|
|
261
|
+
mode: LocationModeSchema.optional(),
|
|
262
|
+
file: z.string().optional(),
|
|
263
|
+
templates: TemplateConfigSchema.optional(),
|
|
264
|
+
llm: LLMConfigSchema.optional()
|
|
265
|
+
});
|
|
260
266
|
var NotesInputConfigSchema = z.object({
|
|
261
267
|
source: z.string().optional(),
|
|
262
268
|
file: z.string().optional()
|
|
263
269
|
});
|
|
264
270
|
var NotesConfigSchema = z.object({
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
templates: TemplateConfigSchema.optional(),
|
|
269
|
-
llm: LLMConfigSchema.optional(),
|
|
270
|
-
updateStrategy: z.enum(["prepend", "regenerate"]).default("prepend")
|
|
271
|
+
changelog: z.union([z.literal(false), ChangelogConfigSchema]).optional(),
|
|
272
|
+
releaseNotes: z.union([z.literal(false), ReleaseNotesConfigSchema]).optional(),
|
|
273
|
+
updateStrategy: z.enum(["prepend", "regenerate"]).optional()
|
|
271
274
|
});
|
|
272
275
|
var CILabelsConfigSchema = z.object({
|
|
273
276
|
stable: z.string().default("release:stable"),
|
|
@@ -629,6 +632,7 @@ function formatVersionPrefix(prefix) {
|
|
|
629
632
|
return prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
630
633
|
}
|
|
631
634
|
function formatTag(version, prefix, packageName, template, packageSpecificTags) {
|
|
635
|
+
const sanitizedPackageName = packageName?.startsWith("@") ? packageName.slice(1).replace(/\//g, "-") : packageName;
|
|
632
636
|
if (template?.includes("${packageName}") && !packageName) {
|
|
633
637
|
log(
|
|
634
638
|
`Warning: Your tagTemplate contains \${packageName} but no package name is available.
|
|
@@ -642,10 +646,10 @@ To fix this:
|
|
|
642
646
|
);
|
|
643
647
|
}
|
|
644
648
|
if (template) {
|
|
645
|
-
return template.replace(/\$\{version\}/g, version).replace(/\$\{prefix\}/g, prefix).replace(/\$\{packageName\}/g,
|
|
649
|
+
return template.replace(/\$\{version\}/g, version).replace(/\$\{prefix\}/g, prefix).replace(/\$\{packageName\}/g, sanitizedPackageName || "");
|
|
646
650
|
}
|
|
647
|
-
if (packageSpecificTags &&
|
|
648
|
-
return `${
|
|
651
|
+
if (packageSpecificTags && sanitizedPackageName) {
|
|
652
|
+
return `${sanitizedPackageName}@${prefix}${version}`;
|
|
649
653
|
}
|
|
650
654
|
return `${prefix}${version}`;
|
|
651
655
|
}
|
|
@@ -742,9 +746,10 @@ async function lastMergeBranchName(branches, baseBranch) {
|
|
|
742
746
|
}
|
|
743
747
|
async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
744
748
|
try {
|
|
745
|
-
const tagTemplate = options?.tagTemplate || `\${prefix}\${version}`;
|
|
746
749
|
const packageSpecificTags = options?.packageSpecificTags ?? false;
|
|
747
|
-
const
|
|
750
|
+
const tagTemplate = options?.tagTemplate || (packageSpecificTags ? `\${packageName}@\${prefix}\${version}` : `\${prefix}\${version}`);
|
|
751
|
+
const sanitizedPackageName = packageName.startsWith("@") ? packageName.slice(1).replace(/\//g, "-") : packageName;
|
|
752
|
+
const escapedPackageName = escapeRegExp(sanitizedPackageName);
|
|
748
753
|
const escapedPrefix = versionPrefix ? escapeRegExp(versionPrefix) : "";
|
|
749
754
|
log(
|
|
750
755
|
`Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
|
|
@@ -763,45 +768,20 @@ async function getLatestTagForPackage(packageName, versionPrefix, options) {
|
|
|
763
768
|
const packageTagPattern = escapeRegExp(tagTemplate).replace(/\\\$\\\{packageName\\\}/g, `(?:${escapedPackageName})`).replace(/\\\$\\\{prefix\\\}/g, `(?:${escapedPrefix})`).replace(/\\\$\\\{version\\\}/g, "(?:[0-9]+\\.[0-9]+\\.[0-9]+(?:-[a-zA-Z0-9.-]+)?)");
|
|
764
769
|
log(`Using package tag pattern: ${packageTagPattern}`, "debug");
|
|
765
770
|
const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
|
|
766
|
-
|
|
771
|
+
const packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
|
|
767
772
|
log(`Found ${packageTags.length} matching tags for ${packageName}`, "debug");
|
|
768
773
|
if (packageTags.length > 0) {
|
|
769
774
|
log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
|
|
770
775
|
log(`Using most recently created tag: ${packageTags[0]}`, "debug");
|
|
771
776
|
return packageTags[0];
|
|
772
777
|
}
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
log(`Using most recently created tag: ${packageTags[0]}`, "debug");
|
|
779
|
-
return packageTags[0];
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
if (versionPrefix) {
|
|
783
|
-
const pattern2 = new RegExp(`^${escapeRegExp(versionPrefix)}${escapedPackageName}@`);
|
|
784
|
-
packageTags = allTags.filter((tag) => pattern2.test(tag));
|
|
785
|
-
if (packageTags.length > 0) {
|
|
786
|
-
log(`Found ${packageTags.length} package tags using pattern: ${versionPrefix}packageName@...`, "debug");
|
|
787
|
-
log(`Using most recently created tag: ${packageTags[0]}`, "debug");
|
|
788
|
-
return packageTags[0];
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
const pattern3 = new RegExp(`^${escapedPackageName}@`);
|
|
792
|
-
packageTags = allTags.filter((tag) => pattern3.test(tag));
|
|
793
|
-
if (packageTags.length === 0) {
|
|
794
|
-
log("No matching tags found for pattern: packageName@version", "debug");
|
|
795
|
-
if (allTags.length > 0) {
|
|
796
|
-
log(`Available tags: ${allTags.join(", ")}`, "debug");
|
|
797
|
-
} else {
|
|
798
|
-
log("No tags available in the repository", "debug");
|
|
799
|
-
}
|
|
800
|
-
return "";
|
|
778
|
+
log("No matching tags found for configured tag pattern", "debug");
|
|
779
|
+
if (allTags.length > 0) {
|
|
780
|
+
log(`Available tags: ${allTags.join(", ")}`, "debug");
|
|
781
|
+
} else {
|
|
782
|
+
log("No tags available in the repository", "debug");
|
|
801
783
|
}
|
|
802
|
-
|
|
803
|
-
log(`Using most recently created tag: ${packageTags[0]}`, "debug");
|
|
804
|
-
return packageTags[0];
|
|
784
|
+
return "";
|
|
805
785
|
}
|
|
806
786
|
log(`Package-specific tags disabled for ${packageName}, falling back to global tags`, "debug");
|
|
807
787
|
return "";
|
|
@@ -1863,30 +1843,38 @@ function createSyncStrategy(config) {
|
|
|
1863
1843
|
let latestTag = await getLatestTag();
|
|
1864
1844
|
let mainPkgPath = packages.root;
|
|
1865
1845
|
let mainPkgName;
|
|
1846
|
+
let versionSourcePath = mainPkgPath;
|
|
1847
|
+
let versionSourceName;
|
|
1866
1848
|
if (mainPackage) {
|
|
1867
1849
|
const mainPkg = packages.packages.find((p) => p.packageJson.name === mainPackage);
|
|
1868
1850
|
if (mainPkg) {
|
|
1869
1851
|
mainPkgPath = mainPkg.dir;
|
|
1870
1852
|
mainPkgName = mainPkg.packageJson.name;
|
|
1853
|
+
versionSourcePath = mainPkgPath;
|
|
1854
|
+
versionSourceName = mainPkgName;
|
|
1871
1855
|
log(`Using ${mainPkgName} as primary package for version determination`, "info");
|
|
1872
1856
|
} else {
|
|
1873
1857
|
log(`Main package '${mainPackage}' not found. Using root package for version determination.`, "warning");
|
|
1874
1858
|
}
|
|
1859
|
+
} else if (packages.packages.length > 0) {
|
|
1860
|
+
versionSourcePath = packages.packages[0].dir;
|
|
1861
|
+
versionSourceName = packages.packages[0].packageJson.name;
|
|
1862
|
+
log(`No mainPackage specified; using ${versionSourceName} as sync version source`, "info");
|
|
1875
1863
|
}
|
|
1876
1864
|
if (!mainPkgPath) {
|
|
1877
1865
|
mainPkgPath = process.cwd();
|
|
1878
1866
|
log(`No valid package path found, using current working directory: ${mainPkgPath}`, "warning");
|
|
1879
1867
|
}
|
|
1880
|
-
if (
|
|
1881
|
-
const packageSpecificTag = await getLatestTagForPackage(
|
|
1868
|
+
if (versionSourceName) {
|
|
1869
|
+
const packageSpecificTag = await getLatestTagForPackage(versionSourceName, formattedPrefix, {
|
|
1882
1870
|
tagTemplate,
|
|
1883
1871
|
packageSpecificTags: config.packageSpecificTags
|
|
1884
1872
|
});
|
|
1885
1873
|
if (packageSpecificTag) {
|
|
1886
1874
|
latestTag = packageSpecificTag;
|
|
1887
|
-
log(`Using package-specific tag for ${
|
|
1875
|
+
log(`Using package-specific tag for ${versionSourceName}: ${latestTag}`, "debug");
|
|
1888
1876
|
} else {
|
|
1889
|
-
log(`No package-specific tag found for ${
|
|
1877
|
+
log(`No package-specific tag found for ${versionSourceName}, using global tag: ${latestTag}`, "debug");
|
|
1890
1878
|
}
|
|
1891
1879
|
}
|
|
1892
1880
|
const nextVersion = await calculateVersion(config, {
|
|
@@ -1895,8 +1883,8 @@ function createSyncStrategy(config) {
|
|
|
1895
1883
|
branchPattern,
|
|
1896
1884
|
baseBranch,
|
|
1897
1885
|
prereleaseIdentifier,
|
|
1898
|
-
path:
|
|
1899
|
-
name:
|
|
1886
|
+
path: versionSourcePath,
|
|
1887
|
+
name: versionSourceName,
|
|
1900
1888
|
type: config.type
|
|
1901
1889
|
});
|
|
1902
1890
|
if (!nextVersion) {
|
|
@@ -2002,7 +1990,10 @@ function createSyncStrategy(config) {
|
|
|
2002
1990
|
nextVersion,
|
|
2003
1991
|
formattedPrefix,
|
|
2004
1992
|
tagPackageName,
|
|
2005
|
-
tagTemplate
|
|
1993
|
+
// Only pass tagTemplate when we have a package name to substitute into it.
|
|
1994
|
+
// In multi-package sync mode tagPackageName is null, so omit the template to
|
|
1995
|
+
// avoid a spurious ${packageName} warning and a malformed tag like "-v1.0.0".
|
|
1996
|
+
tagPackageName ? tagTemplate : void 0,
|
|
2006
1997
|
config.packageSpecificTags || false
|
|
2007
1998
|
);
|
|
2008
1999
|
let formattedCommitMessage;
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/docs/versioning.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## How the Next Version is Calculated
|
|
6
6
|
|
|
7
|
-
There are two primary methods the tool uses to decide the version bump (e.g., patch, minor, major), configured via the `versionStrategy` option in `
|
|
7
|
+
There are two primary methods the tool uses to decide the version bump (e.g., patch, minor, major), configured via the `versionStrategy` option in `releasekit.config.json`:
|
|
8
8
|
|
|
9
9
|
### 1. Conventional Commits (`versionStrategy: "conventional"`)
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ This is the default strategy. `releasekit-version` analyzes Git commit messages
|
|
|
14
14
|
- **Minor Bump (e.g., 1.2.3 -> 1.3.0):** Triggered by `feat:` commit types.
|
|
15
15
|
- **Major Bump (e.g., 1.2.3 -> 2.0.0):** Triggered by commits with `BREAKING CHANGE:` in the footer or `feat!:`, `fix!:` etc. in the header.
|
|
16
16
|
|
|
17
|
-
The specific preset used for analysis (e.g., "angular", "conventional") can be set using the `preset` option in `
|
|
17
|
+
The specific preset used for analysis (e.g., "angular", "conventional") can be set using the `preset` option in `releasekit.config.json`.
|
|
18
18
|
|
|
19
19
|
**Format:** `<type>(<scope>): <subject>`
|
|
20
20
|
|
|
@@ -39,9 +39,9 @@ The specific preset used for analysis (e.g., "angular", "conventional") can be s
|
|
|
39
39
|
|
|
40
40
|
This strategy uses the name of the current Git branch (or the most recently merged branch matching a pattern, if applicable) to determine the version bump.
|
|
41
41
|
|
|
42
|
-
You define patterns in the `branchPattern` array in `
|
|
42
|
+
You define patterns in the `branchPattern` array in `releasekit.config.json`. Each pattern is a string like `"prefix:bumptype"`.
|
|
43
43
|
|
|
44
|
-
**Example `
|
|
44
|
+
**Example `releasekit.config.json`:**
|
|
45
45
|
|
|
46
46
|
```json
|
|
47
47
|
{
|
|
@@ -156,7 +156,7 @@ Mismatches are detected in the following cases:
|
|
|
156
156
|
- Git tag is ahead by a major or minor version (e.g., tag `2.0.0` vs package `1.0.0`)
|
|
157
157
|
- Git tag is a prerelease but package.json is a stable release (e.g., tag `1.0.0-beta.1` vs package `1.0.0`)
|
|
158
158
|
|
|
159
|
-
Configure it in `
|
|
159
|
+
Configure it in `releasekit.config.json`:
|
|
160
160
|
```json
|
|
161
161
|
{
|
|
162
162
|
"mismatchStrategy": "prefer-package"
|
|
@@ -277,7 +277,7 @@ This configuration will process all packages in the `@mycompany` scope except fo
|
|
|
277
277
|
|
|
278
278
|
### Tag Template Configuration
|
|
279
279
|
|
|
280
|
-
You can customize how tags are formatted using the following configuration options in `
|
|
280
|
+
You can customize how tags are formatted using the following configuration options in `releasekit.config.json`:
|
|
281
281
|
|
|
282
282
|
```json
|
|
283
283
|
{
|
|
@@ -430,7 +430,7 @@ For global commit messages, use templates without `${packageName}`:
|
|
|
430
430
|
|
|
431
431
|
## Monorepo Versioning Modes
|
|
432
432
|
|
|
433
|
-
While primarily used for single packages now, `releasekit-version` retains options for monorepo workflows, controlled mainly by the `sync` flag in `
|
|
433
|
+
While primarily used for single packages now, `releasekit-version` retains options for monorepo workflows, controlled mainly by the `sync` flag in `releasekit.config.json`.
|
|
434
434
|
|
|
435
435
|
### Sync Mode (`sync: true`)
|
|
436
436
|
|
|
@@ -475,7 +475,7 @@ npx releasekit-version --bump minor --prerelease beta
|
|
|
475
475
|
# Result: 1.0.0 -> 1.1.0-beta.0
|
|
476
476
|
```
|
|
477
477
|
|
|
478
|
-
You can also set a default prerelease identifier in your `
|
|
478
|
+
You can also set a default prerelease identifier in your `releasekit.config.json`:
|
|
479
479
|
|
|
480
480
|
```json
|
|
481
481
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@releasekit/version",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Semantic versioning based on Git history and conventional commits",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -72,8 +72,8 @@
|
|
|
72
72
|
"tsup": "^8.5.1",
|
|
73
73
|
"typescript": "^5.9.3",
|
|
74
74
|
"vitest": "^4.1.0",
|
|
75
|
-
"@releasekit/
|
|
76
|
-
"@releasekit/
|
|
75
|
+
"@releasekit/core": "0.0.0",
|
|
76
|
+
"@releasekit/config": "0.0.0"
|
|
77
77
|
},
|
|
78
78
|
"engines": {
|
|
79
79
|
"node": ">=20"
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
# CI/CD Integration
|
|
2
|
-
|
|
3
|
-
`releasekit-version` is designed to work seamlessly in CI/CD pipelines, making it easy to automate versioning as part of your release workflow.
|
|
4
|
-
|
|
5
|
-
## JSON Output Mode
|
|
6
|
-
|
|
7
|
-
For programmatic consumption in CI/CD scripts, `releasekit-version` provides a structured JSON output option:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# Output results in JSON format
|
|
11
|
-
npx releasekit-version --json
|
|
12
|
-
|
|
13
|
-
# Combine with dry-run for planning
|
|
14
|
-
npx releasekit-version --dry-run --json
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
This will suppress all normal console output and instead output a single JSON object containing:
|
|
18
|
-
|
|
19
|
-
```json
|
|
20
|
-
{
|
|
21
|
-
"dryRun": false, // Whether this was a dry run
|
|
22
|
-
"updates": [ // Array of packages that were updated
|
|
23
|
-
{
|
|
24
|
-
"packageName": "@scope/package-a", // Package name
|
|
25
|
-
"newVersion": "1.2.3", // New version number
|
|
26
|
-
"filePath": "/path/to/package.json" // Path to the updated package.json
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
"changelogs": [ // Structured changelog data per package
|
|
30
|
-
{
|
|
31
|
-
"packageName": "@scope/package-a", // Package name
|
|
32
|
-
"version": "1.2.3", // New version
|
|
33
|
-
"previousVersion": "v1.2.2", // Previous tag (null if none)
|
|
34
|
-
"revisionRange": "v1.2.2..HEAD", // Git revision range used
|
|
35
|
-
"repoUrl": "https://github.com/org/repo", // Repository URL (null if unknown)
|
|
36
|
-
"entries": [ // Parsed changelog entries
|
|
37
|
-
{ "type": "added", "description": "New feature", "scope": "core" },
|
|
38
|
-
{ "type": "fixed", "description": "Bug fix" }
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"commitMessage": "chore: release my-package v1.2.3", // The commit message that was used
|
|
43
|
-
"tags": [ // Array of tags that were created
|
|
44
|
-
"v1.2.3" // or package-specific tags in targeted mode
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Benefits of JSON Output
|
|
50
|
-
|
|
51
|
-
The structured JSON output provides several advantages for CI/CD integration:
|
|
52
|
-
|
|
53
|
-
- **Reliable Parsing**: Unlike text logs that might change format or include ANSI color codes, the JSON structure remains consistent
|
|
54
|
-
- **Programmatic Access**: Easily extract specific values like version numbers for subsequent steps
|
|
55
|
-
- **Conditional Workflows**: Trigger different CI actions based on the presence of updates or specific version changes
|
|
56
|
-
- **Audit Trail**: Store the JSON output as artifacts for version change tracking
|
|
57
|
-
- **Error Handling**: Better detect and respond to versioning issues in your pipeline
|
|
58
|
-
|
|
59
|
-
## Sample CI/CD Integration Patterns
|
|
60
|
-
|
|
61
|
-
Here are some common ways to incorporate `releasekit-version` into your CI/CD pipeline:
|
|
62
|
-
|
|
63
|
-
### GitHub Actions Workflow Example
|
|
64
|
-
|
|
65
|
-
```yaml
|
|
66
|
-
name: Release
|
|
67
|
-
|
|
68
|
-
on:
|
|
69
|
-
push:
|
|
70
|
-
branches: [main]
|
|
71
|
-
|
|
72
|
-
jobs:
|
|
73
|
-
version:
|
|
74
|
-
runs-on: ubuntu-latest
|
|
75
|
-
outputs:
|
|
76
|
-
changes_detected: ${{ steps.version.outputs.changes_detected }}
|
|
77
|
-
new_version: ${{ steps.version.outputs.new_version }}
|
|
78
|
-
|
|
79
|
-
steps:
|
|
80
|
-
- uses: actions/checkout@v6
|
|
81
|
-
with:
|
|
82
|
-
fetch-depth: 0 # Important for git history
|
|
83
|
-
|
|
84
|
-
- name: Setup Node.js
|
|
85
|
-
uses: actions/setup-node@v6
|
|
86
|
-
with:
|
|
87
|
-
node-version: '18'
|
|
88
|
-
|
|
89
|
-
- name: Install dependencies
|
|
90
|
-
run: npm ci
|
|
91
|
-
|
|
92
|
-
- name: Determine version
|
|
93
|
-
id: version
|
|
94
|
-
run: |
|
|
95
|
-
# Run in JSON mode for parsing
|
|
96
|
-
VERSION_OUTPUT=$(npx releasekit-version --json)
|
|
97
|
-
echo "Version output: $VERSION_OUTPUT"
|
|
98
|
-
|
|
99
|
-
# Use jq to parse the JSON output
|
|
100
|
-
CHANGES_DETECTED=$(echo "$VERSION_OUTPUT" | jq -r '.updates | length > 0')
|
|
101
|
-
echo "changes_detected=$CHANGES_DETECTED" >> $GITHUB_OUTPUT
|
|
102
|
-
|
|
103
|
-
if [ "$CHANGES_DETECTED" = "true" ]; then
|
|
104
|
-
# Extract the first package's new version as representative version
|
|
105
|
-
NEW_VERSION=$(echo "$VERSION_OUTPUT" | jq -r '.updates[0].newVersion')
|
|
106
|
-
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
107
|
-
fi
|
|
108
|
-
|
|
109
|
-
publish:
|
|
110
|
-
needs: version
|
|
111
|
-
if: needs.version.outputs.changes_detected == 'true'
|
|
112
|
-
runs-on: ubuntu-latest
|
|
113
|
-
steps:
|
|
114
|
-
# Publishing steps using the detected version
|
|
115
|
-
- run: echo "Would publish version ${{ needs.version.outputs.new_version }}"
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### GitLab CI Pipeline Example
|
|
119
|
-
|
|
120
|
-
```yaml
|
|
121
|
-
stages:
|
|
122
|
-
- version
|
|
123
|
-
- publish
|
|
124
|
-
|
|
125
|
-
determine_version:
|
|
126
|
-
stage: version
|
|
127
|
-
script:
|
|
128
|
-
- npm ci
|
|
129
|
-
- |
|
|
130
|
-
VERSION_OUTPUT=$(npx releasekit-version --json)
|
|
131
|
-
echo "VERSION_OUTPUT=$VERSION_OUTPUT" >> version.env
|
|
132
|
-
|
|
133
|
-
# Parse values for use in later stages
|
|
134
|
-
CHANGES_DETECTED=$(echo "$VERSION_OUTPUT" | jq -r '.updates | length > 0')
|
|
135
|
-
echo "CHANGES_DETECTED=$CHANGES_DETECTED" >> version.env
|
|
136
|
-
|
|
137
|
-
if [ "$CHANGES_DETECTED" = "true" ]; then
|
|
138
|
-
NEW_VERSION=$(echo "$VERSION_OUTPUT" | jq -r '.updates[0].newVersion')
|
|
139
|
-
echo "NEW_VERSION=$NEW_VERSION" >> version.env
|
|
140
|
-
fi
|
|
141
|
-
artifacts:
|
|
142
|
-
reports:
|
|
143
|
-
dotenv: version.env
|
|
144
|
-
|
|
145
|
-
publish:
|
|
146
|
-
stage: publish
|
|
147
|
-
needs: determine_version
|
|
148
|
-
script:
|
|
149
|
-
- echo "Publishing version $NEW_VERSION"
|
|
150
|
-
rules:
|
|
151
|
-
- if: $CHANGES_DETECTED == "true"
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Working with Tags in CI
|
|
155
|
-
|
|
156
|
-
When using the targeted mode with `-t` flag, `releasekit-version` creates package-specific tags (e.g., `@scope/package-a@1.2.0`) but not a global tag. If your release process needs a global tag, you can add a step to your CI/CD pipeline:
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
# Create a global tag based on the representative version
|
|
160
|
-
NEW_VERSION=$(echo "$VERSION_OUTPUT" | jq -r '.updates[0].newVersion')
|
|
161
|
-
git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
|
|
162
|
-
git push origin "v$NEW_VERSION"
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Environment Variables
|
|
166
|
-
|
|
167
|
-
`releasekit-version` respects the following environment variables:
|
|
168
|
-
|
|
169
|
-
- `NO_COLOR=1`: Disables colored output in logs (automatically detected in CI environments)
|
|
170
|
-
- `CI=true`: Most CI environments set this automatically, which helps the tool adjust its output behaviour
|
|
171
|
-
|
|
172
|
-
## Skipping CI for Version Commits
|
|
173
|
-
|
|
174
|
-
If you want to prevent additional CI runs when version commits are made, you can include CI skip flags in your commit message template in `version.config.json`:
|
|
175
|
-
|
|
176
|
-
```json
|
|
177
|
-
{
|
|
178
|
-
"commitMessage": "chore: release ${packageName} v${version} [skip ci]",
|
|
179
|
-
// other configuration options...
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Common CI skip patterns include:
|
|
184
|
-
- `[skip ci]` or `[ci skip]` - Works in GitHub Actions, GitLab CI, CircleCI
|
|
185
|
-
- `[skip-ci]` - Alternative format supported by some CI systems
|
|
186
|
-
- `[no ci]` - Another variant
|
|
187
|
-
|
|
188
|
-
Each CI system might have slightly different syntax, so check your CI provider's documentation for the exact skip token to use.
|
|
189
|
-
|
|
190
|
-
## Tips for Reliable CI/CD Integration
|
|
191
|
-
|
|
192
|
-
1. **Always use `--json`** in CI/CD pipelines for consistent output parsing
|
|
193
|
-
2. **Use the `fetch-depth: 0`** option in GitHub Actions (or equivalent in other CIs) to ensure access to the full Git history
|
|
194
|
-
3. **Store the JSON output** as a build artifact for debugging and auditing
|
|
195
|
-
4. **Consider dry runs** in your preview/staging branches to validate version changes before they're applied
|
|
196
|
-
5. **Use `--project-dir`** when running from a different directory than your project root
|
|
197
|
-
6. **Be mindful of Git credentials** - ensure your CI has proper permissions for creating commits and tags
|