@wpelevator/wp-release 0.6.0 → 0.7.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 +73 -9
- package/dist/changeset-config.d.ts +22 -0
- package/dist/changeset-config.js +4 -0
- package/dist/changeset-release.d.ts +19 -1
- package/dist/changeset-release.js +284 -30
- package/dist/changesets.d.ts +7 -0
- package/dist/changesets.js +13 -0
- package/dist/cli.js +26 -0
- package/dist/commands/changeset-args.d.ts +18 -0
- package/dist/commands/changeset-args.js +24 -0
- package/dist/commands/changeset-tag.d.ts +10 -0
- package/dist/commands/changeset-tag.js +26 -6
- package/dist/commands/changeset-version.d.ts +7 -0
- package/dist/commands/changeset-version.js +9 -2
- package/dist/commands/deploy-git.d.ts +45 -0
- package/dist/commands/deploy-git.js +81 -0
- package/dist/git-deploy.d.ts +218 -0
- package/dist/git-deploy.js +420 -0
- package/dist/git.d.ts +49 -0
- package/dist/git.js +65 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.js +5 -2
- package/dist/summary.d.ts +51 -0
- package/dist/summary.js +78 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import { promisify } from 'node:util';
|
|
|
6
6
|
import { loadChangesetConfig } from "./changeset-config.js";
|
|
7
7
|
import { findMainPluginFile } from "./package-meta.js";
|
|
8
8
|
import { findConstantValues } from "./version-check.js";
|
|
9
|
-
import { getChangesetBump, getNextVersion, parseChangeset, } from "./changesets.js";
|
|
9
|
+
import { formatChangelogEntries, formatChangeset, getChangesetBump, getNextVersion, parseChangeset, } from "./changesets.js";
|
|
10
10
|
import { ValidationError } from "./errors.js";
|
|
11
11
|
const execFileAsync = promisify(execFile);
|
|
12
12
|
/**
|
|
@@ -19,6 +19,18 @@ export async function getChangesetReleasePlan(input) {
|
|
|
19
19
|
const context = await getReleaseContext(input);
|
|
20
20
|
return context?.plan;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Discover the packages of the repository configuration.
|
|
24
|
+
*
|
|
25
|
+
* @param input Repository root and optional explicit config file.
|
|
26
|
+
* @return Packages in the configured order with their current versions.
|
|
27
|
+
* @throws {ValidationError} Without a config, or for an invalid one.
|
|
28
|
+
*/
|
|
29
|
+
export async function getChangesetPackages(input) {
|
|
30
|
+
const loaded = await requireConfig(input);
|
|
31
|
+
const packages = await discoverPackages(input.cwd, loaded.config);
|
|
32
|
+
return packages.map(toChangesetPackage);
|
|
33
|
+
}
|
|
22
34
|
async function getReleaseContext(input) {
|
|
23
35
|
const loaded = await loadChangesetConfig(input);
|
|
24
36
|
if (!loaded) {
|
|
@@ -110,7 +122,10 @@ export async function versionChangesets(input) {
|
|
|
110
122
|
if (!context) {
|
|
111
123
|
throw new ValidationError('No wp-release config found. Add wp-release.config.js or pass --config <path>.');
|
|
112
124
|
}
|
|
113
|
-
const { config, packages
|
|
125
|
+
const { config, packages } = context;
|
|
126
|
+
const releasePlan = input.packages
|
|
127
|
+
? selectReleasePlan(context.plan, packages, input.packages)
|
|
128
|
+
: context.plan;
|
|
114
129
|
const date = input.date ?? new Date().toISOString().slice(0, 10);
|
|
115
130
|
if (!input.dryRun) {
|
|
116
131
|
const journal = new Map();
|
|
@@ -128,9 +143,7 @@ export async function versionChangesets(input) {
|
|
|
128
143
|
}
|
|
129
144
|
const changesetDirectory = path.resolve(input.cwd, config.changesets ?? '.changeset');
|
|
130
145
|
for (const changeset of releasePlan.changesets) {
|
|
131
|
-
|
|
132
|
-
await rememberFile(journal, file);
|
|
133
|
-
await rm(file);
|
|
146
|
+
await consumeChangeset(path.join(changesetDirectory, changeset.fileName), changeset, input.packages, journal);
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
149
|
catch (error) {
|
|
@@ -147,6 +160,48 @@ export async function versionChangesets(input) {
|
|
|
147
160
|
changed: !input.dryRun && releasePlan.changesets.length > 0,
|
|
148
161
|
};
|
|
149
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Limit a release plan to some packages and the changesets that name them.
|
|
165
|
+
*
|
|
166
|
+
* @param plan Release plan of all pending changesets.
|
|
167
|
+
* @param packages Configured packages.
|
|
168
|
+
* @param names Packages to version.
|
|
169
|
+
* @return Release plan of the named packages.
|
|
170
|
+
* @throws {ValidationError} For unknown packages.
|
|
171
|
+
*/
|
|
172
|
+
function selectReleasePlan(plan, packages, names) {
|
|
173
|
+
names.forEach((name) => {
|
|
174
|
+
if (!packages.some((pkg) => pkg.name === name)) {
|
|
175
|
+
throw new ValidationError(`Unknown package "${name}".`);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
releases: plan.releases.filter((release) => names.includes(release.package.name)),
|
|
180
|
+
changesets: plan.changesets.filter((changeset) => names.some((name) => Object.hasOwn(changeset.packages, name))),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Remove an applied changeset, or only the applied packages from a changeset
|
|
185
|
+
* that names other packages too, so that their releases keep its summary.
|
|
186
|
+
*
|
|
187
|
+
* @param file Changeset file.
|
|
188
|
+
* @param changeset Parsed changeset.
|
|
189
|
+
* @param applied Versioned packages, or undefined when all were versioned.
|
|
190
|
+
* @param journal File journal for rolling back.
|
|
191
|
+
*/
|
|
192
|
+
async function consumeChangeset(file, changeset, applied, journal) {
|
|
193
|
+
const remaining = Object.entries(changeset.packages).filter(([name]) => undefined !== applied && !applied.includes(name));
|
|
194
|
+
await rememberFile(journal, file);
|
|
195
|
+
if (remaining.length) {
|
|
196
|
+
await writeFile(file, formatChangeset({
|
|
197
|
+
packages: Object.fromEntries(remaining),
|
|
198
|
+
summary: changeset.summary,
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
await rm(file);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
150
205
|
/**
|
|
151
206
|
* Create missing release tags, optionally pushing them to the configured remote.
|
|
152
207
|
*
|
|
@@ -157,49 +212,64 @@ export async function tagChangesets(input) {
|
|
|
157
212
|
if (input.githubRelease && !input.push) {
|
|
158
213
|
throw new ValidationError('GitHub Releases require pushing the tags.');
|
|
159
214
|
}
|
|
215
|
+
if (input.untagged && input.push) {
|
|
216
|
+
throw new ValidationError('Listing untagged releases cannot be combined with pushing tags.');
|
|
217
|
+
}
|
|
160
218
|
const context = await getReleaseContext(input);
|
|
161
219
|
if (!context) {
|
|
162
220
|
throw new ValidationError('No wp-release config found. Add wp-release.config.js or pass --config <path>.');
|
|
163
221
|
}
|
|
222
|
+
const selected = selectPackages(context.packages, input);
|
|
164
223
|
const packages = input.since
|
|
165
|
-
? (await changedPackagesSince(input.cwd,
|
|
166
|
-
|
|
224
|
+
? (await changedPackagesSince(input.cwd, selected, input.since))
|
|
225
|
+
.packages
|
|
226
|
+
: selected;
|
|
167
227
|
const results = [];
|
|
228
|
+
const dryRun = input.dryRun || input.untagged;
|
|
168
229
|
const tagFormat = context.config.tagFormat ?? '{path}/{version}';
|
|
169
230
|
for (const pkg of packages) {
|
|
170
|
-
const version =
|
|
171
|
-
|
|
172
|
-
|
|
231
|
+
const version = await getTagVersion(pkg, input.version);
|
|
232
|
+
if (undefined !== input.version && input.version !== version) {
|
|
233
|
+
throw new ValidationError(`Package "${pkg.name}" is at version ${version}, not ${input.version}.`);
|
|
234
|
+
}
|
|
173
235
|
const tag = formatTag(tagFormat, pkg, version);
|
|
174
236
|
const exists = await gitTagExists(input.cwd, tag);
|
|
175
|
-
|
|
176
|
-
|
|
237
|
+
if (!exists) {
|
|
238
|
+
await verifyVersionTargets(input.cwd, pkg, version);
|
|
239
|
+
}
|
|
240
|
+
const previousTag = await getPreviousTag(input.cwd, formatTag(tagFormat, pkg, ''), version);
|
|
241
|
+
const notes = await getReleaseNotes(input.cwd, context.config, pkg, { version, tag, previousTag }, exists ? tag : 'HEAD');
|
|
242
|
+
if (!exists && !dryRun) {
|
|
177
243
|
await git(input.cwd, ['tag', tag]);
|
|
178
244
|
}
|
|
179
245
|
results.push({
|
|
180
246
|
package: pkg.name,
|
|
181
247
|
version,
|
|
182
248
|
tag,
|
|
183
|
-
|
|
249
|
+
...(previousTag ? { previousTag } : {}),
|
|
250
|
+
prerelease: isPrerelease(version),
|
|
251
|
+
exists,
|
|
252
|
+
created: !exists && !dryRun,
|
|
184
253
|
notes,
|
|
185
254
|
});
|
|
186
255
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
'origin',
|
|
193
|
-
...created.map((item) => item.tag),
|
|
194
|
-
]);
|
|
256
|
+
// Push one tag at a time because GitHub doesn't create push events for
|
|
257
|
+
// tags when more than three are pushed at once.
|
|
258
|
+
if (input.push && !dryRun) {
|
|
259
|
+
for (const item of results.filter((result) => result.created)) {
|
|
260
|
+
await git(input.cwd, ['push', 'origin', item.tag]);
|
|
195
261
|
}
|
|
196
262
|
}
|
|
197
|
-
if (input.githubRelease && !
|
|
263
|
+
if (input.githubRelease && !dryRun) {
|
|
198
264
|
for (const release of results) {
|
|
199
265
|
await createGitHubRelease(input.cwd, release);
|
|
200
266
|
}
|
|
201
267
|
}
|
|
202
|
-
return {
|
|
268
|
+
return {
|
|
269
|
+
tags: input.untagged
|
|
270
|
+
? results.filter((result) => !result.exists)
|
|
271
|
+
: results,
|
|
272
|
+
};
|
|
203
273
|
}
|
|
204
274
|
async function createGitHubRelease(cwd, release) {
|
|
205
275
|
try {
|
|
@@ -217,6 +287,7 @@ async function createGitHubRelease(cwd, release) {
|
|
|
217
287
|
`${release.package}@${release.version}`,
|
|
218
288
|
'--notes',
|
|
219
289
|
release.notes,
|
|
290
|
+
...(release.prerelease ? ['--prerelease'] : []),
|
|
220
291
|
], { cwd });
|
|
221
292
|
}
|
|
222
293
|
catch (error) {
|
|
@@ -225,6 +296,141 @@ async function createGitHubRelease(cwd, release) {
|
|
|
225
296
|
}
|
|
226
297
|
}
|
|
227
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Get the version to tag. Packages versioned by their git tags have no
|
|
301
|
+
* version file, so the expected version is the version to tag, or else the
|
|
302
|
+
* latest CHANGELOG.md version.
|
|
303
|
+
*
|
|
304
|
+
* @param pkg Configured package.
|
|
305
|
+
* @param expected Expected version from --version, if any.
|
|
306
|
+
* @return Version to tag.
|
|
307
|
+
*/
|
|
308
|
+
async function getTagVersion(pkg, expected) {
|
|
309
|
+
if ('git-tag' !== pkg.versionConfig.from.kind) {
|
|
310
|
+
return pkg.currentVersion;
|
|
311
|
+
}
|
|
312
|
+
return (expected ??
|
|
313
|
+
(await getLatestChangelogVersion(pkg.directory, pkg.currentVersion)));
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Pick the packages passed with --package, or all packages without it.
|
|
317
|
+
*
|
|
318
|
+
* @param packages Configured packages.
|
|
319
|
+
* @param input Tag options.
|
|
320
|
+
* @return Selected packages in the configured order.
|
|
321
|
+
* @throws {ValidationError} For unknown packages, or --version without a single package.
|
|
322
|
+
*/
|
|
323
|
+
function selectPackages(packages, input) {
|
|
324
|
+
const names = input.packages ?? [];
|
|
325
|
+
if (undefined !== input.version && 1 !== names.length) {
|
|
326
|
+
throw new ValidationError('An expected version requires exactly one package.');
|
|
327
|
+
}
|
|
328
|
+
names.forEach((name) => {
|
|
329
|
+
if (!packages.some((pkg) => pkg.name === name)) {
|
|
330
|
+
throw new ValidationError(`Unknown package "${name}".`);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
return names.length
|
|
334
|
+
? packages.filter((pkg) => names.includes(pkg.name))
|
|
335
|
+
: packages;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Find the tag of the newest release before a version.
|
|
339
|
+
*
|
|
340
|
+
* @param cwd Repository root.
|
|
341
|
+
* @param prefix Tag prefix of the package, like `plugins/alpha/`.
|
|
342
|
+
* @param version Version being released.
|
|
343
|
+
* @return Previous tag, or undefined for the first release.
|
|
344
|
+
*/
|
|
345
|
+
async function getPreviousTag(cwd, prefix, version) {
|
|
346
|
+
const output = await git(cwd, ['tag', '--list', `${prefix}*`]);
|
|
347
|
+
return output
|
|
348
|
+
.trim()
|
|
349
|
+
.split(/\r?\n/)
|
|
350
|
+
.filter((tag) => tag.startsWith(prefix))
|
|
351
|
+
.map((tag) => ({ tag, version: tag.slice(prefix.length) }))
|
|
352
|
+
.filter((item) => isVersion(item.version) &&
|
|
353
|
+
compareVersions(item.version, version) < 0)
|
|
354
|
+
.sort((a, b) => compareVersions(b.version, a.version))[0]?.tag;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Build the release notes of a tag with the configured formatter.
|
|
358
|
+
*
|
|
359
|
+
* @param cwd Repository root.
|
|
360
|
+
* @param config Repository release configuration.
|
|
361
|
+
* @param pkg Released package.
|
|
362
|
+
* @param release Version, tag and previous tag of the release.
|
|
363
|
+
* @param release.version Released version.
|
|
364
|
+
* @param release.tag Release tag.
|
|
365
|
+
* @param release.previousTag Tag of the previous release, if any.
|
|
366
|
+
* @param end Commit of the release: the tag, or HEAD before tagging.
|
|
367
|
+
* @return Release notes.
|
|
368
|
+
*/
|
|
369
|
+
async function getReleaseNotes(cwd, config, pkg, release, end) {
|
|
370
|
+
const changelog = await readReleaseNotes(pkg.directory, release.version);
|
|
371
|
+
const commits = await getReleaseCommits(cwd, pkg, end, release.previousTag);
|
|
372
|
+
const notes = changelog ||
|
|
373
|
+
commits
|
|
374
|
+
.map((commit) => `- ${commit.subject} (${commit.shortHash})`)
|
|
375
|
+
.join('\n');
|
|
376
|
+
if (!config.releaseNotes) {
|
|
377
|
+
return notes;
|
|
378
|
+
}
|
|
379
|
+
const formatted = await config.releaseNotes({
|
|
380
|
+
package: toChangesetPackage(pkg),
|
|
381
|
+
...release,
|
|
382
|
+
changelog,
|
|
383
|
+
commits,
|
|
384
|
+
notes,
|
|
385
|
+
});
|
|
386
|
+
if ('string' !== typeof formatted) {
|
|
387
|
+
throw new ValidationError(`The releaseNotes formatter didn't return a string for package "${pkg.name}".`);
|
|
388
|
+
}
|
|
389
|
+
return formatted.trim();
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* List the commits that changed a package since its previous release.
|
|
393
|
+
*
|
|
394
|
+
* @param cwd Repository root.
|
|
395
|
+
* @param pkg Released package.
|
|
396
|
+
* @param end Commit of the release.
|
|
397
|
+
* @param previousTag Tag of the previous release, if any.
|
|
398
|
+
* @return Commits, newest first.
|
|
399
|
+
*/
|
|
400
|
+
async function getReleaseCommits(cwd, pkg, end, previousTag) {
|
|
401
|
+
const packageFromRepo = path.relative(cwd, pkg.directory).split(path.sep).join('/') ||
|
|
402
|
+
'.';
|
|
403
|
+
const output = await git(cwd, [
|
|
404
|
+
'log',
|
|
405
|
+
'--format=%H%x1f%h%x1f%s',
|
|
406
|
+
previousTag ? `${previousTag}..${end}` : end,
|
|
407
|
+
'--',
|
|
408
|
+
packageFromRepo,
|
|
409
|
+
]);
|
|
410
|
+
return output
|
|
411
|
+
.trim()
|
|
412
|
+
.split(/\r?\n/)
|
|
413
|
+
.filter(Boolean)
|
|
414
|
+
.map((line) => {
|
|
415
|
+
const [hash = '', shortHash = '', subject = ''] = line.split('\x1f');
|
|
416
|
+
return { hash, shortHash, subject };
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Remove the release configuration from a package passed to config callbacks.
|
|
421
|
+
*
|
|
422
|
+
* @param pkg Configured package.
|
|
423
|
+
* @return Package without its group configuration.
|
|
424
|
+
*/
|
|
425
|
+
function toChangesetPackage(pkg) {
|
|
426
|
+
return {
|
|
427
|
+
name: pkg.name,
|
|
428
|
+
directory: pkg.directory,
|
|
429
|
+
path: pkg.path,
|
|
430
|
+
type: pkg.type,
|
|
431
|
+
currentVersion: pkg.currentVersion,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
228
434
|
/**
|
|
229
435
|
* Check whether changed files belong to packages without a changeset.
|
|
230
436
|
*
|
|
@@ -543,9 +749,7 @@ async function prependChangelog(directory, release, date, readmeChangelog, chang
|
|
|
543
749
|
const summaries = release.changesets
|
|
544
750
|
.map((item) => item.summary.trim())
|
|
545
751
|
.filter(Boolean);
|
|
546
|
-
const section = `## ${release.nextVersion} (${date})\n\n${
|
|
547
|
-
.map((item) => `- ${item.replaceAll(/\r?\n/g, '\n ')}`)
|
|
548
|
-
.join('\n')}\n\n`;
|
|
752
|
+
const section = `## ${release.nextVersion} (${date})\n\n${formatChangelogEntries(release.changesets)}\n\n`;
|
|
549
753
|
const changelog = path.join(directory, 'CHANGELOG.md');
|
|
550
754
|
let previous = '';
|
|
551
755
|
try {
|
|
@@ -820,10 +1024,60 @@ function packageType(group) {
|
|
|
820
1024
|
}
|
|
821
1025
|
return 'other';
|
|
822
1026
|
}
|
|
1027
|
+
const VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/;
|
|
1028
|
+
function isVersion(version) {
|
|
1029
|
+
return VERSION_PATTERN.test(version);
|
|
1030
|
+
}
|
|
1031
|
+
function isPrerelease(version) {
|
|
1032
|
+
return Boolean(VERSION_PATTERN.exec(version)?.[4]);
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Compare two versions with SemVer precedence, where a pre-release like
|
|
1036
|
+
* 1.0.0-rc.1 comes before 1.0.0.
|
|
1037
|
+
*
|
|
1038
|
+
* @param left Version.
|
|
1039
|
+
* @param right Version to compare with.
|
|
1040
|
+
* @return Negative when left is lower, positive when higher, otherwise 0.
|
|
1041
|
+
*/
|
|
823
1042
|
function compareVersions(left, right) {
|
|
824
|
-
const
|
|
825
|
-
const
|
|
826
|
-
|
|
1043
|
+
const [leftCore = '', leftPre] = splitPrerelease(left);
|
|
1044
|
+
const [rightCore = '', rightPre] = splitPrerelease(right);
|
|
1045
|
+
const a = leftCore.split('.').map(Number);
|
|
1046
|
+
const b = rightCore.split('.').map(Number);
|
|
1047
|
+
const core = (a[0] ?? 0) - (b[0] ?? 0) ||
|
|
827
1048
|
(a[1] ?? 0) - (b[1] ?? 0) ||
|
|
828
|
-
(a[2] ?? 0) - (b[2] ?? 0)
|
|
1049
|
+
(a[2] ?? 0) - (b[2] ?? 0);
|
|
1050
|
+
if (core || leftPre === rightPre) {
|
|
1051
|
+
return core;
|
|
1052
|
+
}
|
|
1053
|
+
if (undefined === leftPre || undefined === rightPre) {
|
|
1054
|
+
return undefined === leftPre ? 1 : -1;
|
|
1055
|
+
}
|
|
1056
|
+
return comparePrerelease(leftPre.split('.'), rightPre.split('.'));
|
|
1057
|
+
}
|
|
1058
|
+
function splitPrerelease(version) {
|
|
1059
|
+
const index = version.indexOf('-');
|
|
1060
|
+
return -1 === index
|
|
1061
|
+
? [version]
|
|
1062
|
+
: [version.slice(0, index), version.slice(index + 1)];
|
|
1063
|
+
}
|
|
1064
|
+
function comparePrerelease(left, right) {
|
|
1065
|
+
const [a, ...leftRest] = left;
|
|
1066
|
+
const [b, ...rightRest] = right;
|
|
1067
|
+
if (undefined === a || undefined === b) {
|
|
1068
|
+
return (undefined === a ? 0 : 1) - (undefined === b ? 0 : 1);
|
|
1069
|
+
}
|
|
1070
|
+
const order = compareIdentifier(a, b);
|
|
1071
|
+
return order || comparePrerelease(leftRest, rightRest);
|
|
1072
|
+
}
|
|
1073
|
+
function compareIdentifier(a, b) {
|
|
1074
|
+
const aNumeric = /^\d+$/.test(a);
|
|
1075
|
+
const bNumeric = /^\d+$/.test(b);
|
|
1076
|
+
if (aNumeric && bNumeric) {
|
|
1077
|
+
return Number(a) - Number(b);
|
|
1078
|
+
}
|
|
1079
|
+
if (aNumeric || bNumeric) {
|
|
1080
|
+
return aNumeric ? -1 : 1;
|
|
1081
|
+
}
|
|
1082
|
+
return Number(a > b) - Number(a < b);
|
|
829
1083
|
}
|
package/dist/changesets.d.ts
CHANGED
|
@@ -53,6 +53,13 @@ export declare function parseChangeset(contents: string, fileName?: string): Cha
|
|
|
53
53
|
* @return Markdown contents with a trailing newline.
|
|
54
54
|
*/
|
|
55
55
|
export declare function formatChangeset(changeset: Omit<Changeset, 'fileName'>): string;
|
|
56
|
+
/**
|
|
57
|
+
* Format the summaries of changesets as a Markdown list of changelog entries.
|
|
58
|
+
*
|
|
59
|
+
* @param changesets Changesets of a release.
|
|
60
|
+
* @return Markdown list, empty without summaries.
|
|
61
|
+
*/
|
|
62
|
+
export declare function formatChangelogEntries(changesets: Changeset[]): string;
|
|
56
63
|
/**
|
|
57
64
|
* Return the strongest bump requested for a package across changesets.
|
|
58
65
|
*
|
package/dist/changesets.js
CHANGED
|
@@ -64,6 +64,19 @@ export function formatChangeset(changeset) {
|
|
|
64
64
|
.join('\n');
|
|
65
65
|
return `---\n${frontmatter}\n---\n\n${changeset.summary.trim()}\n`;
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Format the summaries of changesets as a Markdown list of changelog entries.
|
|
69
|
+
*
|
|
70
|
+
* @param changesets Changesets of a release.
|
|
71
|
+
* @return Markdown list, empty without summaries.
|
|
72
|
+
*/
|
|
73
|
+
export function formatChangelogEntries(changesets) {
|
|
74
|
+
return changesets
|
|
75
|
+
.map((item) => item.summary.trim())
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.map((item) => `- ${item.replaceAll(/\r?\n/g, '\n ')}`)
|
|
78
|
+
.join('\n');
|
|
79
|
+
}
|
|
67
80
|
/**
|
|
68
81
|
* Return the strongest bump requested for a package across changesets.
|
|
69
82
|
*
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { appendFileSync } from 'node:fs';
|
|
1
2
|
import { parseArgs } from 'node:util';
|
|
2
3
|
import zip from "./commands/zip.js";
|
|
3
4
|
import checksums from "./commands/checksums.js";
|
|
@@ -5,6 +6,7 @@ import sign from "./commands/sign.js";
|
|
|
5
6
|
import verify from "./commands/verify.js";
|
|
6
7
|
import versionCheck from "./commands/version-check.js";
|
|
7
8
|
import deploySvn from "./commands/deploy-svn.js";
|
|
9
|
+
import deployGit from "./commands/deploy-git.js";
|
|
8
10
|
import { Logger, TextWriter } from "./logger.js";
|
|
9
11
|
import { getBoolean, getString } from "./commands/command.js";
|
|
10
12
|
import { ValidationError } from "./errors.js";
|
|
@@ -24,6 +26,7 @@ export const commands = [
|
|
|
24
26
|
verify,
|
|
25
27
|
versionCheck,
|
|
26
28
|
deploySvn,
|
|
29
|
+
deployGit,
|
|
27
30
|
changesetAdd,
|
|
28
31
|
changesetStatus,
|
|
29
32
|
changesetVersion,
|
|
@@ -36,6 +39,7 @@ const globalArgs = {
|
|
|
36
39
|
'log-format': { type: 'string' },
|
|
37
40
|
ci: { type: 'boolean' },
|
|
38
41
|
json: { type: 'boolean' },
|
|
42
|
+
'github-output': { type: 'boolean' },
|
|
39
43
|
config: { type: 'string' },
|
|
40
44
|
};
|
|
41
45
|
const globalOptions = `Global options:
|
|
@@ -47,6 +51,8 @@ const globalOptions = `Global options:
|
|
|
47
51
|
when GITHUB_ACTIONS=true, otherwise text)
|
|
48
52
|
--ci Same as --log-format github
|
|
49
53
|
--json Print the result as JSON
|
|
54
|
+
--github-output Set the result as JSON to the "result" output of
|
|
55
|
+
the GitHub Actions step
|
|
50
56
|
--config <path> Path to a JavaScript release config file`;
|
|
51
57
|
/**
|
|
52
58
|
* Get the top-level help text listing all commands.
|
|
@@ -143,6 +149,21 @@ function createLogWriter(values, streams) {
|
|
|
143
149
|
}
|
|
144
150
|
return create(streams);
|
|
145
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the GitHub Actions step output file for --github-output, checked before
|
|
154
|
+
* the command runs.
|
|
155
|
+
*
|
|
156
|
+
* @param values Parsed option values.
|
|
157
|
+
* @return Output file path, or undefined without --github-output.
|
|
158
|
+
* @throws {ValidationError} For --github-output without GITHUB_OUTPUT.
|
|
159
|
+
*/
|
|
160
|
+
function getGitHubOutputFile(values) {
|
|
161
|
+
const file = process.env.GITHUB_OUTPUT;
|
|
162
|
+
if (getBoolean(values, 'github-output') && !file) {
|
|
163
|
+
throw new ValidationError('--github-output requires the GITHUB_OUTPUT environment variable of GitHub Actions.');
|
|
164
|
+
}
|
|
165
|
+
return getBoolean(values, 'github-output') ? file : undefined;
|
|
166
|
+
}
|
|
146
167
|
/**
|
|
147
168
|
* Parse the arguments of a command and run it.
|
|
148
169
|
*
|
|
@@ -169,6 +190,7 @@ async function runCommand(command, args, io) {
|
|
|
169
190
|
if ('' === configFile?.trim()) {
|
|
170
191
|
throw new ValidationError('--config <path> must not be empty.');
|
|
171
192
|
}
|
|
193
|
+
const githubOutputFile = getGitHubOutputFile(values);
|
|
172
194
|
logger = new Logger({
|
|
173
195
|
writer: createLogWriter(values, { stdout, stderr }),
|
|
174
196
|
quiet: getBoolean(values, 'quiet') || getBoolean(values, 'json'),
|
|
@@ -184,6 +206,10 @@ async function runCommand(command, args, io) {
|
|
|
184
206
|
if (getBoolean(values, 'json')) {
|
|
185
207
|
stdout.write(JSON.stringify(result, null, '\t') + '\n');
|
|
186
208
|
}
|
|
209
|
+
if (githubOutputFile) {
|
|
210
|
+
// Compact JSON has no line breaks, so it fits a single output line.
|
|
211
|
+
appendFileSync(githubOutputFile, `result=${JSON.stringify(result)}\n`);
|
|
212
|
+
}
|
|
187
213
|
return exitCode;
|
|
188
214
|
}
|
|
189
215
|
catch (error) {
|
|
@@ -61,11 +61,16 @@ export declare const changesetVersionArgs: {
|
|
|
61
61
|
readonly date: {
|
|
62
62
|
readonly type: "string";
|
|
63
63
|
};
|
|
64
|
+
readonly package: {
|
|
65
|
+
readonly type: "string";
|
|
66
|
+
readonly multiple: true;
|
|
67
|
+
};
|
|
64
68
|
};
|
|
65
69
|
/** Parsed input for `changeset-version`. */
|
|
66
70
|
export interface ChangesetVersionArgs {
|
|
67
71
|
dryRun: boolean;
|
|
68
72
|
date?: string;
|
|
73
|
+
packages?: string[];
|
|
69
74
|
}
|
|
70
75
|
/**
|
|
71
76
|
* Validate and normalize a release date option.
|
|
@@ -91,19 +96,32 @@ export declare const changesetTagArgs: {
|
|
|
91
96
|
readonly 'dry-run': {
|
|
92
97
|
readonly type: "boolean";
|
|
93
98
|
};
|
|
99
|
+
readonly untagged: {
|
|
100
|
+
readonly type: "boolean";
|
|
101
|
+
};
|
|
94
102
|
readonly 'github-release': {
|
|
95
103
|
readonly type: "boolean";
|
|
96
104
|
};
|
|
97
105
|
readonly since: {
|
|
98
106
|
readonly type: "string";
|
|
99
107
|
};
|
|
108
|
+
readonly package: {
|
|
109
|
+
readonly type: "string";
|
|
110
|
+
readonly multiple: true;
|
|
111
|
+
};
|
|
112
|
+
readonly version: {
|
|
113
|
+
readonly type: "string";
|
|
114
|
+
};
|
|
100
115
|
};
|
|
101
116
|
/** Parsed input for `changeset-tag`. */
|
|
102
117
|
export interface ChangesetTagArgs {
|
|
103
118
|
push: boolean;
|
|
104
119
|
dryRun: boolean;
|
|
120
|
+
untagged: boolean;
|
|
105
121
|
githubRelease: boolean;
|
|
106
122
|
since?: string;
|
|
123
|
+
packages: string[];
|
|
124
|
+
version?: string;
|
|
107
125
|
}
|
|
108
126
|
/**
|
|
109
127
|
* Parse the `changeset-tag` options.
|
|
@@ -74,6 +74,7 @@ export function parseChangesetStatusArgs(values) {
|
|
|
74
74
|
export const changesetVersionArgs = {
|
|
75
75
|
'dry-run': { type: 'boolean' },
|
|
76
76
|
date: { type: 'string' },
|
|
77
|
+
package: { type: 'string', multiple: true },
|
|
77
78
|
};
|
|
78
79
|
/**
|
|
79
80
|
* Validate and normalize a release date option.
|
|
@@ -104,17 +105,25 @@ export function parseChangesetDate(value) {
|
|
|
104
105
|
*/
|
|
105
106
|
export function parseChangesetVersionArgs(values) {
|
|
106
107
|
const date = parseChangesetDate(getString(values, 'date'));
|
|
108
|
+
const packages = getStrings(values, 'package');
|
|
109
|
+
if (packages.some((name) => !name.trim())) {
|
|
110
|
+
throw new ValidationError('--package <name> must not be empty.');
|
|
111
|
+
}
|
|
107
112
|
return {
|
|
108
113
|
dryRun: getBoolean(values, 'dry-run'),
|
|
109
114
|
...(date ? { date } : {}),
|
|
115
|
+
...(packages.length ? { packages } : {}),
|
|
110
116
|
};
|
|
111
117
|
}
|
|
112
118
|
/** Parse-args options for `changeset-tag`. */
|
|
113
119
|
export const changesetTagArgs = {
|
|
114
120
|
push: { type: 'boolean' },
|
|
115
121
|
'dry-run': { type: 'boolean' },
|
|
122
|
+
untagged: { type: 'boolean' },
|
|
116
123
|
'github-release': { type: 'boolean' },
|
|
117
124
|
since: { type: 'string' },
|
|
125
|
+
package: { type: 'string', multiple: true },
|
|
126
|
+
version: { type: 'string' },
|
|
118
127
|
};
|
|
119
128
|
/**
|
|
120
129
|
* Parse the `changeset-tag` options.
|
|
@@ -126,14 +135,29 @@ export function parseChangesetTagArgs(values) {
|
|
|
126
135
|
const args = {
|
|
127
136
|
push: getBoolean(values, 'push'),
|
|
128
137
|
dryRun: getBoolean(values, 'dry-run'),
|
|
138
|
+
untagged: getBoolean(values, 'untagged'),
|
|
129
139
|
githubRelease: getBoolean(values, 'github-release'),
|
|
130
140
|
since: getString(values, 'since'),
|
|
141
|
+
packages: getStrings(values, 'package'),
|
|
142
|
+
version: getString(values, 'version')?.trim(),
|
|
131
143
|
};
|
|
132
144
|
if ('' === args.since?.trim()) {
|
|
133
145
|
throw new ValidationError('--since <ref> must not be empty.');
|
|
134
146
|
}
|
|
147
|
+
if (args.packages.some((name) => !name.trim())) {
|
|
148
|
+
throw new ValidationError('--package <name> must not be empty.');
|
|
149
|
+
}
|
|
150
|
+
if ('' === args.version) {
|
|
151
|
+
throw new ValidationError('--version <version> must not be empty.');
|
|
152
|
+
}
|
|
153
|
+
if (undefined !== args.version && 1 !== args.packages.length) {
|
|
154
|
+
throw new ValidationError('--version <version> requires exactly one --package <name>.');
|
|
155
|
+
}
|
|
135
156
|
if (args.githubRelease && !args.push) {
|
|
136
157
|
throw new ValidationError('--github-release requires --push.');
|
|
137
158
|
}
|
|
159
|
+
if (args.untagged && args.push) {
|
|
160
|
+
throw new ValidationError('--untagged cannot be combined with --push.');
|
|
161
|
+
}
|
|
138
162
|
return args;
|
|
139
163
|
}
|
|
@@ -10,12 +10,22 @@ declare const _default: {
|
|
|
10
10
|
readonly 'dry-run': {
|
|
11
11
|
readonly type: "boolean";
|
|
12
12
|
};
|
|
13
|
+
readonly untagged: {
|
|
14
|
+
readonly type: "boolean";
|
|
15
|
+
};
|
|
13
16
|
readonly 'github-release': {
|
|
14
17
|
readonly type: "boolean";
|
|
15
18
|
};
|
|
16
19
|
readonly since: {
|
|
17
20
|
readonly type: "string";
|
|
18
21
|
};
|
|
22
|
+
readonly package: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly multiple: true;
|
|
25
|
+
};
|
|
26
|
+
readonly version: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
};
|
|
19
29
|
};
|
|
20
30
|
run({ values, cwd, configFile, logger }: import("./command.ts").CommandContext): Promise<{
|
|
21
31
|
exitCode: number;
|