@williamthorsen/release-kit 16.1.2 → 17.0.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/.meta/changelog.json +62 -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 +22 -0
- package/README.md +7 -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/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 +3 -3
- 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
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { createCli } from '@williamthorsen/nmr-core/cli';
|
|
3
|
+
import { enterRepoRoot } from "../enterRepoRoot.js";
|
|
4
|
+
import { initCommand } from "../init/initCommand.js";
|
|
5
|
+
import { updateTemplatesCommand } from "../init/updateTemplatesCommand.js";
|
|
6
|
+
import { showTagPrefixesCommand } from "../showTagPrefixesCommand.js";
|
|
7
|
+
import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
8
|
+
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
9
|
+
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
10
|
+
import { validateOverridesCommand } from "../validateOverridesCommand.js";
|
|
11
|
+
import { CONFIG_FLAG, runReportingFailures } from "./commandKit.js";
|
|
12
|
+
const { defineCommand, defineGroup } = createCli();
|
|
13
|
+
export const showTagPrefixes = defineCommand({
|
|
14
|
+
summary: 'Show derived and declared legacy tag prefixes per workspace',
|
|
15
|
+
description: 'Print a per-workspace table of derived tag prefixes, tag counts, and declared legacy tag prefixes. Reports any release-shaped tags whose prefix is neither a derived prefix nor declared in `legacyIdentities`, with a copy-pasteable config snippet.',
|
|
16
|
+
flags: CONFIG_FLAG,
|
|
17
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
18
|
+
enterRepoRoot();
|
|
19
|
+
return showTagPrefixesCommand(context.styles, flags.config);
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
export const init = defineCommand({
|
|
23
|
+
summary: 'Initialize release-kit in the current repository',
|
|
24
|
+
description: 'Initialize release-kit in the current repository. By default, scaffolds only the GitHub Actions workflow files. When every package is private, skips publish.yaml and create-github-release.yaml.',
|
|
25
|
+
flags: {
|
|
26
|
+
shouldScaffoldConfig: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
long: 'with-config',
|
|
29
|
+
description: 'Also scaffold .config/release-kit.config.ts',
|
|
30
|
+
},
|
|
31
|
+
shouldForce: { type: 'boolean', long: 'force', description: 'Overwrite existing files instead of skipping them' },
|
|
32
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview changes without writing files' },
|
|
33
|
+
},
|
|
34
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
35
|
+
enterRepoRoot();
|
|
36
|
+
return initCommand({
|
|
37
|
+
isDryRun: flags.isDryRun,
|
|
38
|
+
shouldForce: flags.shouldForce,
|
|
39
|
+
shouldScaffoldConfig: flags.shouldScaffoldConfig,
|
|
40
|
+
styles: context.styles,
|
|
41
|
+
});
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
export const updateTemplates = defineCommand({
|
|
45
|
+
summary: 'Update the scaffolded workflows to the current templates',
|
|
46
|
+
description: "Update the GitHub Actions workflows that release-kit scaffolds to the templates of the installed version, showing what changed in each file. Creates a missing workflow that `init` would create, and refreshes sync-labels.yaml only when it exists. When every package is private, refreshes publish.yaml and create-github-release.yaml only when they exist. Keeps publish.yaml's provenance setting. Never touches config files.",
|
|
47
|
+
flags: {
|
|
48
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview changes without writing files' },
|
|
49
|
+
},
|
|
50
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
51
|
+
enterRepoRoot();
|
|
52
|
+
return updateTemplatesCommand({ isDryRun: flags.isDryRun, styles: context.styles });
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
const overridesValidate = defineCommand({
|
|
56
|
+
summary: 'Validate every `.meta/changelog-overrides.json` file across all scopes',
|
|
57
|
+
description: 'Validate every `.meta/changelog-overrides.json` file across the project and per-workspace scopes. Reports schema/parse errors, ambiguous-prefix errors, conflicting-key errors, and stale-key warnings.',
|
|
58
|
+
flags: CONFIG_FLAG,
|
|
59
|
+
epilog: `Exit codes:
|
|
60
|
+
0 Clean: no errors, no stale keys
|
|
61
|
+
1 Stale-key warnings only (no errors)
|
|
62
|
+
2 Schema/parse, ambiguous-prefix, or conflicting-key errors (errors take precedence)`,
|
|
63
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, async () => {
|
|
64
|
+
enterRepoRoot();
|
|
65
|
+
const result = await validateOverridesCommand(context.styles, flags.config);
|
|
66
|
+
if (result.exitCode === 0) {
|
|
67
|
+
process.stdout.write(`${result.message}\n`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
process.stderr.write(`${result.message}\n`);
|
|
71
|
+
}
|
|
72
|
+
return result.exitCode;
|
|
73
|
+
}),
|
|
74
|
+
});
|
|
75
|
+
export const overrides = defineGroup({
|
|
76
|
+
summary: 'Manage editorial changelog overrides',
|
|
77
|
+
commands: { validate: overridesValidate },
|
|
78
|
+
});
|
|
79
|
+
const syncLabelsInit = defineCommand({
|
|
80
|
+
summary: 'Scaffold caller workflow and config, then generate labels',
|
|
81
|
+
description: 'Scaffold the sync-labels caller workflow and seed the repoLabels block of .config/release-kit.config.ts with scope labels discovered from workspaces and retired packages, then generate .github/labels.yaml. When the config file already exists, print the block for manual paste instead of rewriting the file.',
|
|
82
|
+
flags: {
|
|
83
|
+
...CONFIG_FLAG,
|
|
84
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview changes without writing files' },
|
|
85
|
+
shouldForce: { type: 'boolean', long: 'force', description: 'Overwrite existing files instead of skipping them' },
|
|
86
|
+
},
|
|
87
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
88
|
+
enterRepoRoot();
|
|
89
|
+
return syncLabelsInitCommand({
|
|
90
|
+
...(flags.config !== undefined && { configPath: flags.config }),
|
|
91
|
+
isDryRun: flags.isDryRun,
|
|
92
|
+
shouldForce: flags.shouldForce,
|
|
93
|
+
styles: context.styles,
|
|
94
|
+
});
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
const syncLabelsGenerate = defineCommand({
|
|
98
|
+
summary: 'Regenerate .github/labels.yaml from config',
|
|
99
|
+
description: 'Regenerate .github/labels.yaml from the repoLabels block of .config/release-kit.config.ts.',
|
|
100
|
+
flags: {
|
|
101
|
+
shouldCheck: {
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
long: 'check',
|
|
104
|
+
description: 'Report whether .github/labels.yaml is stale instead of writing it',
|
|
105
|
+
},
|
|
106
|
+
...CONFIG_FLAG,
|
|
107
|
+
},
|
|
108
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
109
|
+
enterRepoRoot();
|
|
110
|
+
return generateCommand({
|
|
111
|
+
shouldCheck: flags.shouldCheck,
|
|
112
|
+
...(flags.config !== undefined && { configPath: flags.config }),
|
|
113
|
+
styles: context.styles,
|
|
114
|
+
});
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
const syncLabelsSync = defineCommand({
|
|
118
|
+
summary: 'Trigger the sync-labels workflow via gh CLI',
|
|
119
|
+
description: 'Trigger the sync-labels GitHub Actions workflow via the gh CLI.',
|
|
120
|
+
run: ({ context }) => runReportingFailures(context.styles, () => {
|
|
121
|
+
enterRepoRoot();
|
|
122
|
+
return syncLabelsCommand();
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
export const syncLabels = defineGroup({
|
|
126
|
+
summary: 'Manage GitHub label synchronization',
|
|
127
|
+
description: 'Manage GitHub label synchronization via preset and custom label definitions.',
|
|
128
|
+
commands: { init: syncLabelsInit, generate: syncLabelsGenerate, sync: syncLabelsSync },
|
|
129
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type CommandContext } from './commandKit.js';
|
|
2
|
+
export declare const prepare: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
3
|
+
export declare const commit: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
4
|
+
export declare const tag: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
5
|
+
export declare const push: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
6
|
+
export declare const publish: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
7
|
+
export declare const createGithubRelease: import("@williamthorsen/nmr-core/cli").Command<CommandContext>;
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createCli } from '@williamthorsen/nmr-core/cli';
|
|
2
|
+
import { commitCommand } from "../commitCommand.js";
|
|
3
|
+
import { createGithubReleaseCommand } from "../createGithubReleaseCommand.js";
|
|
4
|
+
import { enterRepoRoot } from "../enterRepoRoot.js";
|
|
5
|
+
import { parseRequestedTags } from "../parseRequestedTags.js";
|
|
6
|
+
import { prepareCommand } from "../prepareCommand.js";
|
|
7
|
+
import { publishCommand } from "../publishCommand.js";
|
|
8
|
+
import { pushCommand } from "../pushCommand.js";
|
|
9
|
+
import { tagCommand } from "../tagCommand.js";
|
|
10
|
+
import { CONFIG_FLAG, runReportingFailures } from "./commandKit.js";
|
|
11
|
+
const { defineCommand } = createCli();
|
|
12
|
+
const TAGS_FLAG = {
|
|
13
|
+
type: 'string',
|
|
14
|
+
valueHint: 'tag1,tag2',
|
|
15
|
+
};
|
|
16
|
+
export const prepare = defineCommand({
|
|
17
|
+
summary: 'Run release preparation (auto-discovers workspaces)',
|
|
18
|
+
description: 'Run release preparation with automatic workspace discovery.',
|
|
19
|
+
flags: {
|
|
20
|
+
...CONFIG_FLAG,
|
|
21
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Run without modifying any files' },
|
|
22
|
+
bump: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Set the level of every release that happens; does not trigger one',
|
|
25
|
+
choices: ['major', 'minor', 'patch'],
|
|
26
|
+
},
|
|
27
|
+
setVersion: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
long: 'set-version',
|
|
30
|
+
description: "Set an explicit version; bypasses the changelog-derived bump. In monorepo mode, name each workspace or directory target: --set-version=arrays@1.0.0[,strings@2.0.0]. Rejected when a composite (a 'project' block or a releases composite) is configured.",
|
|
31
|
+
valueHint: 'X.Y.Z',
|
|
32
|
+
},
|
|
33
|
+
shouldForce: {
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
long: 'force',
|
|
36
|
+
description: 'Release even when the last tag is not followed by any commits, or by any bump-worthy ones. Defaults to patch when --bump is not given; use --bump=X for a different level.',
|
|
37
|
+
},
|
|
38
|
+
shouldSkipGitChecks: {
|
|
39
|
+
type: 'boolean',
|
|
40
|
+
long: 'no-git-checks',
|
|
41
|
+
description: 'Skip the clean-working-tree check',
|
|
42
|
+
short: 'n',
|
|
43
|
+
},
|
|
44
|
+
only: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: "Release only the named release targets and their dependents (comma-separated, monorepo only). A composite declared under 'releases' releases only when named; a 'project' block's release is skipped.",
|
|
47
|
+
valueHint: 'name1,name2',
|
|
48
|
+
},
|
|
49
|
+
shouldIncludeReleaseNotes: {
|
|
50
|
+
type: 'boolean',
|
|
51
|
+
long: 'with-release-notes',
|
|
52
|
+
description: 'Also write per-workspace release-notes previews under <workspacePath>/docs/ (docs/README.v<version>.md and docs/RELEASE_NOTES.v<version>.md). Recommended .gitignore entry: packages/*/docs/*.v*.md (or docs/*.v*.md).',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, async () => {
|
|
56
|
+
enterRepoRoot();
|
|
57
|
+
await prepareCommand({
|
|
58
|
+
bumpOverride: flags.bump,
|
|
59
|
+
configPath: flags.config,
|
|
60
|
+
isDryRun: flags.isDryRun,
|
|
61
|
+
onlyNames: flags.only?.split(','),
|
|
62
|
+
setVersion: flags.setVersion,
|
|
63
|
+
shouldForce: flags.shouldForce,
|
|
64
|
+
shouldIncludeReleaseNotes: flags.shouldIncludeReleaseNotes,
|
|
65
|
+
shouldSkipGitChecks: flags.shouldSkipGitChecks,
|
|
66
|
+
styles: context.styles,
|
|
67
|
+
});
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
export const commit = defineCommand({
|
|
71
|
+
summary: 'Stage changes and create the release commit',
|
|
72
|
+
description: 'Stage all changes and create the release commit using the tags and summary produced by `prepare`.',
|
|
73
|
+
flags: {
|
|
74
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview the commit message without creating it' },
|
|
75
|
+
},
|
|
76
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
77
|
+
enterRepoRoot();
|
|
78
|
+
commitCommand({ isDryRun: flags.isDryRun });
|
|
79
|
+
}),
|
|
80
|
+
});
|
|
81
|
+
export const tag = defineCommand({
|
|
82
|
+
summary: 'Create annotated git tags from the tags file',
|
|
83
|
+
description: 'Create annotated git tags from the tags file produced by `prepare`.',
|
|
84
|
+
flags: {
|
|
85
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview without creating tags' },
|
|
86
|
+
shouldSkipGitChecks: { type: 'boolean', long: 'no-git-checks', description: 'Skip the dirty-working-tree check' },
|
|
87
|
+
},
|
|
88
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, () => {
|
|
89
|
+
enterRepoRoot();
|
|
90
|
+
tagCommand({ isDryRun: flags.isDryRun, shouldSkipGitChecks: flags.shouldSkipGitChecks, styles: context.styles });
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
export const push = defineCommand({
|
|
94
|
+
summary: 'Push the release commit and tags (one push per tag)',
|
|
95
|
+
description: 'Push the release commit and each tag individually, so that GitHub Actions starts a separate workflow run for each tag.',
|
|
96
|
+
flags: {
|
|
97
|
+
...CONFIG_FLAG,
|
|
98
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview without pushing' },
|
|
99
|
+
tags: { ...TAGS_FLAG, description: 'Only push the named tags (comma-separated, full tag names)' },
|
|
100
|
+
tagsOnly: { type: 'boolean', description: 'Skip the branch push (push tags only)' },
|
|
101
|
+
},
|
|
102
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, async () => {
|
|
103
|
+
enterRepoRoot();
|
|
104
|
+
await pushCommand({
|
|
105
|
+
configPath: flags.config,
|
|
106
|
+
isDryRun: flags.isDryRun,
|
|
107
|
+
tags: parseRequestedTags(flags.tags),
|
|
108
|
+
tagsOnly: flags.tagsOnly,
|
|
109
|
+
styles: context.styles,
|
|
110
|
+
});
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
export const publish = defineCommand({
|
|
114
|
+
summary: 'Publish packages with release tags on HEAD',
|
|
115
|
+
description: 'Publish packages that have release tags on HEAD. Operates only on workspaces whose package.json#private is absent or false. Without --tags, unpublishable workspaces are silently filtered out. With --tags, an unpublishable tag is skipped with a warning while any publishable tags are still published. A project tag is never published.',
|
|
116
|
+
flags: {
|
|
117
|
+
...CONFIG_FLAG,
|
|
118
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview without publishing' },
|
|
119
|
+
shouldSkipGitChecks: { type: 'boolean', long: 'no-git-checks', description: 'Skip the clean-working-tree check' },
|
|
120
|
+
tags: { ...TAGS_FLAG, description: 'Only publish the named tags (comma-separated, full tag names)' },
|
|
121
|
+
shouldGenerateProvenance: {
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
long: 'provenance',
|
|
124
|
+
description: 'Generate a provenance statement (requires OIDC; not supported by classic yarn)',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, async () => {
|
|
128
|
+
enterRepoRoot();
|
|
129
|
+
await publishCommand({
|
|
130
|
+
configPath: flags.config,
|
|
131
|
+
isDryRun: flags.isDryRun,
|
|
132
|
+
shouldGenerateProvenance: flags.shouldGenerateProvenance,
|
|
133
|
+
shouldSkipGitChecks: flags.shouldSkipGitChecks,
|
|
134
|
+
tags: parseRequestedTags(flags.tags),
|
|
135
|
+
styles: context.styles,
|
|
136
|
+
});
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
export const createGithubRelease = defineCommand({
|
|
140
|
+
summary: 'Create GitHub Releases from changelog.json for tags on HEAD',
|
|
141
|
+
description: "Create GitHub Releases from changelog.json for tags on HEAD. Private (package.json#private) workspaces are skipped with a warning, and the command doesn't create a Release for them. A project tag gets a Release even when the root package is private.",
|
|
142
|
+
flags: {
|
|
143
|
+
...CONFIG_FLAG,
|
|
144
|
+
isDryRun: { type: 'boolean', long: 'dry-run', description: 'Preview without creating releases' },
|
|
145
|
+
tags: { ...TAGS_FLAG, description: 'Only create releases for the named tags (comma-separated, full tag names)' },
|
|
146
|
+
},
|
|
147
|
+
run: ({ flags, context }) => runReportingFailures(context.styles, async () => {
|
|
148
|
+
enterRepoRoot();
|
|
149
|
+
await createGithubReleaseCommand({
|
|
150
|
+
configPath: flags.config,
|
|
151
|
+
isDryRun: flags.isDryRun,
|
|
152
|
+
tags: parseRequestedTags(flags.tags),
|
|
153
|
+
styles: context.styles,
|
|
154
|
+
});
|
|
155
|
+
}),
|
|
156
|
+
});
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import process from 'node:process';
|
|
3
3
|
import { reportFailure } from '@williamthorsen/nmr-core';
|
|
4
|
-
import {
|
|
4
|
+
import { BIN } from "./bin.js";
|
|
5
|
+
import { routeCommand } from "./route.js";
|
|
5
6
|
try {
|
|
6
|
-
await
|
|
7
|
+
process.exitCode = await routeCommand(process.argv.slice(2));
|
|
7
8
|
}
|
|
8
9
|
catch (error) {
|
|
9
|
-
reportFailure(
|
|
10
|
+
reportFailure(BIN, error);
|
|
10
11
|
process.exitCode = 1;
|
|
11
12
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
export interface CommitCommandOptions {
|
|
2
|
+
isDryRun: boolean;
|
|
3
|
+
}
|
|
1
4
|
/**
|
|
2
5
|
* Runs the CLI `commit` command: reads the tags and summary files written by `prepare`, stages all changes, and
|
|
3
6
|
* creates the release commit.
|
|
4
7
|
*/
|
|
5
|
-
export declare function commitCommand(
|
|
8
|
+
export declare function commitCommand({ isDryRun }: CommitCommandOptions): void;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { GIT_OUTPUT_LIMIT_BYTES, hasErrnoCode,
|
|
3
|
+
import { GIT_OUTPUT_LIMIT_BYTES, hasErrnoCode, UserError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { readReleaseTags, RELEASE_SUMMARY_FILE, resolveReleaseTagsPath } from "./releaseFiles.js";
|
|
5
5
|
import { asUserError } from "./user-error-chaining.js";
|
|
6
|
-
|
|
7
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
8
|
-
};
|
|
9
|
-
export function commitCommand(argv) {
|
|
10
|
-
const isDryRun = parseArgsOrExit(argv, commitFlagSchema).flags.isDryRun;
|
|
6
|
+
export function commitCommand({ isDryRun }) {
|
|
11
7
|
const tags = readReleaseTags();
|
|
12
8
|
if (tags.length === 0) {
|
|
13
9
|
throw new UserError(`Tags file at ${resolveReleaseTagsPath()} is empty. Run \`release-kit prepare\` first.`);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
export interface CreateGithubReleaseCommandOptions {
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
isDryRun: boolean;
|
|
5
|
+
/** The tag names that `--tags` selects, or `undefined` for every release tag on HEAD. */
|
|
6
|
+
tags: string[] | undefined;
|
|
7
|
+
styles: StreamStyles;
|
|
8
|
+
}
|
|
2
9
|
/**
|
|
3
10
|
* Runs the CLI `create-github-release` command: creates GitHub Releases from `changelog.json` for the tags on HEAD, or
|
|
4
11
|
* a comma-separated `--tags` subset, without publishing to npm.
|
|
5
12
|
*
|
|
6
13
|
* A private workspace's tag is skipped with a warning and doesn't get a Release; an all-private tag set is a clean
|
|
7
|
-
* no-op. A project tag gets a Release whatever the root `package.json#private` says.
|
|
8
|
-
* against `invocationDir`.
|
|
14
|
+
* no-op. A project tag gets a Release whatever the root `package.json#private` says.
|
|
9
15
|
*/
|
|
10
|
-
export declare function createGithubReleaseCommand(
|
|
16
|
+
export declare function createGithubReleaseCommand({ configPath, isDryRun, tags, styles, }: CreateGithubReleaseCommandOptions): Promise<void>;
|
|
@@ -1,25 +1,12 @@
|
|
|
1
|
-
import { parseArgsOrExit } from '@williamthorsen/nmr-core';
|
|
2
|
-
import { configFlagSchema } from "./configFlagSchema.js";
|
|
3
1
|
import { createGithubReleases } from "./createGithubRelease.js";
|
|
4
2
|
import { deriveReleaseNotesConfig } from "./deriveReleaseNotesConfig.js";
|
|
5
3
|
import { formatPrivateSkip } from "./formatPrivateSkip.js";
|
|
6
4
|
import { loadUsableConfig } from "./loadValidatedConfig.js";
|
|
7
|
-
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
8
5
|
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
9
|
-
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
10
6
|
import { prefixUserError } from "./user-error-chaining.js";
|
|
11
|
-
|
|
12
|
-
...configFlagSchema,
|
|
13
|
-
isDryRun: { long: '--dry-run', type: 'boolean' },
|
|
14
|
-
tags: { long: '--tags', type: 'string' },
|
|
15
|
-
};
|
|
16
|
-
export async function createGithubReleaseCommand(argv, styles, invocationDir) {
|
|
17
|
-
const parsedArgs = parseArgsOrExit(argv, createGithubReleaseFlagSchema);
|
|
18
|
-
const configPath = resolveConfigFlag(parsedArgs.flags.config, invocationDir);
|
|
19
|
-
const { isDryRun } = parsedArgs.flags;
|
|
7
|
+
export async function createGithubReleaseCommand({ configPath, isDryRun, tags, styles, }) {
|
|
20
8
|
const userConfig = await loadUsableConfig(styles.stderr, configPath);
|
|
21
|
-
const
|
|
22
|
-
const resolvedTags = resolveCommandTags(requestedTags, userConfig);
|
|
9
|
+
const resolvedTags = resolveCommandTags(tags, userConfig);
|
|
23
10
|
const releasableTags = resolvedTags.filter(isReleasable);
|
|
24
11
|
for (const resolvedTag of resolvedTags) {
|
|
25
12
|
if (!isReleasable(resolvedTag)) {
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { UserError } from '@williamthorsen/nmr-core';
|
|
4
|
-
import { isRecord } from "./typeGuards.js";
|
|
3
|
+
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
5
4
|
import { chainUserError } from "./user-error-chaining.js";
|
|
6
5
|
export const DIRECTORY_VERSION_FILE = 'version.json';
|
|
7
6
|
const TAG_PREFIX_PATTERN = /^[\w.-]+$/;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { basename } from 'node:path';
|
|
3
|
-
import { UserError } from '@williamthorsen/nmr-core';
|
|
3
|
+
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { isPublishableManifest } from "./isPublishableManifest.js";
|
|
5
|
-
import { isRecord } from "./typeGuards.js";
|
|
6
5
|
import { chainUserError } from "./user-error-chaining.js";
|
|
7
6
|
export function deriveWorkspaceConfig(workspacePath) {
|
|
8
7
|
const dir = basename(workspacePath);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { isRecord } from
|
|
3
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
4
4
|
export function detectPackageManager() {
|
|
5
5
|
const packageJsonPath = join(process.cwd(), 'package.json');
|
|
6
6
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRecord } from
|
|
1
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
2
2
|
const BUILT_IN_DEV_CONDITION = 'source';
|
|
3
3
|
export function findMissingManifestPaths(manifest, packedFiles, devExportConditions) {
|
|
4
4
|
const packedSet = new Set(packedFiles);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { isRecord } from
|
|
3
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
4
4
|
export function hasPrettierConfig() {
|
|
5
5
|
const cwd = process.cwd();
|
|
6
6
|
for (const file of PRETTIER_CONFIG_FILES) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRecord } from
|
|
1
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
2
2
|
export function isPublishableManifest(manifest) {
|
|
3
3
|
const privateField = isRecord(manifest) ? manifest['private'] : undefined;
|
|
4
4
|
return privateField === undefined || privateField === false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
2
3
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
|
-
import { isRecord } from "./typeGuards.js";
|
|
4
4
|
const VALID_AUDIENCE_VALUES = new Set(['all', 'dev', 'skip']);
|
|
5
5
|
const V1_SUPPORTED_AUDIENCE_VALUES = new Set(['skip']);
|
|
6
6
|
const SCHEMA_KEY = '$schema';
|
package/dist/esm/loadConfig.js
CHANGED
|
@@ -9,8 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
9
9
|
import { existsSync, readFileSync } from 'node:fs';
|
|
10
10
|
import path from 'node:path';
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
|
-
import { UserError } from '@williamthorsen/nmr-core';
|
|
13
|
-
import { isRecord } from "./typeGuards.js";
|
|
12
|
+
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
14
13
|
import { chainUserError } from "./user-error-chaining.js";
|
|
15
14
|
export const ROOT_PACKAGE_JSON_PATH = 'package.json';
|
|
16
15
|
export function readRootPackageVersion() {
|
|
@@ -12,6 +12,11 @@ export type ConfigProblem = {
|
|
|
12
12
|
kind: 'validation';
|
|
13
13
|
errors: string[];
|
|
14
14
|
};
|
|
15
|
+
/** Reports a config file that a CLI command cannot use, carrying the problem for `reportConfigProblem` to render. */
|
|
16
|
+
export declare class ConfigProblemError extends Error {
|
|
17
|
+
readonly problem: ConfigProblem;
|
|
18
|
+
constructor(problem: ConfigProblem);
|
|
19
|
+
}
|
|
15
20
|
/**
|
|
16
21
|
* Outcome of loading and validating the consumer config file. `missing` is not an error here; the caller decides
|
|
17
22
|
* whether an absent config file is one.
|
|
@@ -47,7 +52,7 @@ export declare function loadValidatedConfig(configPath?: string): Promise<LoadVa
|
|
|
47
52
|
* Loads and validates the config for a CLI command, returning it, or `undefined` when the default config file does
|
|
48
53
|
* not exist.
|
|
49
54
|
*
|
|
50
|
-
*
|
|
55
|
+
* Throws a `ConfigProblemError` for an unusable config, whether the file failed to load or failed validation, and
|
|
51
56
|
* whether it was named by the caller or the default one. Validation warnings print to stderr.
|
|
52
57
|
*/
|
|
53
58
|
export declare function loadUsableConfig(stderrStyle: OutputStyle, configPath?: string): Promise<ReleaseKitConfig | undefined>;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { formatStatusLine, printError, reportError } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
|
+
import { BIN } from "./bin.js";
|
|
3
4
|
import { CONFIG_FILE_PATH, loadConfig } from "./loadConfig.js";
|
|
4
5
|
import { validateConfig } from "./validateConfig.js";
|
|
6
|
+
export class ConfigProblemError extends Error {
|
|
7
|
+
problem;
|
|
8
|
+
constructor(problem) {
|
|
9
|
+
super('Invalid config');
|
|
10
|
+
this.name = 'ConfigProblemError';
|
|
11
|
+
this.problem = problem;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
5
14
|
export async function loadValidatedConfig(configPath) {
|
|
6
15
|
const configFilePath = configPath ?? CONFIG_FILE_PATH;
|
|
7
16
|
let rawConfig;
|
|
@@ -23,8 +32,7 @@ export async function loadValidatedConfig(configPath) {
|
|
|
23
32
|
export async function loadUsableConfig(stderrStyle, configPath) {
|
|
24
33
|
const result = await loadValidatedConfig(configPath);
|
|
25
34
|
if (result.status === 'invalid') {
|
|
26
|
-
|
|
27
|
-
process.exit(1);
|
|
35
|
+
throw new ConfigProblemError(result.problem);
|
|
28
36
|
}
|
|
29
37
|
if (result.status === 'missing') {
|
|
30
38
|
return undefined;
|
|
@@ -34,7 +42,7 @@ export async function loadUsableConfig(stderrStyle, configPath) {
|
|
|
34
42
|
}
|
|
35
43
|
export function reportConfigProblem(problem, stderrStyle) {
|
|
36
44
|
if (problem.kind === 'load') {
|
|
37
|
-
reportError(`Failed to load config: ${problem.message}`);
|
|
45
|
+
reportError(BIN, `Failed to load config: ${problem.message}`);
|
|
38
46
|
return;
|
|
39
47
|
}
|
|
40
48
|
process.stderr.write('Invalid config:\n');
|
package/dist/esm/npmRegistry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execFileSync } from 'node:child_process';
|
|
2
|
-
import { isRecord } from
|
|
2
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
3
3
|
export const AUTH_ERROR_CODES = new Set(['E401', 'ENEEDAUTH']);
|
|
4
4
|
export const UNREACHABLE_ERROR_CODES = new Set([
|
|
5
5
|
'EAI_AGAIN',
|
|
@@ -2,9 +2,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { UserError } from '@williamthorsen/nmr-core';
|
|
5
|
+
import { isRecord, UserError } from '@williamthorsen/nmr-core';
|
|
6
6
|
import { resolveExecutable } from "./detectPackageManager.js";
|
|
7
|
-
import { isRecord } from "./typeGuards.js";
|
|
8
7
|
import { chainUserError } from "./user-error-chaining.js";
|
|
9
8
|
export function packFromCleanCheckout(workspacePaths, packageManager) {
|
|
10
9
|
const repoRoot = runStep('find the repository root', 'git', ['rev-parse', '--show-toplevel'], process.cwd()).trim();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { isRecord } from '@williamthorsen/nmr-core';
|
|
1
2
|
import { parseDocument } from 'yaml';
|
|
2
|
-
import { isRecord } from "./typeGuards.js";
|
|
3
3
|
export function parseChangeRecordBlock(message) {
|
|
4
4
|
const lines = message.split(/\r?\n/);
|
|
5
5
|
const fence = findFences(lines).at(-1);
|
|
@@ -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>;
|