@williamthorsen/release-kit 16.1.2 → 17.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.meta/changelog.json +79 -0
- package/.readyup/kits/default.js +2 -2
- package/.readyup/kits/npm-auto-publish.js +1 -1
- package/.readyup/manifest.json +204 -16
- package/CHANGELOG.md +28 -0
- package/README.md +2 -2
- package/bin/release-kit.js +1 -1
- package/dist/esm/assertReleasesOnNpm.js +1 -2
- package/dist/esm/assertTarballsComplete.js +2 -3
- package/dist/esm/bin.d.ts +2 -0
- package/dist/esm/bin.js +1 -0
- package/dist/esm/changelogJsonUtils.js +2 -1
- package/dist/esm/cli/commandKit.d.ts +18 -0
- package/dist/esm/cli/commandKit.js +27 -0
- package/dist/esm/cli/maintenanceCommands.d.ts +6 -0
- package/dist/esm/cli/maintenanceCommands.js +129 -0
- package/dist/esm/cli/releaseCommands.d.ts +7 -0
- package/dist/esm/cli/releaseCommands.js +156 -0
- package/dist/esm/cli.js +4 -3
- package/dist/esm/commitCommand.d.ts +4 -1
- package/dist/esm/commitCommand.js +2 -6
- package/dist/esm/createGithubReleaseCommand.d.ts +10 -4
- package/dist/esm/createGithubReleaseCommand.js +2 -15
- package/dist/esm/deriveDirectoryConfig.js +1 -2
- package/dist/esm/deriveWorkspaceConfig.js +1 -2
- package/dist/esm/detectPackageManager.js +1 -1
- package/dist/esm/findMissingManifestPaths.js +1 -1
- package/dist/esm/hasPrettierConfig.js +1 -1
- package/dist/esm/init/parseJsonRecord.js +1 -1
- package/dist/esm/isPublishableManifest.js +1 -1
- package/dist/esm/loadChangelogOverrides.js +1 -1
- package/dist/esm/loadConfig.js +1 -2
- package/dist/esm/loadValidatedConfig.d.ts +6 -1
- package/dist/esm/loadValidatedConfig.js +11 -3
- package/dist/esm/npmRegistry.js +1 -1
- package/dist/esm/packFromCleanCheckout.js +1 -2
- package/dist/esm/parseChangeRecordBlock.js +1 -1
- package/dist/esm/parseCommitMessage.d.ts +1 -3
- package/dist/esm/parseCommitMessage.js +2 -15
- package/dist/esm/prepareArgs.d.ts +1 -49
- package/dist/esm/prepareArgs.js +4 -51
- package/dist/esm/prepareCommand.d.ts +15 -3
- package/dist/esm/prepareCommand.js +15 -23
- package/dist/esm/publishCommand.d.ts +12 -3
- package/dist/esm/publishCommand.js +1 -16
- package/dist/esm/pushCommand.d.ts +12 -4
- package/dist/esm/pushCommand.js +2 -16
- package/dist/esm/resolveCommandTags.d.ts +3 -3
- package/dist/esm/resolveCommandTags.js +5 -7
- package/dist/esm/{resolveStylesOrExit.d.ts → resolveStyles.d.ts} +2 -2
- package/dist/esm/resolveStyles.js +10 -0
- package/dist/esm/route.d.ts +2 -0
- package/dist/esm/route.js +39 -0
- package/dist/esm/showTagPrefixesCommand.js +2 -1
- package/dist/esm/sync-labels/generateCommand.js +7 -6
- package/dist/esm/sync-labels/presets.js +1 -2
- package/dist/esm/sync-labels/retiredConfig.js +2 -1
- package/dist/esm/sync-labels/syncCommand.js +4 -3
- package/dist/esm/tagCommand.d.ts +8 -3
- package/dist/esm/tagCommand.js +1 -7
- package/dist/esm/typeGuards.d.ts +0 -2
- package/dist/esm/typeGuards.js +0 -3
- package/dist/esm/validateConfig.js +1 -1
- package/dist/esm/validateOverridesCommand.js +5 -4
- package/docs/configuration.md +1 -1
- package/docs/editorial-overrides.md +2 -0
- package/docs/labels.md +2 -0
- package/docs/releasing.md +1 -1
- package/package.json +4 -4
- package/dist/esm/configFlagSchema.d.ts +0 -12
- package/dist/esm/configFlagSchema.js +0 -3
- package/dist/esm/help/initHelp.d.ts +0 -4
- package/dist/esm/help/initHelp.js +0 -31
- package/dist/esm/help/overridesHelp.d.ts +0 -4
- package/dist/esm/help/overridesHelp.js +0 -32
- package/dist/esm/help/prepareHelp.d.ts +0 -7
- package/dist/esm/help/prepareHelp.js +0 -30
- package/dist/esm/help/releaseStepsHelp.d.ts +0 -12
- package/dist/esm/help/releaseStepsHelp.js +0 -91
- package/dist/esm/help/syncLabelsHelp.d.ts +0 -8
- package/dist/esm/help/syncLabelsHelp.js +0 -55
- package/dist/esm/help/usage.d.ts +0 -2
- package/dist/esm/help/usage.js +0 -29
- package/dist/esm/resolveConfigFlag.d.ts +0 -7
- package/dist/esm/resolveConfigFlag.js +0 -4
- package/dist/esm/resolveStylesOrExit.js +0 -10
- package/dist/esm/runCli.d.ts +0 -5
- package/dist/esm/runCli.js +0 -224
|
@@ -1,55 +1,7 @@
|
|
|
1
|
-
import type { ReleaseType } from './types.js';
|
|
2
1
|
/** Canonical `N.N.N` semver pattern for validating `--set-version` input. */
|
|
3
2
|
export declare const CANONICAL_SEMVER_PATTERN: RegExp;
|
|
4
|
-
export declare const prepareFlagSchema: {
|
|
5
|
-
isDryRun: {
|
|
6
|
-
long: string;
|
|
7
|
-
type: "boolean";
|
|
8
|
-
};
|
|
9
|
-
shouldForce: {
|
|
10
|
-
long: string;
|
|
11
|
-
type: "boolean";
|
|
12
|
-
};
|
|
13
|
-
shouldSkipGitChecks: {
|
|
14
|
-
long: string;
|
|
15
|
-
type: "boolean";
|
|
16
|
-
short: string;
|
|
17
|
-
};
|
|
18
|
-
bump: {
|
|
19
|
-
long: string;
|
|
20
|
-
type: "string";
|
|
21
|
-
};
|
|
22
|
-
setVersion: {
|
|
23
|
-
long: string;
|
|
24
|
-
type: "string";
|
|
25
|
-
};
|
|
26
|
-
only: {
|
|
27
|
-
long: string;
|
|
28
|
-
type: "string";
|
|
29
|
-
};
|
|
30
|
-
shouldIncludeReleaseNotes: {
|
|
31
|
-
long: string;
|
|
32
|
-
type: "boolean";
|
|
33
|
-
};
|
|
34
|
-
config: {
|
|
35
|
-
long: string;
|
|
36
|
-
type: "string";
|
|
37
|
-
};
|
|
38
|
-
};
|
|
39
|
-
/** Parses CLI arguments into structured options. Prints a usage error and exits on invalid input. */
|
|
40
|
-
export declare function parseArgs(argv: string[]): {
|
|
41
|
-
configPath: string | undefined;
|
|
42
|
-
isDryRun: boolean;
|
|
43
|
-
shouldForce: boolean;
|
|
44
|
-
shouldSkipGitChecks: boolean;
|
|
45
|
-
bumpOverride: ReleaseType | undefined;
|
|
46
|
-
onlyNames: string[] | undefined;
|
|
47
|
-
/** The raw `--set-version` value, whose accepted form depends on the mode. */
|
|
48
|
-
setVersion: string | undefined;
|
|
49
|
-
shouldIncludeReleaseNotes: boolean;
|
|
50
|
-
};
|
|
51
3
|
/**
|
|
52
4
|
* Parses a monorepo `--set-version` value, a comma-separated list of `<target>@N.N.N` entries, into versions keyed by
|
|
53
|
-
* target name.
|
|
5
|
+
* target name. Throws a `UsageError` for a malformed entry, an unknown target, or a repeated one.
|
|
54
6
|
*/
|
|
55
7
|
export declare function parseSetVersions(value: string, knownNames: readonly string[]): Map<string, string>;
|
package/dist/esm/prepareArgs.js
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
3
|
-
const VALID_BUMP_TYPES = ['major', 'minor', 'patch'];
|
|
1
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
4
2
|
export const CANONICAL_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
5
|
-
export const prepareFlagSchema = {
|
|
6
|
-
...configFlagSchema,
|
|
7
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
8
|
-
shouldForce: { long: '--force', type: 'boolean' },
|
|
9
|
-
shouldSkipGitChecks: {
|
|
10
|
-
long: '--no-git-checks',
|
|
11
|
-
type: 'boolean',
|
|
12
|
-
short: '-n',
|
|
13
|
-
},
|
|
14
|
-
bump: { long: '--bump', type: 'string' },
|
|
15
|
-
setVersion: { long: '--set-version', type: 'string' },
|
|
16
|
-
only: { long: '--only', type: 'string' },
|
|
17
|
-
shouldIncludeReleaseNotes: { long: '--with-release-notes', type: 'boolean' },
|
|
18
|
-
};
|
|
19
|
-
export function parseArgs(argv) {
|
|
20
|
-
const { flags } = parseArgsOrExit(argv, prepareFlagSchema);
|
|
21
|
-
let bumpOverride;
|
|
22
|
-
if (flags.bump !== undefined) {
|
|
23
|
-
if (!isReleaseType(flags.bump)) {
|
|
24
|
-
reportError(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(', ')}`);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
bumpOverride = flags.bump;
|
|
28
|
-
}
|
|
29
|
-
let onlyNames;
|
|
30
|
-
if (flags.only !== undefined) {
|
|
31
|
-
onlyNames = flags.only.split(',');
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
configPath: flags.config,
|
|
35
|
-
isDryRun: flags.isDryRun,
|
|
36
|
-
shouldForce: flags.shouldForce,
|
|
37
|
-
shouldSkipGitChecks: flags.shouldSkipGitChecks,
|
|
38
|
-
bumpOverride,
|
|
39
|
-
onlyNames,
|
|
40
|
-
setVersion: flags.setVersion,
|
|
41
|
-
shouldIncludeReleaseNotes: flags.shouldIncludeReleaseNotes,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
3
|
export function parseSetVersions(value, knownNames) {
|
|
45
4
|
const setVersions = new Map();
|
|
46
5
|
for (const entry of value.split(',')) {
|
|
@@ -48,22 +7,16 @@ export function parseSetVersions(value, knownNames) {
|
|
|
48
7
|
const name = entry.slice(0, Math.max(0, separatorIndex));
|
|
49
8
|
const version = entry.slice(separatorIndex + 1);
|
|
50
9
|
if (separatorIndex <= 0 || !CANONICAL_SEMVER_PATTERN.test(version)) {
|
|
51
|
-
|
|
10
|
+
throw new UsageError(`Invalid --set-version entry "${entry}". In monorepo mode, each entry must be <target>@N.N.N ` +
|
|
52
11
|
'(canonical semver, no pre-release suffix), e.g. --set-version=arrays@1.0.0.');
|
|
53
|
-
process.exit(1);
|
|
54
12
|
}
|
|
55
13
|
if (!knownNames.includes(name)) {
|
|
56
|
-
|
|
57
|
-
process.exit(1);
|
|
14
|
+
throw new UsageError(`Unknown release target "${name}" in --set-version. Known targets: ${knownNames.join(', ')}`);
|
|
58
15
|
}
|
|
59
16
|
if (setVersions.has(name)) {
|
|
60
|
-
|
|
61
|
-
process.exit(1);
|
|
17
|
+
throw new UsageError(`--set-version names target "${name}" more than once`);
|
|
62
18
|
}
|
|
63
19
|
setVersions.set(name, version);
|
|
64
20
|
}
|
|
65
21
|
return setVersions;
|
|
66
22
|
}
|
|
67
|
-
function isReleaseType(value) {
|
|
68
|
-
return VALID_BUMP_TYPES.includes(value);
|
|
69
|
-
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
import type { ReleaseType } from './types.js';
|
|
3
|
+
export interface PrepareCommandOptions {
|
|
4
|
+
bumpOverride: ReleaseType | undefined;
|
|
5
|
+
configPath: string | undefined;
|
|
6
|
+
isDryRun: boolean;
|
|
7
|
+
/** The release targets that `--only` names, or `undefined` to release every target that has changes. */
|
|
8
|
+
onlyNames: string[] | undefined;
|
|
9
|
+
/** The raw `--set-version` value, whose accepted form depends on the mode. */
|
|
10
|
+
setVersion: string | undefined;
|
|
11
|
+
shouldForce: boolean;
|
|
12
|
+
shouldIncludeReleaseNotes: boolean;
|
|
13
|
+
shouldSkipGitChecks: boolean;
|
|
14
|
+
styles: StreamStyles;
|
|
15
|
+
}
|
|
2
16
|
/**
|
|
3
17
|
* Orchestrates the CLI `prepare` command: checks the working tree, loads and validates the config, discovers the
|
|
4
18
|
* workspaces, and prepares the release over the targets of the repo shape.
|
|
5
|
-
*
|
|
6
|
-
* A relative `--config` resolves against `invocationDir`.
|
|
7
19
|
*/
|
|
8
|
-
export declare function prepareCommand(
|
|
20
|
+
export declare function prepareCommand({ bumpOverride, configPath, isDryRun, onlyNames, setVersion, shouldForce, shouldIncludeReleaseNotes, shouldSkipGitChecks, styles, }: PrepareCommandOptions): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
|
-
import { formatGlyphLine, isAnticipatedFailure,
|
|
2
|
+
import { formatGlyphLine, isAnticipatedFailure, UserError, } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
3
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
4
5
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
5
6
|
import { assertReleasesOnNpm } from "./assertReleasesOnNpm.js";
|
|
@@ -7,18 +8,16 @@ import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
|
7
8
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
8
9
|
import { dim } from "./format.js";
|
|
9
10
|
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
10
|
-
import {
|
|
11
|
-
import { CANONICAL_SEMVER_PATTERN,
|
|
11
|
+
import { ConfigProblemError, loadValidatedConfig, reportConfigWarnings } from "./loadValidatedConfig.js";
|
|
12
|
+
import { CANONICAL_SEMVER_PATTERN, parseSetVersions } from "./prepareArgs.js";
|
|
12
13
|
import { prepareReleaseTargets } from "./prepareReleaseTargets.js";
|
|
13
14
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
|
|
14
15
|
import { applyReleasePlan } from "./releasePlan.js";
|
|
15
16
|
import { reportPrepare } from "./reportPrepare.js";
|
|
16
|
-
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
17
17
|
import { isSinglePackageTargets, PROJECT_TARGET_ID, resolveReleaseTargets, SINGLE_PACKAGE_TARGET_ID, } from "./resolveReleaseTargets.js";
|
|
18
18
|
import { prefixUserError } from "./user-error-chaining.js";
|
|
19
19
|
const UNCHANGED_TREE_NOTE = 'The command did not write any files; the working tree is unchanged.';
|
|
20
|
-
export async function prepareCommand(
|
|
21
|
-
const { configPath: configFlag, isDryRun, shouldForce, shouldSkipGitChecks, bumpOverride, onlyNames, setVersion, shouldIncludeReleaseNotes, } = parseArgs(argv);
|
|
20
|
+
export async function prepareCommand({ bumpOverride, configPath, isDryRun, onlyNames, setVersion, shouldForce, shouldIncludeReleaseNotes, shouldSkipGitChecks, styles, }) {
|
|
22
21
|
const options = {
|
|
23
22
|
shouldForce,
|
|
24
23
|
...(bumpOverride !== undefined && { bumpOverride }),
|
|
@@ -30,10 +29,9 @@ export async function prepareCommand(argv, styles, invocationDir) {
|
|
|
30
29
|
if (!isDryRun && !shouldSkipGitChecks) {
|
|
31
30
|
assertCleanWorkingTree();
|
|
32
31
|
}
|
|
33
|
-
const configResult = await loadValidatedConfig(
|
|
32
|
+
const configResult = await loadValidatedConfig(configPath);
|
|
34
33
|
if (configResult.status === 'invalid') {
|
|
35
|
-
|
|
36
|
-
process.exit(1);
|
|
34
|
+
throw new ConfigProblemError(configResult.problem);
|
|
37
35
|
}
|
|
38
36
|
let userConfig;
|
|
39
37
|
if (configResult.status === 'ok') {
|
|
@@ -48,10 +46,9 @@ export async function prepareCommand(argv, styles, invocationDir) {
|
|
|
48
46
|
throw prefixUserError('Failed to discover workspaces', error);
|
|
49
47
|
}
|
|
50
48
|
if (workspaceDiscovery.kind === 'empty') {
|
|
51
|
-
|
|
52
|
-
process.exit(1);
|
|
49
|
+
throw new UserError(`No workspace package to release. ${describeEmptyWorkspace(workspaceDiscovery)}`);
|
|
53
50
|
}
|
|
54
|
-
const config =
|
|
51
|
+
const config = resolveSelectedTargets(userConfig, workspaceDiscovery, onlyNames, setVersion);
|
|
55
52
|
const targetOptions = {
|
|
56
53
|
...options,
|
|
57
54
|
...(onlyNames !== undefined && { onlyTargetIds: onlyNames }),
|
|
@@ -69,15 +66,13 @@ function findReleasedPackagePaths(plan, targets) {
|
|
|
69
66
|
const releasedIds = new Set(plan.targets.flatMap((result) => result.status === 'released' && result.kind === 'package' ? result.targetId : []));
|
|
70
67
|
return targets.flatMap((target) => target.kind === 'package' && releasedIds.has(target.id) ? target.packagePath : []);
|
|
71
68
|
}
|
|
72
|
-
function
|
|
69
|
+
function resolveSelectedTargets(userConfig, discovery, onlyNames, setVersion) {
|
|
73
70
|
if (discovery.kind === 'single-package') {
|
|
74
71
|
if (onlyNames !== undefined) {
|
|
75
|
-
|
|
76
|
-
process.exit(1);
|
|
72
|
+
throw new UsageError('--only is only supported for monorepo configurations');
|
|
77
73
|
}
|
|
78
74
|
if (setVersion !== undefined && !CANONICAL_SEMVER_PATTERN.test(setVersion)) {
|
|
79
|
-
|
|
80
|
-
process.exit(1);
|
|
75
|
+
throw new UsageError(`Invalid --set-version value "${setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`);
|
|
81
76
|
}
|
|
82
77
|
return resolveReleaseTargets(userConfig, discovery);
|
|
83
78
|
}
|
|
@@ -89,10 +84,9 @@ function resolveTargetsOrExit(userConfig, discovery, onlyNames, setVersion) {
|
|
|
89
84
|
throw prefixUserError('Failed to resolve workspaces', error);
|
|
90
85
|
}
|
|
91
86
|
if (setVersion !== undefined && config.targets.some((target) => target.kind === 'composite')) {
|
|
92
|
-
|
|
87
|
+
throw new UsageError('--set-version cannot be combined with a project release. ' +
|
|
93
88
|
"--set-version sets individual workspaces' versions; a project release rolls up every " +
|
|
94
89
|
'contributing workspace. To use --set-version, run on a config without a `project` block or a `releases` composite.');
|
|
95
|
-
process.exit(1);
|
|
96
90
|
}
|
|
97
91
|
const knownNames = listSelectableIds(config.targets);
|
|
98
92
|
const requestedNames = onlyNames ?? [];
|
|
@@ -100,8 +94,7 @@ function resolveTargetsOrExit(userConfig, discovery, onlyNames, setVersion) {
|
|
|
100
94
|
if (knownNames.includes(name)) {
|
|
101
95
|
continue;
|
|
102
96
|
}
|
|
103
|
-
|
|
104
|
-
process.exit(1);
|
|
97
|
+
throw new UsageError(`Unknown release target "${name}". Known targets: ${knownNames.join(', ')}`);
|
|
105
98
|
}
|
|
106
99
|
return config;
|
|
107
100
|
}
|
|
@@ -141,9 +134,8 @@ function runAndReport(computePlan, isDryRun, style) {
|
|
|
141
134
|
if (formatError === undefined) {
|
|
142
135
|
return;
|
|
143
136
|
}
|
|
144
|
-
|
|
137
|
+
throw new UserError(`The format command failed, but the release is prepared and ${RELEASE_TAGS_FILE} is written. ` +
|
|
145
138
|
`Format the release files and run 'release-kit commit'.`);
|
|
146
|
-
process.exit(1);
|
|
147
139
|
}
|
|
148
140
|
function runFormatCommand(formatCommand) {
|
|
149
141
|
if (formatCommand === undefined) {
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface PublishCommandOptions {
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
isDryRun: boolean;
|
|
5
|
+
shouldGenerateProvenance: boolean;
|
|
6
|
+
shouldSkipGitChecks: boolean;
|
|
7
|
+
/** The tag names that `--tags` selects, or `undefined` to publish every publishable release tag on HEAD. */
|
|
8
|
+
tags: string[] | undefined;
|
|
9
|
+
styles: StreamStyles;
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
12
|
* Orchestrates the CLI `publish` command: checks for a clean working tree, resolves the tags on HEAD, checks the
|
|
4
13
|
* tarballs of the publishable ones from a clean checkout, and publishes each one, injecting release notes into its
|
|
5
|
-
* README for the duration of the publish.
|
|
14
|
+
* README for the duration of the publish.
|
|
6
15
|
*/
|
|
7
|
-
export declare function publishCommand(
|
|
16
|
+
export declare function publishCommand({ configPath, isDryRun, shouldGenerateProvenance, shouldSkipGitChecks, tags: requestedTags, styles, }: PublishCommandOptions): Promise<void>;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
4
3
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
5
4
|
import { assertTarballsComplete } from "./assertTarballsComplete.js";
|
|
6
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
7
5
|
import { deriveReleaseNotesConfig } from "./deriveReleaseNotesConfig.js";
|
|
8
6
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
9
7
|
import { formatDirectoryPublishSkip } from "./formatDirectoryPublishSkip.js";
|
|
@@ -11,26 +9,13 @@ import { formatPrivateSkip } from "./formatPrivateSkip.js";
|
|
|
11
9
|
import { formatProjectPublishSkip } from "./formatProjectPublishSkip.js";
|
|
12
10
|
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
13
11
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
14
|
-
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
15
12
|
import { publishPackage } from "./publish.js";
|
|
16
13
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
17
|
-
|
|
18
|
-
const publishFlagSchema = {
|
|
19
|
-
...configFlagSchema,
|
|
20
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
21
|
-
shouldSkipGitChecks: { long: '--no-git-checks', type: 'boolean' },
|
|
22
|
-
shouldGenerateProvenance: { long: '--provenance', type: 'boolean' },
|
|
23
|
-
tags: { long: '--tags', type: 'string' },
|
|
24
|
-
};
|
|
25
|
-
export async function publishCommand(argv, styles, invocationDir) {
|
|
26
|
-
const parsedArgs = parseArgsOrExit(argv, publishFlagSchema);
|
|
27
|
-
const configPath = resolveConfigFlag(parsedArgs.flags.config, invocationDir);
|
|
28
|
-
const { isDryRun, shouldSkipGitChecks, shouldGenerateProvenance } = parsedArgs.flags;
|
|
14
|
+
export async function publishCommand({ configPath, isDryRun, shouldGenerateProvenance, shouldSkipGitChecks, tags: requestedTags, styles, }) {
|
|
29
15
|
const userConfig = await loadUsableConfig(styles.stderr, configPath);
|
|
30
16
|
if (!isDryRun && !shouldSkipGitChecks) {
|
|
31
17
|
assertCleanWorkingTree();
|
|
32
18
|
}
|
|
33
|
-
const requestedTags = parseRequestedTags(parsedArgs.flags.tags);
|
|
34
19
|
const resolvedTags = resolveCommandTags(requestedTags, userConfig);
|
|
35
20
|
if (resolvedTags.length === 0) {
|
|
36
21
|
return;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface PushCommandOptions {
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
isDryRun: boolean;
|
|
5
|
+
/** The tag names that `--tags` selects, or `undefined` to push every release tag on HEAD. */
|
|
6
|
+
tags: string[] | undefined;
|
|
7
|
+
tagsOnly: boolean;
|
|
8
|
+
styles: StreamStyles;
|
|
9
|
+
}
|
|
2
10
|
/**
|
|
3
|
-
* Orchestrates the CLI `push` command:
|
|
4
|
-
*
|
|
11
|
+
* Orchestrates the CLI `push` command: loads the config, resolves tags from HEAD, and pushes the release commit and
|
|
12
|
+
* each tag individually.
|
|
5
13
|
*/
|
|
6
|
-
export declare function pushCommand(
|
|
14
|
+
export declare function pushCommand({ configPath, isDryRun, tags, tagsOnly, styles }: PushCommandOptions): Promise<void>;
|
package/dist/esm/pushCommand.js
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
import { parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
3
1
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
4
|
-
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
5
2
|
import { pushRelease } from "./pushRelease.js";
|
|
6
3
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
7
|
-
|
|
8
|
-
const pushFlagSchema = {
|
|
9
|
-
...configFlagSchema,
|
|
10
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
11
|
-
tags: { long: '--tags', type: 'string' },
|
|
12
|
-
tagsOnly: { long: '--tags-only', type: 'boolean' },
|
|
13
|
-
};
|
|
14
|
-
export async function pushCommand(argv, styles, invocationDir) {
|
|
15
|
-
const parsedArgs = parseArgsOrExit(argv, pushFlagSchema);
|
|
16
|
-
const configPath = resolveConfigFlag(parsedArgs.flags.config, invocationDir);
|
|
17
|
-
const { isDryRun, tagsOnly } = parsedArgs.flags;
|
|
4
|
+
export async function pushCommand({ configPath, isDryRun, tags, tagsOnly, styles }) {
|
|
18
5
|
const userConfig = await loadUsableConfig(styles.stderr, configPath);
|
|
19
|
-
const
|
|
20
|
-
const resolvedTags = resolveCommandTags(requestedTags, userConfig);
|
|
6
|
+
const resolvedTags = resolveCommandTags(tags, userConfig);
|
|
21
7
|
if (resolvedTags.length === 0) {
|
|
22
8
|
return;
|
|
23
9
|
}
|
|
@@ -3,9 +3,9 @@ import { type ResolvedTag } from './resolveReleaseTags.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Discovers workspaces, resolves release tags from HEAD, validates `--tags` names against the full resolved tag
|
|
5
5
|
* names (e.g., `nmr-core-v1.3.0`), and returns the filtered tag list. Throws a `UserError` when workspace discovery
|
|
6
|
-
* fails, when a workspace's `package.json` does not have a `name` field,
|
|
7
|
-
* config
|
|
8
|
-
* any
|
|
6
|
+
* fails, when a workspace's `package.json` does not have a `name` field, when `mergeMonorepoConfig` rejects the
|
|
7
|
+
* config, when the workspace does not resolve to any package (which is not single-package mode), and when HEAD does
|
|
8
|
+
* not have any release tags; throws a `UsageError` for a `--tags` name that HEAD lacks.
|
|
9
9
|
*
|
|
10
10
|
* In monorepo mode the workspace list is `userConfig` merged over the discovered workspaces, as `prepare` builds it,
|
|
11
11
|
* and each directory target and a declared composite (a `project` block or a `releases` composite) contribute their
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UserError } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
2
3
|
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
3
4
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
4
5
|
import { formatExcludedSkip } from "./formatExcludedSkip.js";
|
|
@@ -15,8 +16,7 @@ export function resolveCommandTags(tags, userConfig) {
|
|
|
15
16
|
throw prefixUserError('Failed to discover workspaces', error);
|
|
16
17
|
}
|
|
17
18
|
if (workspace.kind === 'empty') {
|
|
18
|
-
|
|
19
|
-
process.exit(1);
|
|
19
|
+
throw new UserError(`No workspace package to tag. ${describeEmptyWorkspace(workspace)}`);
|
|
20
20
|
}
|
|
21
21
|
let workspaces;
|
|
22
22
|
let directories = [];
|
|
@@ -50,8 +50,7 @@ export function resolveCommandTags(tags, userConfig) {
|
|
|
50
50
|
throw new Error('resolveCommandTags: invariant violated; neither workspaces nor singleWorkspace was derived');
|
|
51
51
|
}
|
|
52
52
|
if (resolvedTags.length === 0) {
|
|
53
|
-
|
|
54
|
-
process.exit(1);
|
|
53
|
+
throw new UserError('HEAD does not have any release tags. Create tags with `release-kit tag` first.');
|
|
55
54
|
}
|
|
56
55
|
if (tags !== undefined) {
|
|
57
56
|
const availableTagNames = resolvedTags.map((t) => t.tag);
|
|
@@ -59,8 +58,7 @@ export function resolveCommandTags(tags, userConfig) {
|
|
|
59
58
|
if (availableTagNames.includes(name)) {
|
|
60
59
|
continue;
|
|
61
60
|
}
|
|
62
|
-
|
|
63
|
-
process.exit(1);
|
|
61
|
+
throw new UsageError(`Unknown tag "${name}" in --tags. Available: ${availableTagNames.join(', ')}`);
|
|
64
62
|
}
|
|
65
63
|
resolvedTags = resolvedTags.filter((t) => tags.includes(t.tag));
|
|
66
64
|
}
|
|
@@ -6,5 +6,5 @@ export interface ResolveStylesOptions {
|
|
|
6
6
|
readonly stderrIsTty: boolean;
|
|
7
7
|
readonly stdoutIsTty: boolean;
|
|
8
8
|
}
|
|
9
|
-
/** Resolves the style of each output stream,
|
|
10
|
-
export declare function
|
|
9
|
+
/** Resolves the style of each output stream, throwing a `UsageError` when the variable doesn't name a setting. */
|
|
10
|
+
export declare function resolveStyles(options: ResolveStylesOptions): StreamStyles;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { describeInvalidOutputStyle, resolveStreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { UsageError } from '@williamthorsen/nmr-core/cli';
|
|
3
|
+
export const OUTPUT_STYLE_ENV_VAR = 'RELEASE_KIT_OUTPUT_STYLE';
|
|
4
|
+
export function resolveStyles(options) {
|
|
5
|
+
const { invalid, styles } = resolveStreamStyles({ ...options, envVar: OUTPUT_STYLE_ENV_VAR });
|
|
6
|
+
if (invalid !== undefined) {
|
|
7
|
+
throw new UsageError(describeInvalidOutputStyle(invalid));
|
|
8
|
+
}
|
|
9
|
+
return styles;
|
|
10
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { resolveSelfVersion } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { createCli, runCli } from '@williamthorsen/nmr-core/cli';
|
|
4
|
+
import { BIN } from "./bin.js";
|
|
5
|
+
import { init, overrides, showTagPrefixes, syncLabels, updateTemplates } from "./cli/maintenanceCommands.js";
|
|
6
|
+
import { commit, createGithubRelease, prepare, publish, push, tag } from "./cli/releaseCommands.js";
|
|
7
|
+
import { OUTPUT_STYLE_ENV_VAR, resolveStyles } from "./resolveStyles.js";
|
|
8
|
+
const VERSION = resolveSelfVersion(import.meta.url);
|
|
9
|
+
export async function routeCommand(args) {
|
|
10
|
+
return runCli(args, ROOT, {
|
|
11
|
+
name: BIN,
|
|
12
|
+
context: { env: process.env, stderrIsTty: process.stderr.isTTY, stdoutIsTty: process.stdout.isTTY },
|
|
13
|
+
stdout: process.stdout,
|
|
14
|
+
stderr: process.stderr,
|
|
15
|
+
version: () => VERSION,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const { defineGroup } = createCli();
|
|
19
|
+
const ROOT = defineGroup({
|
|
20
|
+
summary: 'Version-bumping and changelog-generation toolkit for release workflows',
|
|
21
|
+
deriveContext: (_flags, context) => ({ styles: resolveStyles(context) }),
|
|
22
|
+
commands: {
|
|
23
|
+
prepare,
|
|
24
|
+
commit,
|
|
25
|
+
tag,
|
|
26
|
+
push,
|
|
27
|
+
publish,
|
|
28
|
+
'create-github-release': createGithubRelease,
|
|
29
|
+
'show-tag-prefixes': showTagPrefixes,
|
|
30
|
+
init,
|
|
31
|
+
'update-templates': updateTemplates,
|
|
32
|
+
overrides,
|
|
33
|
+
'sync-labels': syncLabels,
|
|
34
|
+
},
|
|
35
|
+
epilog: `Environment:
|
|
36
|
+
${OUTPUT_STYLE_ENV_VAR}
|
|
37
|
+
Output style: auto (default), plain, or rich. auto prints plain, without emoji,
|
|
38
|
+
when CI is set or the stream is not a terminal.`,
|
|
39
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatStatusLine, isAnticipatedFailure, reportError, } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { BIN } from "./bin.js";
|
|
2
3
|
import { detectRepoType } from "./init/detectRepoType.js";
|
|
3
4
|
import { loadValidatedConfig, reportConfigProblem } from "./loadValidatedConfig.js";
|
|
4
5
|
import { previewTagPrefixes } from "./previewTagPrefixes.js";
|
|
@@ -20,7 +21,7 @@ export async function showTagPrefixesCommand(styles, configPath) {
|
|
|
20
21
|
if (!isAnticipatedFailure(error)) {
|
|
21
22
|
throw error;
|
|
22
23
|
}
|
|
23
|
-
reportError(error.message);
|
|
24
|
+
reportError(BIN, error.message);
|
|
24
25
|
return 1;
|
|
25
26
|
}
|
|
26
27
|
process.stdout.write(renderMonorepo(preview, styles.stdout));
|
|
@@ -3,6 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { isAnticipatedFailure, reportError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
5
|
import { stringify } from 'yaml';
|
|
6
|
+
import { BIN } from "../bin.js";
|
|
6
7
|
import { CONFIG_FILE_PATH } from "../loadConfig.js";
|
|
7
8
|
import { loadValidatedConfig, reportConfigProblem } from "../loadValidatedConfig.js";
|
|
8
9
|
import { hashPreset } from "./presets.js";
|
|
@@ -28,7 +29,7 @@ export function formatLabelsYaml(labels, presetHashes, configFilePath = CONFIG_F
|
|
|
28
29
|
export async function loadRepoLabelsConfig(stderrStyle, configPath) {
|
|
29
30
|
const result = await loadValidatedConfig(configPath);
|
|
30
31
|
if (result.status === 'missing') {
|
|
31
|
-
reportError(`Config file not found at ${result.configFilePath}. Run \`release-kit sync-labels init\` first.`);
|
|
32
|
+
reportError(BIN, `Config file not found at ${result.configFilePath}. Run \`release-kit sync-labels init\` first.`);
|
|
32
33
|
return undefined;
|
|
33
34
|
}
|
|
34
35
|
if (result.status === 'invalid') {
|
|
@@ -36,7 +37,7 @@ export async function loadRepoLabelsConfig(stderrStyle, configPath) {
|
|
|
36
37
|
return undefined;
|
|
37
38
|
}
|
|
38
39
|
if (result.config.repoLabels === undefined) {
|
|
39
|
-
reportError(`The \`repoLabels\` block is missing from ${result.configFilePath}. Run \`release-kit sync-labels init\` to seed one.`);
|
|
40
|
+
reportError(BIN, `The \`repoLabels\` block is missing from ${result.configFilePath}. Run \`release-kit sync-labels init\` to seed one.`);
|
|
40
41
|
return undefined;
|
|
41
42
|
}
|
|
42
43
|
return result.config.repoLabels;
|
|
@@ -58,7 +59,7 @@ export async function generateCommand({ shouldCheck = false, configPath, styles
|
|
|
58
59
|
throw error;
|
|
59
60
|
}
|
|
60
61
|
const message = describeError(error);
|
|
61
|
-
reportError(`Failed to resolve labels: ${message}`);
|
|
62
|
+
reportError(BIN, `Failed to resolve labels: ${message}`);
|
|
62
63
|
return 1;
|
|
63
64
|
}
|
|
64
65
|
const presetHashes = new Map();
|
|
@@ -76,7 +77,7 @@ export async function generateCommand({ shouldCheck = false, configPath, styles
|
|
|
76
77
|
}
|
|
77
78
|
catch (error) {
|
|
78
79
|
const message = describeError(error);
|
|
79
|
-
reportError(`Failed to write ${LABELS_OUTPUT_PATH}: ${message}`);
|
|
80
|
+
reportError(BIN, `Failed to write ${LABELS_OUTPUT_PATH}: ${message}`);
|
|
80
81
|
return 1;
|
|
81
82
|
}
|
|
82
83
|
console.info(`Generated ${LABELS_OUTPUT_PATH} with ${String(labels.length)} labels.`);
|
|
@@ -88,11 +89,11 @@ function compareAgainstCommittedFile(expectedContent) {
|
|
|
88
89
|
existingContent = readFileSync(LABELS_OUTPUT_PATH, 'utf8');
|
|
89
90
|
}
|
|
90
91
|
catch {
|
|
91
|
-
reportError(`${LABELS_OUTPUT_PATH} is missing. Run \`release-kit sync-labels generate\` and commit the result.`);
|
|
92
|
+
reportError(BIN, `${LABELS_OUTPUT_PATH} is missing. Run \`release-kit sync-labels generate\` and commit the result.`);
|
|
92
93
|
return 1;
|
|
93
94
|
}
|
|
94
95
|
if (existingContent !== expectedContent) {
|
|
95
|
-
reportError(`${LABELS_OUTPUT_PATH} is stale. Run \`release-kit sync-labels generate\` and commit the result.`);
|
|
96
|
+
reportError(BIN, `${LABELS_OUTPUT_PATH} is stale. Run \`release-kit sync-labels generate\` and commit the result.`);
|
|
96
97
|
return 1;
|
|
97
98
|
}
|
|
98
99
|
console.info(`${LABELS_OUTPUT_PATH} is up to date.`);
|
|
@@ -2,9 +2,8 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
4
|
import { CANONICAL_TAXONOMY } from '@williamthorsen/change-grammar';
|
|
5
|
-
import { findPackageRoot, UserError } from '@williamthorsen/nmr-core';
|
|
5
|
+
import { findPackageRoot, isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
6
6
|
import { parse } from 'yaml';
|
|
7
|
-
import { isRecord } from "../typeGuards.js";
|
|
8
7
|
import { chainUserError } from "../user-error-chaining.js";
|
|
9
8
|
import { deriveTypeLabels } from "./typeLabels.js";
|
|
10
9
|
const TYPE_LABELS_PRESET = 'common';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { reportError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { BIN } from "../bin.js";
|
|
3
4
|
import { CONFIG_FILE_PATH } from "../loadConfig.js";
|
|
4
5
|
export const RETIRED_SYNC_LABELS_CONFIG_PATH = '.config/sync-labels.config.ts';
|
|
5
6
|
export function checkRetiredSyncLabelsConfig(configPath = CONFIG_FILE_PATH) {
|
|
6
7
|
if (!existsSync(RETIRED_SYNC_LABELS_CONFIG_PATH)) {
|
|
7
8
|
return false;
|
|
8
9
|
}
|
|
9
|
-
reportError(`${RETIRED_SYNC_LABELS_CONFIG_PATH} is no longer read. Move its labels into the \`repoLabels\` block of ${configPath} (presets move to \`repoLabels.extends\`; each label becomes a \`'name': { color, description }\` entry under \`repoLabels.labels\`), then delete the file.`);
|
|
10
|
+
reportError(BIN, `${RETIRED_SYNC_LABELS_CONFIG_PATH} is no longer read. Move its labels into the \`repoLabels\` block of ${configPath} (presets move to \`repoLabels.extends\`; each label becomes a \`'name': { color, description }\` entry under \`repoLabels.labels\`), then delete the file.`);
|
|
10
11
|
return true;
|
|
11
12
|
}
|
|
@@ -2,6 +2,7 @@ import { execSync } from 'node:child_process';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { GIT_OUTPUT_LIMIT_BYTES, reportError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
|
+
import { BIN } from "../bin.js";
|
|
5
6
|
import { checkRetiredSyncLabelsConfig } from "./retiredConfig.js";
|
|
6
7
|
const WORKFLOW_FILE = '.github/workflows/sync-labels.yaml';
|
|
7
8
|
function checkGhAvailable() {
|
|
@@ -18,11 +19,11 @@ export function syncLabelsCommand() {
|
|
|
18
19
|
return 1;
|
|
19
20
|
}
|
|
20
21
|
if (!checkGhAvailable()) {
|
|
21
|
-
reportError('The `gh` CLI is not installed or not in PATH. Install it from https://cli.github.com/');
|
|
22
|
+
reportError(BIN, 'The `gh` CLI is not installed or not in PATH. Install it from https://cli.github.com/');
|
|
22
23
|
return 1;
|
|
23
24
|
}
|
|
24
25
|
if (!existsSync(WORKFLOW_FILE)) {
|
|
25
|
-
reportError(`${WORKFLOW_FILE} not found. Run \`release-kit sync-labels init\` first.`);
|
|
26
|
+
reportError(BIN, `${WORKFLOW_FILE} not found. Run \`release-kit sync-labels init\` first.`);
|
|
26
27
|
return 1;
|
|
27
28
|
}
|
|
28
29
|
try {
|
|
@@ -30,7 +31,7 @@ export function syncLabelsCommand() {
|
|
|
30
31
|
}
|
|
31
32
|
catch (error) {
|
|
32
33
|
const message = describeError(error);
|
|
33
|
-
reportError(`Failed to trigger workflow: ${message}`);
|
|
34
|
+
reportError(BIN, `Failed to trigger workflow: ${message}`);
|
|
34
35
|
return 1;
|
|
35
36
|
}
|
|
36
37
|
console.info('Workflow triggered successfully. View runs at: gh run list --workflow=sync-labels.yaml');
|
package/dist/esm/tagCommand.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface TagCommandOptions {
|
|
3
|
+
isDryRun: boolean;
|
|
4
|
+
shouldSkipGitChecks: boolean;
|
|
5
|
+
styles: StreamStyles;
|
|
6
|
+
}
|
|
7
|
+
/** Runs the CLI `tag` command by delegating to `createTags`. */
|
|
8
|
+
export declare function tagCommand({ isDryRun, shouldSkipGitChecks, styles }: TagCommandOptions): void;
|
package/dist/esm/tagCommand.js
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import { parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
2
1
|
import { createTags } from "./createTags.js";
|
|
3
|
-
|
|
4
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
5
|
-
shouldSkipGitChecks: { long: '--no-git-checks', type: 'boolean' },
|
|
6
|
-
};
|
|
7
|
-
export function tagCommand(argv, styles) {
|
|
8
|
-
const { isDryRun, shouldSkipGitChecks } = parseArgsOrExit(argv, tagFlagSchema).flags;
|
|
2
|
+
export function tagCommand({ isDryRun, shouldSkipGitChecks, styles }) {
|
|
9
3
|
createTags({ isDryRun, shouldSkipGitChecks, style: styles.stdout });
|
|
10
4
|
}
|