@modulify/conventional-release 0.1.0 → 0.1.2
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 +44 -1
- package/bin/cli.cjs +29 -305
- package/bin/cli.mjs +27 -288
- package/dist/_virtual/_rolldown/runtime.cjs +23 -0
- package/dist/cli/args.cjs +67 -0
- package/dist/cli/args.mjs +65 -0
- package/dist/cli/output.cjs +51 -0
- package/dist/cli/output.mjs +46 -0
- package/dist/cli/reporter.cjs +127 -0
- package/dist/cli/reporter.mjs +127 -0
- package/dist/config.cjs +54 -0
- package/dist/config.mjs +51 -0
- package/dist/constants.cjs +16 -0
- package/dist/constants.mjs +12 -0
- package/dist/execute.cjs +192 -0
- package/dist/execute.mjs +190 -0
- package/dist/index.cjs +51 -707
- package/dist/index.mjs +51 -710
- package/dist/plan.cjs +260 -0
- package/dist/plan.mjs +253 -0
- package/dist/reporter.cjs +33 -0
- package/dist/reporter.mjs +27 -0
- package/dist/runtime.cjs +68 -0
- package/dist/runtime.mjs +67 -0
- package/package.json +16 -11
- package/types/release.d.ts +2 -0
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# @modulify/conventional-release
|
|
2
2
|
|
|
3
|
-
Release
|
|
3
|
+
Release-core package for conventional workflows.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@modulify/conventional-release)
|
|
6
|
+
[](https://codecov.io/gh/modulify/conventional?flags[0]=conventional-release)
|
|
7
|
+
|
|
8
|
+
[🌐 Translations](https://github.com/modulify/conventional/blob/main/packages/conventional-release/docs/INDEX.md)
|
|
4
9
|
|
|
5
10
|
This workspace combines:
|
|
6
11
|
- semantic version recommendation from `@modulify/conventional-bump`,
|
|
@@ -13,6 +18,22 @@ The package is library-first. It exposes:
|
|
|
13
18
|
- `run()` to apply the release flow,
|
|
14
19
|
- `conventional-release` as a config-driven CLI binary.
|
|
15
20
|
|
|
21
|
+
## Scope and non-goals
|
|
22
|
+
|
|
23
|
+
This package is intentionally focused on release-core responsibilities inside the repository:
|
|
24
|
+
|
|
25
|
+
- discover the release scope
|
|
26
|
+
- compute versions from commit history
|
|
27
|
+
- update manifests and changelog files
|
|
28
|
+
- finalize the release with a commit and tags
|
|
29
|
+
|
|
30
|
+
It does not try to be an all-in-one delivery tool.
|
|
31
|
+
In particular, `npm publish`, GitHub Releases, GitLab Releases, registry credentials, and deployment-specific CI steps are out of scope for this package.
|
|
32
|
+
|
|
33
|
+
The intended layering is:
|
|
34
|
+
- `@modulify/conventional-release` handles planning and repository-local release finalization
|
|
35
|
+
- higher-level tools can add publishing, hosting, or CI-specific orchestration on top
|
|
36
|
+
|
|
16
37
|
## Installation
|
|
17
38
|
|
|
18
39
|
```bash
|
|
@@ -36,6 +57,9 @@ It resolves packages, filters workspaces, detects affected packages, and produce
|
|
|
36
57
|
2. `run(options)` resolves the same scope and applies side effects.
|
|
37
58
|
It updates manifests, writes the changelog, creates a commit, and creates tags.
|
|
38
59
|
|
|
60
|
+
That is the end of this package's responsibility boundary.
|
|
61
|
+
Delivery steps outside the repository, such as package publication or hosted release creation, should be implemented above this layer.
|
|
62
|
+
|
|
39
63
|
`Scope` is the declarative view of a release.
|
|
40
64
|
`Slice` is one execution unit inside that scope.
|
|
41
65
|
|
|
@@ -103,6 +127,7 @@ Useful flags:
|
|
|
103
127
|
- `--prerelease <channel>`: use `alpha`, `beta`, or `rc`
|
|
104
128
|
|
|
105
129
|
The CLI reads the same repository configuration as the library API and wires a lifecycle reporter into `run()`.
|
|
130
|
+
It stops after repository-local release finalization and does not publish artifacts.
|
|
106
131
|
|
|
107
132
|
## Inspect before running
|
|
108
133
|
|
|
@@ -146,6 +171,23 @@ console.log(result.slices)
|
|
|
146
171
|
|
|
147
172
|
When `dry: true` is used, the package still resolves versions, tags, and touched files, but skips write-side effects.
|
|
148
173
|
|
|
174
|
+
## Pre-1.0 releases
|
|
175
|
+
|
|
176
|
+
For packages below `1.0.0`, automatic conventional recommendations follow `@modulify/conventional-bump` pre-major semantics by default:
|
|
177
|
+
|
|
178
|
+
- recommended `major` becomes `minor`
|
|
179
|
+
- recommended `minor` becomes `patch`
|
|
180
|
+
- recommended `patch` stays `patch`
|
|
181
|
+
|
|
182
|
+
Explicit `releaseAs` overrides stay authoritative, so `releaseAs: 'major'` still produces `1.0.0`.
|
|
183
|
+
Set `preMajor: false` to opt out of automatic downgrades:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
await run({
|
|
187
|
+
preMajor: false,
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
149
191
|
## Configuration sources
|
|
150
192
|
|
|
151
193
|
Configuration is resolved in this order:
|
|
@@ -198,6 +240,7 @@ The most important public options are:
|
|
|
198
240
|
|
|
199
241
|
- `mode`: release strategy, one of `sync`, `async`, or `hybrid`
|
|
200
242
|
- `releaseAs`: explicit semver bump override such as `major`, `minor`, or `patch`
|
|
243
|
+
- `preMajor`: whether automatic recommendations should be downgraded below `1.0.0`
|
|
201
244
|
- `prerelease`: prerelease channel, one of `alpha`, `beta`, or `rc`
|
|
202
245
|
- `fromTag`: explicit lower bound tag for advisory commit analysis
|
|
203
246
|
- `tagPrefix`: tag matcher used during advisory commit analysis
|
package/bin/cli.cjs
CHANGED
|
@@ -1,308 +1,32 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
function _interopNamespaceDefault(e) {
|
|
12
|
-
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
13
|
-
if (e) {
|
|
14
|
-
for (const k in e) {
|
|
15
|
-
if (k !== "default") {
|
|
16
|
-
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
-
enumerable: true,
|
|
19
|
-
get: () => e[k]
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
n.default = e;
|
|
25
|
-
return Object.freeze(n);
|
|
26
|
-
}
|
|
27
|
-
const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
|
|
28
|
-
const DEFAULTS = {
|
|
29
|
-
dry: false,
|
|
30
|
-
verbose: false,
|
|
31
|
-
tags: false
|
|
32
|
-
};
|
|
33
|
-
class CliParseError extends Error {
|
|
34
|
-
help;
|
|
35
|
-
}
|
|
36
|
-
async function parseArgv(argv = process.argv) {
|
|
37
|
-
const parser = yargs(helpers.hideBin(argv)).locale("en").scriptName("conventional-release").usage("Usage: $0 [options]").option("release-as", {
|
|
38
|
-
alias: "r",
|
|
39
|
-
describe: "Specify the release type (major|minor|patch)",
|
|
40
|
-
requiresArg: true,
|
|
41
|
-
string: true
|
|
42
|
-
}).option("prerelease", {
|
|
43
|
-
alias: "p",
|
|
44
|
-
describe: "Specify the prerelease type (alpha|beta|rc)",
|
|
45
|
-
requiresArg: true,
|
|
46
|
-
string: true
|
|
47
|
-
}).option("dry", {
|
|
48
|
-
type: "boolean",
|
|
49
|
-
default: DEFAULTS.dry,
|
|
50
|
-
describe: "See the commands that running release would run"
|
|
51
|
-
}).option("verbose", {
|
|
52
|
-
type: "boolean",
|
|
53
|
-
default: DEFAULTS.verbose,
|
|
54
|
-
describe: "Show detailed per-slice progress output"
|
|
55
|
-
}).option("tags", {
|
|
56
|
-
type: "boolean",
|
|
57
|
-
default: DEFAULTS.tags,
|
|
58
|
-
describe: "Show generated tags in the final output"
|
|
59
|
-
}).exitProcess(false).check((options) => {
|
|
60
|
-
if (!["alpha", "beta", "rc", void 0].includes(options.prerelease)) {
|
|
61
|
-
throw new Error("prerelease should be one of alpha, beta, rc or undefined");
|
|
62
|
-
}
|
|
63
|
-
return true;
|
|
64
|
-
}).showHelpOnFail(false).fail((message) => {
|
|
65
|
-
throw new Error(message);
|
|
66
|
-
}).alias("version", "v").alias("help", "h").example("$0", "Update changelog and tag release").example("$0 --dry --verbose", "Show a detailed dry-run release preview").pkgConf("release").wrap(97);
|
|
67
|
-
let parsed;
|
|
68
|
-
try {
|
|
69
|
-
parsed = await parser.parseAsync();
|
|
70
|
-
} catch (error) {
|
|
71
|
-
const failure = new CliParseError(error.message);
|
|
72
|
-
const help = await parser.getHelp();
|
|
73
|
-
failure.help = [help].flat().join("\n");
|
|
74
|
-
throw failure;
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
releaseAs: parsed.releaseAs,
|
|
78
|
-
prerelease: parsed.prerelease,
|
|
79
|
-
dry: parsed.dry,
|
|
80
|
-
verbose: parsed.verbose,
|
|
81
|
-
tags: parsed.tags
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
function createReporter({
|
|
85
|
-
output,
|
|
86
|
-
git,
|
|
87
|
-
showTags = false,
|
|
88
|
-
verbosity = "summary"
|
|
89
|
-
}) {
|
|
90
|
-
if (verbosity === "detailed") {
|
|
91
|
-
return new DetailedReporter({
|
|
92
|
-
output,
|
|
93
|
-
git,
|
|
94
|
-
showTags
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
return new SummaryReporter({
|
|
98
|
-
output,
|
|
99
|
-
git,
|
|
100
|
-
showTags
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
class SummaryReporter {
|
|
104
|
-
output;
|
|
105
|
-
git;
|
|
106
|
-
showTags;
|
|
107
|
-
scope = null;
|
|
108
|
-
position = /* @__PURE__ */ new Map();
|
|
109
|
-
constructor({
|
|
110
|
-
output,
|
|
111
|
-
git,
|
|
112
|
-
showTags
|
|
113
|
-
}) {
|
|
114
|
-
this.output = output;
|
|
115
|
-
this.git = git;
|
|
116
|
-
this.showTags = showTags;
|
|
117
|
-
}
|
|
118
|
-
async onStart(context) {
|
|
119
|
-
this.output.info(
|
|
120
|
-
context.dry ? "Starting dry release" : "Starting release"
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
async onScope(scope, context) {
|
|
124
|
-
this.scope = scope;
|
|
125
|
-
this.position = new Map(
|
|
126
|
-
scope.slices.map((slice, index2) => [slice.id, index2 + 1])
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
async onSliceStart(slice) {
|
|
130
|
-
this.output.info("Running slice %s", [this.describeProgress(slice)]);
|
|
131
|
-
}
|
|
132
|
-
async onSuccess(result) {
|
|
133
|
-
if (!result.changed) {
|
|
134
|
-
this.output.success("No changes since last release");
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const changed = result.slices.filter((slice) => slice.changed);
|
|
138
|
-
const primary = changed[0];
|
|
139
|
-
const packages = collectPackages(changed);
|
|
140
|
-
this.output.success("Release slices: %s", [String(changed.length)]);
|
|
141
|
-
this.output.success("Updated packages: %s", [String(packages.length)]);
|
|
142
|
-
if (primary) {
|
|
143
|
-
this.output.success("Next version: %s", [primary.nextVersion]);
|
|
144
|
-
}
|
|
145
|
-
if (result.dry) {
|
|
146
|
-
this.output.info("No committing or tagging since this was a dry run");
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
this.output.success("Committed %s staged files", [String(result.files.length)]);
|
|
150
|
-
if (this.showTags) {
|
|
151
|
-
const tags = collectTags(changed);
|
|
152
|
-
if (tags.length) {
|
|
153
|
-
this.output.success("Tags: %s", [tags.join(", ")]);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
this.output.info("Run `%s` to publish", [
|
|
157
|
-
`git push --follow-tags origin ${await this.resolveBranch()}`
|
|
158
|
-
]);
|
|
159
|
-
}
|
|
160
|
-
async onError(error) {
|
|
161
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
162
|
-
this.output.error(message);
|
|
163
|
-
}
|
|
164
|
-
describeProgress(slice) {
|
|
165
|
-
const position = this.position.get(slice.id);
|
|
166
|
-
const total = this.scope?.slices.length;
|
|
167
|
-
const label = describeSlice(slice);
|
|
168
|
-
return position && total ? `${position}/${total}: ${label}` : label;
|
|
169
|
-
}
|
|
170
|
-
async resolveBranch() {
|
|
171
|
-
try {
|
|
172
|
-
return await this.git.revParse("HEAD", { abbrevRef: true });
|
|
173
|
-
} catch {
|
|
174
|
-
return "%branch%";
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
class DetailedReporter extends SummaryReporter {
|
|
179
|
-
async onScope(scope, context) {
|
|
180
|
-
await super.onScope(scope, context);
|
|
181
|
-
this.output.info("%s scope: %s packages, %s affected, %s slices", [
|
|
182
|
-
scope.mode,
|
|
183
|
-
String(scope.packages.length),
|
|
184
|
-
String(scope.affected.length),
|
|
185
|
-
String(scope.slices.length)
|
|
186
|
-
]);
|
|
187
|
-
}
|
|
188
|
-
async onSliceSuccess(slice) {
|
|
189
|
-
if (!slice.changed) {
|
|
190
|
-
this.output.warn("Completed slice %s without version changes (%s)", [
|
|
191
|
-
describeSlice(slice),
|
|
192
|
-
slice.currentVersion
|
|
193
|
-
]);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
this.output.success("Completed slice %s: %s -> %s (%s)", [
|
|
197
|
-
describeSlice(slice),
|
|
198
|
-
slice.currentVersion,
|
|
199
|
-
slice.nextVersion,
|
|
200
|
-
slice.releaseType
|
|
201
|
-
]);
|
|
202
|
-
if (this.showTags && slice.tag) {
|
|
203
|
-
this.output.info("Tag: %s", [slice.tag]);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
async onSuccess(result) {
|
|
207
|
-
await super.onSuccess(result);
|
|
208
|
-
if (!result.changed) {
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
const packages = collectPackages(result.slices.filter((slice) => slice.changed));
|
|
212
|
-
this.output.info("Updated packages: %s", [describePackages(packages)]);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function describeSlice(slice) {
|
|
216
|
-
if (slice.partition) {
|
|
217
|
-
return `${slice.partition} [${describePackages(slice.packages)}]`;
|
|
218
|
-
}
|
|
219
|
-
return describePackages(slice.packages);
|
|
220
|
-
}
|
|
221
|
-
function describePackages(packages) {
|
|
222
|
-
return packages.map((pkg) => pkg.name ?? pkg.path).join(", ");
|
|
223
|
-
}
|
|
224
|
-
function collectTags(slices) {
|
|
225
|
-
return slices.map((slice) => slice.tag).filter((tag) => !!tag);
|
|
226
|
-
}
|
|
227
|
-
function collectPackages(slices) {
|
|
228
|
-
const packages = [];
|
|
229
|
-
const seen = /* @__PURE__ */ new Set();
|
|
230
|
-
for (const slice of slices) {
|
|
231
|
-
for (const pkg of slice.packages) {
|
|
232
|
-
const identity = pkg.name ?? pkg.path;
|
|
233
|
-
if (seen.has(identity)) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
seen.add(identity);
|
|
237
|
-
packages.push(pkg);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return packages;
|
|
241
|
-
}
|
|
242
|
-
class ConsoleOutput {
|
|
243
|
-
write(message) {
|
|
244
|
-
console.info(message);
|
|
245
|
-
}
|
|
246
|
-
writeError(message) {
|
|
247
|
-
console.error(message);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
class Output {
|
|
251
|
-
output;
|
|
252
|
-
theme;
|
|
253
|
-
constructor({
|
|
254
|
-
dry,
|
|
255
|
-
output = new ConsoleOutput(),
|
|
256
|
-
theme = createDefaultTheme(dry)
|
|
257
|
-
}) {
|
|
258
|
-
this.output = output;
|
|
259
|
-
this.theme = theme;
|
|
260
|
-
}
|
|
261
|
-
info(template, context = [], figure = this.theme.info) {
|
|
262
|
-
this.output.write(format(template, context, figure));
|
|
263
|
-
}
|
|
264
|
-
success(template, context = [], figure = this.theme.success) {
|
|
265
|
-
this.output.write(format(template, context, figure));
|
|
266
|
-
}
|
|
267
|
-
warn(template, context = [], figure = this.theme.warning) {
|
|
268
|
-
this.output.write(format(template, context, figure));
|
|
269
|
-
}
|
|
270
|
-
error(template, context = [], figure = this.theme.error) {
|
|
271
|
-
this.output.writeError(format(template, context, figure));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
function createDefaultTheme(dry) {
|
|
275
|
-
return {
|
|
276
|
-
success: dry ? chalk.yellow(figures.tick) : chalk.green(figures.tick),
|
|
277
|
-
warning: chalk.yellow(figures.warning),
|
|
278
|
-
error: chalk.red(figures.cross),
|
|
279
|
-
info: chalk.blue(figures.info)
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
function format(template, context, figure) {
|
|
283
|
-
const bold = (arg) => chalk.bold(arg);
|
|
284
|
-
const message = util__namespace.format(template, ...context.map(bold));
|
|
285
|
-
return `${figure} ${message}`;
|
|
286
|
-
}
|
|
2
|
+
require("../dist/_virtual/_rolldown/runtime.cjs");
|
|
3
|
+
const require_index = require("../dist/index.cjs");
|
|
4
|
+
const require_args = require("../dist/cli/args.cjs");
|
|
5
|
+
const require_reporter = require("../dist/cli/reporter.cjs");
|
|
6
|
+
const require_output = require("../dist/cli/output.cjs");
|
|
7
|
+
let _modulify_git_toolkit = require("@modulify/git-toolkit");
|
|
8
|
+
let _modulify_git_toolkit_shell = require("@modulify/git-toolkit/shell");
|
|
9
|
+
//#region src/cli.ts
|
|
287
10
|
async function main(argv = process.argv) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
const options = await require_args.parseArgv(argv);
|
|
13
|
+
const output = new require_output.Output({
|
|
14
|
+
dry: options.dry,
|
|
15
|
+
output: new require_output.ConsoleOutput()
|
|
16
|
+
});
|
|
17
|
+
const git = new _modulify_git_toolkit.GitCommander({ sh: new _modulify_git_toolkit_shell.Runner(cwd) });
|
|
18
|
+
await require_index.run({
|
|
19
|
+
cwd,
|
|
20
|
+
dry: options.dry,
|
|
21
|
+
releaseAs: options.releaseAs,
|
|
22
|
+
prerelease: options.prerelease,
|
|
23
|
+
reporter: require_reporter.createReporter({
|
|
24
|
+
output,
|
|
25
|
+
git,
|
|
26
|
+
showTags: options.tags,
|
|
27
|
+
verbosity: options.verbose ? "detailed" : "summary"
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
308
32
|
exports.main = main;
|