@wpelevator/wp-release 0.12.0 → 0.14.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 +150 -19
- package/dist/changeset-config.d.ts +91 -5
- package/dist/changeset-config.js +108 -11
- package/dist/changeset-dependents.d.ts +19 -8
- package/dist/changeset-dependents.js +96 -34
- package/dist/changeset-files.d.ts +6 -5
- package/dist/changeset-files.js +7 -5
- package/dist/changeset-packages.d.ts +55 -8
- package/dist/changeset-packages.js +85 -14
- package/dist/changeset-prompt.d.ts +119 -0
- package/dist/changeset-prompt.js +203 -0
- package/dist/changeset-release.d.ts +43 -5
- package/dist/changeset-release.js +99 -16
- package/dist/changesets.d.ts +6 -2
- package/dist/changesets.js +18 -12
- package/dist/cli.d.ts +7 -1
- package/dist/cli.js +51 -14
- package/dist/commands/args.d.ts +0 -21
- package/dist/commands/args.js +1 -35
- package/dist/commands/changeset-add.d.ts +5 -2
- package/dist/commands/changeset-add.js +109 -9
- package/dist/commands/changeset-args.d.ts +24 -5
- package/dist/commands/changeset-args.js +57 -14
- package/dist/commands/changeset-status.d.ts +3 -1
- package/dist/commands/changeset-status.js +2 -1
- package/dist/commands/changeset-tag.d.ts +1 -1
- package/dist/commands/changeset-tag.js +6 -3
- package/dist/commands/changeset-version.d.ts +7 -1
- package/dist/commands/changeset-version.js +17 -4
- package/dist/commands/checksums.js +2 -2
- package/dist/commands/command.d.ts +5 -0
- package/dist/commands/deploy-git.d.ts +24 -4
- package/dist/commands/deploy-git.js +57 -3
- package/dist/commands/deploy-svn.d.ts +24 -4
- package/dist/commands/deploy-svn.js +57 -2
- package/dist/commands/keygen.d.ts +3 -0
- package/dist/commands/keygen.js +6 -1
- package/dist/commands/package-deploys.d.ts +86 -0
- package/dist/commands/package-deploys.js +113 -0
- package/dist/commands/sign.d.ts +19 -1
- package/dist/commands/sign.js +47 -12
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.js +43 -24
- package/dist/commands/zip.d.ts +12 -8
- package/dist/commands/zip.js +135 -78
- package/dist/ed25519.d.ts +25 -0
- package/dist/ed25519.js +44 -0
- package/dist/file-selection.d.ts +21 -0
- package/dist/file-selection.js +35 -0
- package/dist/git-deploy.d.ts +32 -0
- package/dist/git-deploy.js +57 -1
- package/dist/git.d.ts +17 -0
- package/dist/git.js +23 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +7 -3
- package/dist/minisign.d.ts +41 -0
- package/dist/minisign.js +128 -0
- package/dist/package-deploy.d.ts +133 -0
- package/dist/package-deploy.js +323 -0
- package/dist/package-zip.d.ts +52 -0
- package/dist/package-zip.js +78 -0
- package/dist/release-repository.d.ts +18 -5
- package/dist/release-repository.js +51 -11
- package/dist/release-tags.d.ts +3 -1
- package/dist/release-tags.js +5 -2
- package/dist/signing.d.ts +64 -9
- package/dist/signing.js +113 -40
- package/dist/ssh-signature.d.ts +43 -0
- package/dist/ssh-signature.js +156 -0
- package/dist/summary.d.ts +3 -3
- package/dist/summary.js +2 -2
- package/dist/tag-format.d.ts +23 -14
- package/dist/tag-format.js +26 -40
- package/dist/version-files.d.ts +8 -8
- package/dist/version-files.js +22 -18
- package/dist/versions.d.ts +28 -3
- package/dist/versions.js +84 -9
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -43,6 +43,7 @@ const globalArgs = {
|
|
|
43
43
|
json: { type: 'boolean' },
|
|
44
44
|
'github-output': { type: 'boolean' },
|
|
45
45
|
config: { type: 'string' },
|
|
46
|
+
'tag-format': { type: 'string' },
|
|
46
47
|
};
|
|
47
48
|
const globalOptions = `Global options:
|
|
48
49
|
-h, --help Show help for the command
|
|
@@ -55,7 +56,32 @@ const globalOptions = `Global options:
|
|
|
55
56
|
--json Print the result as JSON
|
|
56
57
|
--github-output Set the result as JSON to the "result" output of
|
|
57
58
|
the GitHub Actions step
|
|
58
|
-
--config <path> Path to a JavaScript release config file
|
|
59
|
+
--config <path> Path to a JavaScript release config file
|
|
60
|
+
--tag-format <format>
|
|
61
|
+
Tag format with {name}, {path} and {version} that
|
|
62
|
+
replaces the tagFormat of the config, like
|
|
63
|
+
{path}/{version}`;
|
|
64
|
+
/**
|
|
65
|
+
* Get the config file and the tag format from the global options.
|
|
66
|
+
*
|
|
67
|
+
* @param values Parsed CLI values.
|
|
68
|
+
* @return The --config and --tag-format values.
|
|
69
|
+
* @throws {ValidationError} For an empty --config or --tag-format.
|
|
70
|
+
*/
|
|
71
|
+
function getConfigArgs(values) {
|
|
72
|
+
const configFile = getString(values, 'config');
|
|
73
|
+
const tagFormat = getString(values, 'tag-format');
|
|
74
|
+
if ('' === configFile?.trim()) {
|
|
75
|
+
throw new ValidationError('--config <path> must not be empty.');
|
|
76
|
+
}
|
|
77
|
+
if ('' === tagFormat?.trim()) {
|
|
78
|
+
throw new ValidationError('--tag-format <format> must not be empty.');
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
...(configFile ? { configFile } : {}),
|
|
82
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
59
85
|
/**
|
|
60
86
|
* Get the top-level help text listing all commands.
|
|
61
87
|
*
|
|
@@ -176,11 +202,11 @@ function getGitHubOutputFile(values) {
|
|
|
176
202
|
*
|
|
177
203
|
* @param command Command definition with options and run().
|
|
178
204
|
* @param args Arguments after the command name.
|
|
179
|
-
* @param io
|
|
205
|
+
* @param io Input and output streams and working directory.
|
|
180
206
|
* @return Exit code.
|
|
181
207
|
*/
|
|
182
208
|
async function runCommand(command, args, io) {
|
|
183
|
-
const { stdout, stderr, cwd } = io;
|
|
209
|
+
const { stdin, stdout, stderr, cwd } = io;
|
|
184
210
|
let logger = new Logger({ writer: new TextWriter({ stdout, stderr }) });
|
|
185
211
|
try {
|
|
186
212
|
const { values, positionals } = parseArgs({
|
|
@@ -188,22 +214,28 @@ async function runCommand(command, args, io) {
|
|
|
188
214
|
options: { ...globalArgs, ...command.options },
|
|
189
215
|
allowPositionals: true,
|
|
190
216
|
});
|
|
191
|
-
const configFile =
|
|
192
|
-
if ('' === configFile?.trim()) {
|
|
193
|
-
throw new ValidationError('--config <path> must not be empty.');
|
|
194
|
-
}
|
|
217
|
+
const { configFile, tagFormat } = getConfigArgs(values);
|
|
195
218
|
const githubOutputFile = getGitHubOutputFile(values);
|
|
196
219
|
logger = new Logger({
|
|
197
220
|
writer: createLogWriter(values, { stdout, stderr }),
|
|
198
221
|
quiet: getBoolean(values, 'quiet') || getBoolean(values, 'json'),
|
|
199
222
|
verbose: getBoolean(values, 'verbose'),
|
|
200
223
|
});
|
|
224
|
+
// Prompts need a person at a terminal, and would corrupt JSON output.
|
|
225
|
+
const interactive = true === stdin.isTTY &&
|
|
226
|
+
true === stdout.isTTY &&
|
|
227
|
+
!getBoolean(values, 'json') &&
|
|
228
|
+
!githubOutputFile;
|
|
201
229
|
const { exitCode, result } = await command.run({
|
|
202
230
|
values,
|
|
203
231
|
positionals,
|
|
204
232
|
cwd,
|
|
205
233
|
...(configFile ? { configFile } : {}),
|
|
234
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
206
235
|
logger,
|
|
236
|
+
...(interactive
|
|
237
|
+
? { terminal: { input: stdin, output: stdout } }
|
|
238
|
+
: {}),
|
|
207
239
|
});
|
|
208
240
|
if (getBoolean(values, 'json')) {
|
|
209
241
|
stdout.write(JSON.stringify(result, null, '\t') + '\n');
|
|
@@ -243,27 +275,27 @@ export async function execute(name, args, context) {
|
|
|
243
275
|
options: { ...globalArgs, ...command.options },
|
|
244
276
|
allowPositionals: true,
|
|
245
277
|
});
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
278
|
+
const configArgs = getConfigArgs(values);
|
|
279
|
+
const configFile = configArgs.configFile ?? context.configFile;
|
|
280
|
+
const { tagFormat } = configArgs;
|
|
250
281
|
// The command table is keyed by name, so the result matches CommandResults.
|
|
251
282
|
return command.run({
|
|
252
283
|
values,
|
|
253
284
|
positionals,
|
|
254
285
|
...context,
|
|
255
286
|
...(configFile ? { configFile } : {}),
|
|
287
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
256
288
|
});
|
|
257
289
|
}
|
|
258
290
|
/**
|
|
259
291
|
* Run the CLI.
|
|
260
292
|
*
|
|
261
293
|
* @param args CLI arguments without the node and script paths.
|
|
262
|
-
* @param io
|
|
294
|
+
* @param io Input and output streams and working directory.
|
|
263
295
|
* @return Exit code.
|
|
264
296
|
*/
|
|
265
297
|
export async function run(args, io = {}) {
|
|
266
|
-
const { stdout = process.stdout, stderr = process.stderr, cwd = process.cwd(), } = io;
|
|
298
|
+
const { stdin = process.stdin, stdout = process.stdout, stderr = process.stderr, cwd = process.cwd(), } = io;
|
|
267
299
|
const wantsHelp = args.includes('--help') || args.includes('-h');
|
|
268
300
|
const command = findCommand(args);
|
|
269
301
|
if (command && wantsHelp) {
|
|
@@ -271,7 +303,12 @@ export async function run(args, io = {}) {
|
|
|
271
303
|
return EXIT_SUCCESS;
|
|
272
304
|
}
|
|
273
305
|
if (command) {
|
|
274
|
-
return runCommand(command, args.slice(1), {
|
|
306
|
+
return runCommand(command, args.slice(1), {
|
|
307
|
+
stdin,
|
|
308
|
+
stdout,
|
|
309
|
+
stderr,
|
|
310
|
+
cwd,
|
|
311
|
+
});
|
|
275
312
|
}
|
|
276
313
|
if (0 === args.length || wantsHelp || 'help' === args[0]) {
|
|
277
314
|
stdout.write(getHelp() + '\n');
|
package/dist/commands/args.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ParseArgsOptionsConfig } from 'node:util';
|
|
2
|
-
import type { FileRules } from '../file-selection.ts';
|
|
3
2
|
import type { ParsedValues } from './command.ts';
|
|
4
3
|
/**
|
|
5
4
|
* Options shared by the commands that select files with .distignore and
|
|
@@ -40,26 +39,6 @@ export declare function checkDirectory(path: string): Promise<void>;
|
|
|
40
39
|
* @return Whether the path is a directory.
|
|
41
40
|
*/
|
|
42
41
|
export declare function isDirectory(path: string): Promise<boolean>;
|
|
43
|
-
/**
|
|
44
|
-
* Ignore a file that the command writes or reads, such as the checksums
|
|
45
|
-
* manifest, when it's inside the selected directory.
|
|
46
|
-
*
|
|
47
|
-
* @param rules Matcher from loadFileRules().
|
|
48
|
-
* @param sourceDir Selected directory.
|
|
49
|
-
* @param path Absolute file path.
|
|
50
|
-
* @return The same matcher.
|
|
51
|
-
*/
|
|
52
|
-
export declare function ignoreOwnFile(rules: FileRules, sourceDir: string, path: string): FileRules;
|
|
53
|
-
/**
|
|
54
|
-
* Ignore the signature file that "wp-release sign" writes next to a file the
|
|
55
|
-
* command writes, when it's inside the selected directory.
|
|
56
|
-
*
|
|
57
|
-
* @param rules Matcher from loadFileRules().
|
|
58
|
-
* @param sourceDir Selected directory.
|
|
59
|
-
* @param path Absolute path of the signed file.
|
|
60
|
-
* @return The same matcher.
|
|
61
|
-
*/
|
|
62
|
-
export declare function ignoreOwnSignature(rules: FileRules, sourceDir: string, path: string): FileRules;
|
|
63
42
|
/**
|
|
64
43
|
* Option for the source directory, which can also be the first positional
|
|
65
44
|
* argument.
|
package/dist/commands/args.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
3
|
import { ValidationError } from "../errors.js";
|
|
4
|
-
import { SIGNATURE_EXTENSION } from "../signing.js";
|
|
5
4
|
import { getBoolean, getString, getStrings } from "./command.js";
|
|
6
5
|
/**
|
|
7
6
|
* Options shared by the commands that select files with .distignore and
|
|
@@ -63,39 +62,6 @@ export async function isDirectory(path) {
|
|
|
63
62
|
const stats = await stat(path).catch(() => null);
|
|
64
63
|
return Boolean(stats?.isDirectory());
|
|
65
64
|
}
|
|
66
|
-
/**
|
|
67
|
-
* Ignore a file that the command writes or reads, such as the checksums
|
|
68
|
-
* manifest, when it's inside the selected directory.
|
|
69
|
-
*
|
|
70
|
-
* @param rules Matcher from loadFileRules().
|
|
71
|
-
* @param sourceDir Selected directory.
|
|
72
|
-
* @param path Absolute file path.
|
|
73
|
-
* @return The same matcher.
|
|
74
|
-
*/
|
|
75
|
-
export function ignoreOwnFile(rules, sourceDir, path) {
|
|
76
|
-
const relativePath = relative(sourceDir, path).split('\\').join('/');
|
|
77
|
-
const isInside = relativePath &&
|
|
78
|
-
'..' !== relativePath &&
|
|
79
|
-
!relativePath.startsWith('../') &&
|
|
80
|
-
!isAbsolute(relativePath);
|
|
81
|
-
if (isInside) {
|
|
82
|
-
// Escape the gitignore wildcards so that the pattern matches only this file.
|
|
83
|
-
rules.add(`/${relativePath.replace(/[\\*?[\]!#]/g, '\\$&')}`);
|
|
84
|
-
}
|
|
85
|
-
return rules;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Ignore the signature file that "wp-release sign" writes next to a file the
|
|
89
|
-
* command writes, when it's inside the selected directory.
|
|
90
|
-
*
|
|
91
|
-
* @param rules Matcher from loadFileRules().
|
|
92
|
-
* @param sourceDir Selected directory.
|
|
93
|
-
* @param path Absolute path of the signed file.
|
|
94
|
-
* @return The same matcher.
|
|
95
|
-
*/
|
|
96
|
-
export function ignoreOwnSignature(rules, sourceDir, path) {
|
|
97
|
-
return ignoreOwnFile(rules, sourceDir, path + SIGNATURE_EXTENSION);
|
|
98
|
-
}
|
|
99
65
|
/**
|
|
100
66
|
* Option for the source directory, which can also be the first positional
|
|
101
67
|
* argument.
|
|
@@ -22,15 +22,18 @@ declare const _default: {
|
|
|
22
22
|
readonly empty: {
|
|
23
23
|
readonly type: "boolean";
|
|
24
24
|
};
|
|
25
|
+
readonly since: {
|
|
26
|
+
readonly type: "string";
|
|
27
|
+
};
|
|
25
28
|
readonly name: {
|
|
26
29
|
readonly type: "string";
|
|
27
30
|
};
|
|
28
31
|
};
|
|
29
|
-
run({ values, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<{
|
|
32
|
+
run({ values, cwd, configFile, tagFormat, logger, terminal }: import("./command.ts").CommandContext): Promise<{
|
|
30
33
|
exitCode: number;
|
|
31
34
|
result: {
|
|
32
35
|
file: string;
|
|
33
|
-
packages:
|
|
36
|
+
packages: any;
|
|
34
37
|
};
|
|
35
38
|
}>;
|
|
36
39
|
};
|
|
@@ -1,34 +1,134 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolvePackageNames } from "../changeset-packages.js";
|
|
2
|
+
import { ChangesetPrompt } from "../changeset-prompt.js";
|
|
3
|
+
import { ChangesetRelease } from "../changeset-release.js";
|
|
4
|
+
import { ValidationError } from "../errors.js";
|
|
2
5
|
import { parseChangesetAddArgs, changesetAddArgs } from "./changeset-args.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get the bumps of the changed packages that have no changeset yet.
|
|
8
|
+
*
|
|
9
|
+
* @param release Changeset release of the repository.
|
|
10
|
+
* @param input Parsed options with the base ref.
|
|
11
|
+
* @param bump Bump of the changed packages.
|
|
12
|
+
* @return Bump of each changed package.
|
|
13
|
+
* @throws {ValidationError} If no changed package needs a changeset.
|
|
14
|
+
*/
|
|
15
|
+
async function getChangedPackageBumps(release, input, bump) {
|
|
16
|
+
const status = await release.getStatus({
|
|
17
|
+
...(input.since ? { since: input.since } : {}),
|
|
18
|
+
uncommitted: true,
|
|
19
|
+
});
|
|
20
|
+
if (!status.missingChangesets.length) {
|
|
21
|
+
const since = status.since ? ` since ${status.since}` : '';
|
|
22
|
+
throw new ValidationError(`No package changed${since} without a changeset. Pass --package <name> to choose the packages.`);
|
|
23
|
+
}
|
|
24
|
+
return Object.fromEntries(status.missingChangesets.map((name) => [name, bump]));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Ask for the changeset, preselecting the changed packages without one.
|
|
28
|
+
*
|
|
29
|
+
* @param release Changeset release of the repository.
|
|
30
|
+
* @param input Parsed options with the base ref and release note.
|
|
31
|
+
* @param terminal Terminal to ask in.
|
|
32
|
+
* @return Package bumps and release note.
|
|
33
|
+
*/
|
|
34
|
+
async function askChangeset(release, input, terminal) {
|
|
35
|
+
const packages = await release.getPackages();
|
|
36
|
+
const status = await release.getStatus({
|
|
37
|
+
...(input.since ? { since: input.since } : {}),
|
|
38
|
+
uncommitted: true,
|
|
39
|
+
});
|
|
40
|
+
const prompt = new ChangesetPrompt(terminal);
|
|
41
|
+
try {
|
|
42
|
+
return await prompt.ask({
|
|
43
|
+
packages,
|
|
44
|
+
changed: status.missingChangesets,
|
|
45
|
+
...(status.since ? { since: status.since } : {}),
|
|
46
|
+
...(input.summary ? { summary: input.summary } : {}),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
prompt.close();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the package bumps and release note from the options, the changed
|
|
55
|
+
* packages or the prompts.
|
|
56
|
+
*
|
|
57
|
+
* @param release Changeset release of the repository.
|
|
58
|
+
* @param input Parsed options.
|
|
59
|
+
* @param terminal Terminal to ask in, when the CLI runs in one.
|
|
60
|
+
* @return Package bumps, empty for no release, and release note.
|
|
61
|
+
* @throws {ValidationError} Without packages outside a terminal.
|
|
62
|
+
*/
|
|
63
|
+
async function getChangeset(release, input, terminal) {
|
|
64
|
+
const summary = input.summary ?? '';
|
|
65
|
+
let changeset;
|
|
66
|
+
if (input.empty) {
|
|
67
|
+
changeset = { packages: {}, summary };
|
|
68
|
+
}
|
|
69
|
+
else if (Object.keys(input.packages).length) {
|
|
70
|
+
changeset = { packages: input.packages, summary };
|
|
71
|
+
}
|
|
72
|
+
else if (input.bump) {
|
|
73
|
+
changeset = {
|
|
74
|
+
packages: await getChangedPackageBumps(release, input, input.bump),
|
|
75
|
+
summary,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (terminal) {
|
|
79
|
+
changeset = await askChangeset(release, input, terminal);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
throw new ValidationError('Pass matching repeated --package <name> and --bump <patch|minor|major> options, a single --bump for the changed packages, or --empty.');
|
|
83
|
+
}
|
|
84
|
+
return changeset;
|
|
85
|
+
}
|
|
3
86
|
export default {
|
|
4
87
|
name: 'changeset-add',
|
|
5
|
-
usage: 'changeset-add
|
|
88
|
+
usage: 'changeset-add [--empty | --package <name> --bump <bump> ... | --bump <bump>] [options]',
|
|
6
89
|
summary: 'Add a package release note for the next release PR.',
|
|
7
90
|
help: `Options:
|
|
8
|
-
--package <name> Package identifier
|
|
91
|
+
--package <name> Package identifier, or its path under the configured
|
|
92
|
+
root (repeatable)
|
|
9
93
|
--bump <bump> patch, minor or major (repeatable, in package order)
|
|
10
94
|
--summary <text> Customer-facing release note
|
|
11
95
|
--empty Record that this change does not need a release
|
|
96
|
+
--since <ref> Compare changed files with this git ref (default:
|
|
97
|
+
<remote>/<baseBranch>)
|
|
12
98
|
--name <name> Changeset filename (default: random identifier)
|
|
13
99
|
|
|
14
100
|
Use --package and --bump pairs for package releases, or --empty for changes
|
|
15
|
-
that should pass the status check without producing a release
|
|
101
|
+
that should pass the status check without producing a release. A single
|
|
102
|
+
--bump without --package releases the packages that changed since the base
|
|
103
|
+
ref, including uncommitted files, and have no changeset yet. Without
|
|
104
|
+
--package and --bump in a terminal, the command asks for the packages, with
|
|
105
|
+
the changed ones preselected, their bumps and the release note.`,
|
|
16
106
|
options: changesetAddArgs,
|
|
17
|
-
async run({ values, cwd, configFile, logger }) {
|
|
107
|
+
async run({ values, cwd, configFile, tagFormat, logger, terminal }) {
|
|
18
108
|
const input = parseChangesetAddArgs(values);
|
|
19
|
-
const
|
|
109
|
+
const release = new ChangesetRelease({
|
|
20
110
|
cwd,
|
|
21
111
|
...(configFile ? { configFile } : {}),
|
|
22
|
-
|
|
23
|
-
|
|
112
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
113
|
+
});
|
|
114
|
+
const changeset = await getChangeset(release, input, terminal);
|
|
115
|
+
// Adding resolves the package paths and rejects a package passed twice.
|
|
116
|
+
const file = await release.add({
|
|
117
|
+
packages: changeset.packages,
|
|
118
|
+
summary: changeset.summary,
|
|
24
119
|
...(input.name ? { name: input.name } : {}),
|
|
25
120
|
});
|
|
121
|
+
const names = resolvePackageNames(await release.getPackages(), Object.keys(changeset.packages));
|
|
122
|
+
const packages = Object.fromEntries(Object.values(changeset.packages).map((bump, index) => [
|
|
123
|
+
names[index],
|
|
124
|
+
bump,
|
|
125
|
+
]));
|
|
26
126
|
logger.info(`Created ${file}.`);
|
|
27
127
|
return {
|
|
28
128
|
exitCode: 0,
|
|
29
129
|
result: {
|
|
30
130
|
file,
|
|
31
|
-
packages
|
|
131
|
+
packages,
|
|
32
132
|
},
|
|
33
133
|
};
|
|
34
134
|
},
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import type { ChangesetPackages } from '../changesets.ts';
|
|
1
|
+
import type { ChangesetBump, ChangesetPackages } from '../changesets.ts';
|
|
2
2
|
import type { ParsedValues } from './command.ts';
|
|
3
3
|
/** Parsed input for `changeset-add`. */
|
|
4
4
|
export interface ChangesetAddArgs {
|
|
5
|
+
/** Bump of each package passed with --package, by identifier or path. */
|
|
5
6
|
packages: ChangesetPackages;
|
|
6
|
-
|
|
7
|
+
/** Bump for the changed packages without a changeset, passed without --package. */
|
|
8
|
+
bump?: ChangesetBump;
|
|
9
|
+
summary?: string;
|
|
7
10
|
empty: boolean;
|
|
11
|
+
since?: string;
|
|
8
12
|
name?: string;
|
|
9
13
|
}
|
|
10
14
|
/** Parse-args options for `changeset-add`. */
|
|
@@ -23,16 +27,21 @@ export declare const changesetAddArgs: {
|
|
|
23
27
|
readonly empty: {
|
|
24
28
|
readonly type: "boolean";
|
|
25
29
|
};
|
|
30
|
+
readonly since: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
};
|
|
26
33
|
readonly name: {
|
|
27
34
|
readonly type: "string";
|
|
28
35
|
};
|
|
29
36
|
};
|
|
30
37
|
/**
|
|
31
|
-
* Parse and validate the `changeset-add` options.
|
|
38
|
+
* Parse and validate the `changeset-add` options. Without --package, a
|
|
39
|
+
* single --bump applies to the changed packages without a changeset, and
|
|
40
|
+
* without --bump either the command asks for the changeset in a terminal.
|
|
32
41
|
*
|
|
33
42
|
* @param values Parsed CLI options.
|
|
34
43
|
* @return Normalized changeset input.
|
|
35
|
-
* @throws {ValidationError} If
|
|
44
|
+
* @throws {ValidationError} If options are conflicting or incomplete.
|
|
36
45
|
*/
|
|
37
46
|
export declare function parseChangesetAddArgs(values: ParsedValues): ChangesetAddArgs;
|
|
38
47
|
/** Parse-args options for `changeset-status`. */
|
|
@@ -68,6 +77,12 @@ export declare const changesetVersionArgs: {
|
|
|
68
77
|
readonly 'no-dependents': {
|
|
69
78
|
readonly type: "boolean";
|
|
70
79
|
};
|
|
80
|
+
readonly prerelease: {
|
|
81
|
+
readonly type: "string";
|
|
82
|
+
};
|
|
83
|
+
readonly snapshot: {
|
|
84
|
+
readonly type: "string";
|
|
85
|
+
};
|
|
71
86
|
};
|
|
72
87
|
/** Parsed input for `changeset-version`. */
|
|
73
88
|
export interface ChangesetVersionArgs {
|
|
@@ -76,6 +91,10 @@ export interface ChangesetVersionArgs {
|
|
|
76
91
|
packages?: string[];
|
|
77
92
|
/** Whether the selected sources release their configured dependents; defaults to true. */
|
|
78
93
|
dependents: boolean;
|
|
94
|
+
/** Pre-release identifier with an optional number, like rc or rc.1. */
|
|
95
|
+
prerelease?: string;
|
|
96
|
+
/** Snapshot identifier, like pr-251. */
|
|
97
|
+
snapshot?: string;
|
|
79
98
|
}
|
|
80
99
|
/**
|
|
81
100
|
* Validate and normalize a release date option.
|
|
@@ -90,7 +109,7 @@ export declare function parseChangesetDate(value: string | undefined): string |
|
|
|
90
109
|
*
|
|
91
110
|
* @param values Parsed CLI options.
|
|
92
111
|
* @return Normalized versioning input.
|
|
93
|
-
* @throws {ValidationError} If --date is invalid.
|
|
112
|
+
* @throws {ValidationError} If --date is invalid, or an option is empty or conflicting.
|
|
94
113
|
*/
|
|
95
114
|
export declare function parseChangesetVersionArgs(values: ParsedValues): ChangesetVersionArgs;
|
|
96
115
|
/** Parse-args options for `changeset-tag`. */
|
|
@@ -7,48 +7,76 @@ export const changesetAddArgs = {
|
|
|
7
7
|
bump: { type: 'string', multiple: true },
|
|
8
8
|
summary: { type: 'string' },
|
|
9
9
|
empty: { type: 'boolean' },
|
|
10
|
+
since: { type: 'string' },
|
|
10
11
|
name: { type: 'string' },
|
|
11
12
|
};
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
|
+
* Check that a bump is patch, minor or major.
|
|
15
|
+
*
|
|
16
|
+
* @param bump Bump option value.
|
|
17
|
+
* @param packageName Package of the bump, for the error.
|
|
18
|
+
* @return The bump.
|
|
19
|
+
* @throws {ValidationError} For another value.
|
|
20
|
+
*/
|
|
21
|
+
function parseBump(bump, packageName) {
|
|
22
|
+
if (!CHANGESET_BUMPS.includes(bump)) {
|
|
23
|
+
const target = packageName ? ` for package "${packageName}"` : '';
|
|
24
|
+
throw new ValidationError(`Invalid bump "${bump}"${target}; expected patch, minor or major.`);
|
|
25
|
+
}
|
|
26
|
+
return bump;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Parse and validate the `changeset-add` options. Without --package, a
|
|
30
|
+
* single --bump applies to the changed packages without a changeset, and
|
|
31
|
+
* without --bump either the command asks for the changeset in a terminal.
|
|
14
32
|
*
|
|
15
33
|
* @param values Parsed CLI options.
|
|
16
34
|
* @return Normalized changeset input.
|
|
17
|
-
* @throws {ValidationError} If
|
|
35
|
+
* @throws {ValidationError} If options are conflicting or incomplete.
|
|
18
36
|
*/
|
|
19
37
|
export function parseChangesetAddArgs(values) {
|
|
20
38
|
const names = getStrings(values, 'package');
|
|
21
39
|
const bumps = getStrings(values, 'bump');
|
|
22
40
|
const summary = getString(values, 'summary')?.trim();
|
|
23
41
|
const empty = getBoolean(values, 'empty');
|
|
42
|
+
const since = getString(values, 'since');
|
|
24
43
|
const name = getString(values, 'name');
|
|
25
|
-
if (empty && (names.length || bumps.length || summary)) {
|
|
26
|
-
throw new ValidationError('--empty cannot be combined with --package, --bump or --
|
|
44
|
+
if (empty && (names.length || bumps.length || summary || since)) {
|
|
45
|
+
throw new ValidationError('--empty cannot be combined with --package, --bump, --summary or --since.');
|
|
27
46
|
}
|
|
28
|
-
if (
|
|
47
|
+
if (names.length && names.length !== bumps.length) {
|
|
29
48
|
throw new ValidationError('Pass matching repeated --package <name> and --bump <patch|minor|major> options, or use --empty.');
|
|
30
49
|
}
|
|
31
|
-
if (!
|
|
32
|
-
throw new ValidationError('--
|
|
50
|
+
if (!names.length && bumps.length > 1) {
|
|
51
|
+
throw new ValidationError('Pass a single --bump without --package to release the changed packages, or pair each --bump with a --package.');
|
|
52
|
+
}
|
|
53
|
+
if (names.length && undefined !== since) {
|
|
54
|
+
throw new ValidationError('--since selects the changed packages, so it cannot be combined with --package.');
|
|
55
|
+
}
|
|
56
|
+
if ('' === since?.trim()) {
|
|
57
|
+
throw new ValidationError('--since <ref> must not be empty.');
|
|
58
|
+
}
|
|
59
|
+
if (bumps.length && !summary) {
|
|
60
|
+
throw new ValidationError('--summary <text> is required with --bump.');
|
|
33
61
|
}
|
|
34
62
|
if (name && !/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(name)) {
|
|
35
63
|
throw new ValidationError('--name must contain only letters, numbers, dots, underscores or hyphens.');
|
|
36
64
|
}
|
|
37
65
|
const packages = {};
|
|
38
66
|
names.forEach((packageName, index) => {
|
|
39
|
-
const bump = bumps[index];
|
|
40
67
|
if (Object.hasOwn(packages, packageName)) {
|
|
41
68
|
throw new ValidationError(`Package "${packageName}" was passed more than once.`);
|
|
42
69
|
}
|
|
43
|
-
|
|
44
|
-
throw new ValidationError(`Invalid bump "${bump}" for package "${packageName}"; expected patch, minor or major.`);
|
|
45
|
-
}
|
|
46
|
-
packages[packageName] = bump;
|
|
70
|
+
packages[packageName] = parseBump(bumps[index] ?? '', packageName);
|
|
47
71
|
});
|
|
48
72
|
return {
|
|
49
73
|
packages,
|
|
50
|
-
summary: summary ?? '',
|
|
51
74
|
empty,
|
|
75
|
+
...(!names.length && bumps.length
|
|
76
|
+
? { bump: parseBump(bumps[0] ?? '') }
|
|
77
|
+
: {}),
|
|
78
|
+
...(summary ? { summary } : {}),
|
|
79
|
+
...(since ? { since } : {}),
|
|
52
80
|
...(name ? { name } : {}),
|
|
53
81
|
};
|
|
54
82
|
}
|
|
@@ -76,6 +104,8 @@ export const changesetVersionArgs = {
|
|
|
76
104
|
date: { type: 'string' },
|
|
77
105
|
package: { type: 'string', multiple: true },
|
|
78
106
|
'no-dependents': { type: 'boolean' },
|
|
107
|
+
prerelease: { type: 'string' },
|
|
108
|
+
snapshot: { type: 'string' },
|
|
79
109
|
};
|
|
80
110
|
/**
|
|
81
111
|
* Validate and normalize a release date option.
|
|
@@ -102,19 +132,32 @@ export function parseChangesetDate(value) {
|
|
|
102
132
|
*
|
|
103
133
|
* @param values Parsed CLI options.
|
|
104
134
|
* @return Normalized versioning input.
|
|
105
|
-
* @throws {ValidationError} If --date is invalid.
|
|
135
|
+
* @throws {ValidationError} If --date is invalid, or an option is empty or conflicting.
|
|
106
136
|
*/
|
|
107
137
|
export function parseChangesetVersionArgs(values) {
|
|
108
138
|
const date = parseChangesetDate(getString(values, 'date'));
|
|
109
139
|
const packages = getStrings(values, 'package');
|
|
140
|
+
const prerelease = getString(values, 'prerelease')?.trim();
|
|
110
141
|
if (packages.some((name) => !name.trim())) {
|
|
111
142
|
throw new ValidationError('--package <name> must not be empty.');
|
|
112
143
|
}
|
|
144
|
+
const snapshot = getString(values, 'snapshot')?.trim();
|
|
145
|
+
if ('' === prerelease) {
|
|
146
|
+
throw new ValidationError('--prerelease <id> must not be empty.');
|
|
147
|
+
}
|
|
148
|
+
if ('' === snapshot) {
|
|
149
|
+
throw new ValidationError('--snapshot <id> must not be empty.');
|
|
150
|
+
}
|
|
151
|
+
if (prerelease && snapshot) {
|
|
152
|
+
throw new ValidationError('--prerelease cannot be combined with --snapshot.');
|
|
153
|
+
}
|
|
113
154
|
return {
|
|
114
155
|
dryRun: getBoolean(values, 'dry-run'),
|
|
115
156
|
dependents: !getBoolean(values, 'no-dependents'),
|
|
116
157
|
...(date ? { date } : {}),
|
|
117
158
|
...(packages.length ? { packages } : {}),
|
|
159
|
+
...(prerelease ? { prerelease } : {}),
|
|
160
|
+
...(snapshot ? { snapshot } : {}),
|
|
118
161
|
};
|
|
119
162
|
}
|
|
120
163
|
/** Parse-args options for `changeset-tag`. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface ChangesetStatusResult {
|
|
2
|
+
since?: string;
|
|
2
3
|
changesets: Array<{
|
|
3
4
|
fileName: string;
|
|
4
5
|
packages: Record<string, string>;
|
|
@@ -25,7 +26,7 @@ declare const _default: {
|
|
|
25
26
|
readonly type: "string";
|
|
26
27
|
};
|
|
27
28
|
};
|
|
28
|
-
run({ values, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<{
|
|
29
|
+
run({ values, cwd, configFile, tagFormat, logger }: import("./command.ts").CommandContext): Promise<{
|
|
29
30
|
exitCode: number;
|
|
30
31
|
result: {
|
|
31
32
|
releases: {
|
|
@@ -34,6 +35,7 @@ declare const _default: {
|
|
|
34
35
|
currentVersion: string;
|
|
35
36
|
nextVersion: string;
|
|
36
37
|
}[];
|
|
38
|
+
since?: string;
|
|
37
39
|
changesets: import("../changesets.ts").Changeset[];
|
|
38
40
|
changedPackages: string[];
|
|
39
41
|
changedPackageFiles: Record<string, string[]>;
|
|
@@ -10,11 +10,12 @@ export default {
|
|
|
10
10
|
Exit status is 2 when a changed package has no changeset added since the comparison base.
|
|
11
11
|
An empty changeset added since the comparison base counts as an explicit no-release decision.`,
|
|
12
12
|
options: changesetStatusArgs,
|
|
13
|
-
async run({ values, cwd, configFile, logger }) {
|
|
13
|
+
async run({ values, cwd, configFile, tagFormat, logger }) {
|
|
14
14
|
const { since } = parseChangesetStatusArgs(values);
|
|
15
15
|
const result = await getChangesetStatus({
|
|
16
16
|
cwd,
|
|
17
17
|
...(configFile ? { configFile } : {}),
|
|
18
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
18
19
|
...(since ? { since } : {}),
|
|
19
20
|
});
|
|
20
21
|
logger.summary(() => {
|
|
@@ -24,7 +24,7 @@ declare const _default: {
|
|
|
24
24
|
readonly type: "string";
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
-
run({ values, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<{
|
|
27
|
+
run({ values, cwd, configFile, tagFormat, logger }: import("./command.ts").CommandContext): Promise<{
|
|
28
28
|
exitCode: number;
|
|
29
29
|
result: import("../changeset-release.ts").ChangesetTagResult;
|
|
30
30
|
}>;
|
|
@@ -10,10 +10,12 @@ export default {
|
|
|
10
10
|
creating them
|
|
11
11
|
--untagged List only releases without a tag, without creating tags
|
|
12
12
|
--since <ref> Tag packages changed since this git ref
|
|
13
|
-
--package <name> Tag only this package
|
|
13
|
+
--package <name> Tag only this package, by identifier or path under
|
|
14
|
+
the configured root (repeatable)
|
|
14
15
|
--version <version> Fail unless the single --package is at this version;
|
|
15
16
|
the version to tag for packages versioned by git tags
|
|
16
|
-
--push Push newly created tags to
|
|
17
|
+
--push Push newly created tags to the configured remote
|
|
18
|
+
(default: origin), one at a time
|
|
17
19
|
|
|
18
20
|
Tags are created only when absent, so rerunning after a partial failure is
|
|
19
21
|
safe. Before a tag is created, every configured version target must match the
|
|
@@ -22,11 +24,12 @@ and pre-release flag of its release, for a workflow step that creates the
|
|
|
22
24
|
GitHub Release. --untagged lists only the releases that still need a tag, for
|
|
23
25
|
example to release each of them with a separate workflow.`,
|
|
24
26
|
options: changesetTagArgs,
|
|
25
|
-
async run({ values, cwd, configFile, logger }) {
|
|
27
|
+
async run({ values, cwd, configFile, tagFormat, logger }) {
|
|
26
28
|
const args = parseChangesetTagArgs(values);
|
|
27
29
|
const result = await tagChangesets({
|
|
28
30
|
cwd,
|
|
29
31
|
...(configFile ? { configFile } : {}),
|
|
32
|
+
...(tagFormat ? { tagFormat } : {}),
|
|
30
33
|
...args,
|
|
31
34
|
});
|
|
32
35
|
result.tags.forEach((item) => {
|