@williamthorsen/release-kit 12.0.0 → 13.1.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 +229 -0
- package/.readyup/kits/default.js +18 -17
- package/.readyup/kits/npm-auto-publish.js +24 -23
- package/.readyup/manifest.json +16 -11
- package/CHANGELOG.md +64 -0
- package/README.md +27 -51
- package/bin/release-kit.js +1 -1
- package/dist/esm/assertCleanWorkingTree.d.ts +1 -5
- package/dist/esm/assertTaggedBaseline.d.ts +29 -0
- package/dist/esm/assertTaggedBaseline.js +41 -0
- package/dist/esm/attachChangelogDiagnostics.d.ts +1 -1
- package/dist/esm/attachChangelogDiagnostics.js +3 -0
- package/dist/esm/bin/release-kit.js +85 -22
- package/dist/esm/buildChangelogEntries.d.ts +23 -13
- package/dist/esm/buildChangelogEntries.js +36 -17
- package/dist/esm/buildDependencyGraph.d.ts +6 -6
- package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -8
- package/dist/esm/buildReleaseSummary.d.ts +2 -8
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +3 -5
- package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
- package/dist/esm/bumpVersion.d.ts +1 -8
- package/dist/esm/changelogJsonFile.d.ts +5 -7
- package/dist/esm/changelogJsonFile.js +2 -17
- package/dist/esm/changelogJsonUtils.d.ts +7 -10
- package/dist/esm/changelogOverrides.d.ts +59 -98
- package/dist/esm/classifyChangelogCommit.d.ts +7 -9
- package/dist/esm/classifyChangelogCommit.js +3 -2
- package/dist/esm/commitCommand.d.ts +2 -4
- package/dist/esm/compareVersions.d.ts +10 -4
- package/dist/esm/compareVersions.js +16 -0
- package/dist/esm/configFlagSchema.d.ts +1 -1
- package/dist/esm/createGithubRelease.d.ts +7 -19
- package/dist/esm/createGithubReleaseCommand.d.ts +5 -5
- package/dist/esm/createGithubReleaseCommand.js +9 -9
- package/dist/esm/createTags.d.ts +2 -3
- package/dist/esm/decideRelease.d.ts +2 -6
- package/dist/esm/defaults.d.ts +5 -24
- package/dist/esm/defaults.js +4 -5
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/deleteFileIfExists.d.ts +1 -1
- package/dist/esm/deriveReleaseNotesConfig.d.ts +20 -0
- package/dist/esm/deriveReleaseNotesConfig.js +21 -0
- package/dist/esm/deriveWorkspaceConfig.d.ts +3 -5
- package/dist/esm/detectPackageManager.d.ts +2 -4
- package/dist/esm/detectUndeclaredTagPrefixes.d.ts +4 -6
- package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
- package/dist/esm/determineBumpType.d.ts +1 -1
- package/dist/esm/discoverWorkspaces.d.ts +14 -15
- package/dist/esm/discoverWorkspaces.js +12 -12
- package/dist/esm/enterRepoRoot.d.ts +12 -0
- package/dist/esm/enterRepoRoot.js +16 -0
- package/dist/esm/enumerateReleaseWindows.d.ts +8 -8
- package/dist/esm/extractMigration.d.ts +6 -8
- package/dist/esm/findUnroutedEntryScopes.d.ts +16 -0
- package/dist/esm/findUnroutedEntryScopes.js +57 -0
- package/dist/esm/formatExcludedSkip.d.ts +3 -0
- package/dist/esm/formatExcludedSkip.js +3 -0
- package/dist/esm/formatPrivateSkip.d.ts +1 -6
- package/dist/esm/generateChangelogs.d.ts +10 -5
- package/dist/esm/glyphs.d.ts +1 -1
- package/dist/esm/help/prepareHelp.d.ts +3 -4
- package/dist/esm/help/prepareHelp.js +4 -3
- package/dist/esm/init/checks.d.ts +3 -3
- package/dist/esm/init/checks.js +4 -1
- package/dist/esm/init/detectRepoType.d.ts +2 -3
- package/dist/esm/init/hasProvenance.d.ts +2 -0
- package/dist/esm/init/hasProvenance.js +3 -0
- package/dist/esm/init/initCommand.d.ts +3 -6
- package/dist/esm/init/initCommand.js +10 -10
- package/dist/esm/init/parseJsonRecord.d.ts +1 -4
- package/dist/esm/init/scaffold.d.ts +1 -1
- package/dist/esm/init/templates.d.ts +9 -11
- package/dist/esm/init/templates.js +12 -11
- package/dist/esm/init/updateTemplatesCommand.d.ts +13 -0
- package/dist/esm/init/updateTemplatesCommand.js +64 -0
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +12 -17
- package/dist/esm/injectSection.d.ts +3 -3
- package/dist/esm/loadConfig.d.ts +16 -14
- package/dist/esm/loadConfig.js +2 -2
- package/dist/esm/loadValidatedConfig.d.ts +16 -19
- package/dist/esm/loadValidatedConfig.js +6 -1
- package/dist/esm/packsPath.d.ts +9 -9
- package/dist/esm/parseChangeRecordBlock.d.ts +2 -2
- package/dist/esm/parseCommitMessage.d.ts +17 -32
- package/dist/esm/parseCommitMessage.js +28 -37
- package/dist/esm/parseRequestedTags.d.ts +3 -3
- package/dist/esm/planReleaseNotesPreviews.d.ts +6 -6
- package/dist/esm/planVersionBump.d.ts +5 -3
- package/dist/esm/planVersionBump.js +3 -0
- package/dist/esm/prepareCommand.d.ts +4 -7
- package/dist/esm/prepareCommand.js +13 -6
- package/dist/esm/previewTagPrefixes.d.ts +8 -10
- package/dist/esm/propagateBumps.d.ts +6 -7
- package/dist/esm/publish.d.ts +1 -1
- package/dist/esm/publishCommand.d.ts +4 -3
- package/dist/esm/publishCommand.js +8 -8
- package/dist/esm/pushCommand.d.ts +4 -3
- package/dist/esm/pushCommand.js +8 -2
- package/dist/esm/pushRelease.d.ts +2 -2
- package/dist/esm/readChangelogSections.d.ts +33 -0
- package/dist/esm/readChangelogSections.js +80 -0
- package/dist/esm/releaseFiles.d.ts +5 -7
- package/dist/esm/releasePlan.d.ts +11 -13
- package/dist/esm/releasePrepare.d.ts +23 -22
- package/dist/esm/releasePrepare.js +31 -11
- package/dist/esm/releasePrepareMono.d.ts +9 -7
- package/dist/esm/releasePrepareMono.js +85 -33
- package/dist/esm/releasePrepareProject.d.ts +19 -29
- package/dist/esm/releasePrepareProject.js +31 -12
- package/dist/esm/renderChangelogMarkdown.d.ts +10 -4
- package/dist/esm/renderChangelogMarkdown.js +27 -2
- package/dist/esm/renderReleaseNotes.d.ts +3 -11
- package/dist/esm/renderReleaseNotes.js +2 -6
- package/dist/esm/reportPrepare.d.ts +4 -5
- package/dist/esm/reportPrepare.js +33 -19
- package/dist/esm/resolveCommandTags.d.ts +15 -11
- package/dist/esm/resolveCommandTags.js +35 -5
- package/dist/esm/resolveConfigFlag.d.ts +7 -0
- package/dist/esm/resolveConfigFlag.js +4 -0
- package/dist/esm/resolveReleaseTags.d.ts +6 -8
- package/dist/esm/resolveReleaseTags.js +1 -1
- package/dist/esm/resolveStylesOrExit.d.ts +2 -2
- package/dist/esm/showTagPrefixesCommand.d.ts +7 -10
- package/dist/esm/showTagPrefixesCommand.js +4 -4
- package/dist/esm/stripEmojiPrefix.d.ts +1 -0
- package/dist/esm/stripScope.d.ts +2 -2
- package/dist/esm/stripScope.js +3 -3
- package/dist/esm/sync-labels/generateCommand.d.ts +11 -10
- package/dist/esm/sync-labels/generateCommand.js +17 -9
- package/dist/esm/sync-labels/initCommand.d.ts +6 -7
- package/dist/esm/sync-labels/initCommand.js +2 -2
- package/dist/esm/sync-labels/presets.d.ts +5 -4
- package/dist/esm/sync-labels/presets.js +9 -9
- package/dist/esm/sync-labels/resolveLabels.d.ts +7 -7
- package/dist/esm/sync-labels/resolveLabels.js +2 -1
- package/dist/esm/sync-labels/retiredConfig.d.ts +4 -5
- package/dist/esm/sync-labels/syncCommand.d.ts +1 -4
- package/dist/esm/sync-labels/templates.d.ts +3 -3
- package/dist/esm/sync-labels/templates.js +3 -3
- package/dist/esm/sync-labels/typeLabels.d.ts +14 -0
- package/dist/esm/sync-labels/typeLabels.js +38 -0
- package/dist/esm/sync-labels/types.d.ts +4 -2
- package/dist/esm/tagCommand.d.ts +1 -1
- package/dist/esm/typeGuards.d.ts +2 -2
- package/dist/esm/types.d.ts +122 -93
- package/dist/esm/types.js +2 -1
- package/dist/esm/validateConfig.d.ts +2 -11
- package/dist/esm/validateConfig.js +2 -2
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -12
- package/dist/esm/validateOverridesCommand.d.ts +17 -20
- package/dist/esm/validateOverridesCommand.js +1 -1
- package/docs/api.md +6 -6
- package/docs/changelogs.md +63 -40
- package/docs/configuration.md +29 -27
- package/docs/editorial-overrides.md +23 -23
- package/docs/labels.md +37 -19
- package/docs/project-releases.md +13 -13
- package/docs/publishing.md +5 -5
- package/docs/readiness-checks.md +3 -3
- package/docs/releasing.md +50 -25
- package/docs/work-types.md +41 -57
- package/package.json +7 -6
- package/presets/labels/common.yaml +1 -41
- package/dist/esm/resolveReleaseNotesConfig.d.ts +0 -26
- package/dist/esm/resolveReleaseNotesConfig.js +0 -29
- package/dist/esm/workTypesData.d.ts +0 -50
- package/dist/esm/workTypesData.js +0 -145
- package/schemas/label-map.json +0 -24
package/dist/esm/stripScope.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const PIPE_SCOPE_PATTERN =
|
|
1
|
+
import { TICKET_PREFIX_PATTERNS } from '@williamthorsen/change-grammar';
|
|
2
|
+
const PIPE_SCOPE_PATTERN = /^[^|]+\|(.*)$/;
|
|
3
3
|
export function stripScope(message) {
|
|
4
4
|
let ticketPrefix = '';
|
|
5
5
|
let remainder = message;
|
|
6
|
-
for (const pattern of
|
|
6
|
+
for (const pattern of TICKET_PREFIX_PATTERNS) {
|
|
7
7
|
const match = remainder.match(pattern);
|
|
8
8
|
if (match) {
|
|
9
9
|
ticketPrefix += match[0];
|
|
@@ -4,34 +4,35 @@ import type { LabelDefinition } from './types.js';
|
|
|
4
4
|
/** Output path for the generated labels file. */
|
|
5
5
|
export declare const LABELS_OUTPUT_PATH = ".github/labels.yaml";
|
|
6
6
|
/**
|
|
7
|
-
* Formats resolved labels as YAML
|
|
7
|
+
* Formats resolved labels as YAML under a generated header that includes the preset hashes.
|
|
8
8
|
*
|
|
9
|
-
* `configFilePath`
|
|
10
|
-
* from; it defaults to `CONFIG_FILE_PATH`.
|
|
9
|
+
* The header records `configFilePath` so that a reader of the generated file can find the config from which it came.
|
|
11
10
|
*/
|
|
12
11
|
export declare function formatLabelsYaml(labels: LabelDefinition[], presetHashes: Map<string, string>, configFilePath?: string): string;
|
|
13
12
|
/**
|
|
14
13
|
* Loads the config file and returns its `repoLabels` block.
|
|
15
14
|
*
|
|
16
15
|
* Reports an actionable error and returns `undefined` when the config file is missing,
|
|
17
|
-
* fails to load or validate, or
|
|
18
|
-
* this run read, so that `--config` never points the reader at the default path.
|
|
16
|
+
* fails to load or validate, or lacks a `repoLabels` block. Every message names the file
|
|
17
|
+
* that this run read, so that `--config` never points the reader at the default path.
|
|
19
18
|
*/
|
|
20
19
|
export declare function loadRepoLabelsConfig(stderrStyle: OutputStyle, configPath?: string): Promise<RepoLabelsConfig | undefined>;
|
|
21
20
|
/** Options for the `sync-labels generate` subcommand. */
|
|
22
21
|
interface GenerateOptions {
|
|
23
|
-
/**
|
|
22
|
+
/** Compares the regenerated content against the committed file instead of writing. */
|
|
24
23
|
check?: boolean;
|
|
25
24
|
/** Config file to read, relative to the working directory. Defaults to `CONFIG_FILE_PATH`. */
|
|
26
25
|
configPath?: string;
|
|
27
26
|
styles: StreamStyles;
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
|
-
* Runs the `sync-labels generate` command.
|
|
29
|
+
* Runs the `sync-labels generate` command and returns the process exit code.
|
|
31
30
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
31
|
+
* Writes `.github/labels.yaml` from the config's `repoLabels` block, or, with `check`, reports whether the committed
|
|
32
|
+
* file is stale and writes nothing.
|
|
33
|
+
*
|
|
34
|
+
* The header names the config by its POSIX path relative to the working directory, the repo root, so that the file
|
|
35
|
+
* does not depend on the invocation directory or on the machine on which the command ran.
|
|
35
36
|
*/
|
|
36
37
|
export declare function generateCommand({ check, configPath, styles }: GenerateOptions): Promise<number>;
|
|
37
38
|
export {};
|
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { reportError } from '@williamthorsen/nmr-core';
|
|
4
4
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
5
5
|
import { stringify } from 'yaml';
|
|
6
6
|
import { CONFIG_FILE_PATH } from "../loadConfig.js";
|
|
7
7
|
import { loadValidatedConfig, reportConfigProblem } from "../loadValidatedConfig.js";
|
|
8
|
-
import {
|
|
8
|
+
import { hashPreset } from "./presets.js";
|
|
9
9
|
import { resolveLabels } from "./resolveLabels.js";
|
|
10
10
|
import { checkRetiredSyncLabelsConfig } from "./retiredConfig.js";
|
|
11
11
|
export const LABELS_OUTPUT_PATH = '.github/labels.yaml';
|
|
12
12
|
export function formatLabelsYaml(labels, presetHashes, configFilePath = CONFIG_FILE_PATH) {
|
|
13
13
|
const headerLines = ['# Generated by release-kit sync-labels — do not edit.', `# Source: ${configFilePath}`];
|
|
14
|
-
const sortedPresetHashes = [...presetHashes].
|
|
14
|
+
const sortedPresetHashes = [...presetHashes].toSorted(([a], [b]) => a.localeCompare(b));
|
|
15
15
|
for (const [name, hash] of sortedPresetHashes) {
|
|
16
16
|
headerLines.push(`# ${name} preset hash: ${hash}`);
|
|
17
17
|
}
|
|
18
|
-
const declared = labels.map((
|
|
18
|
+
const declared = labels.map(({ archived, color, description, name }) => ({
|
|
19
|
+
name,
|
|
20
|
+
color,
|
|
21
|
+
description: description ?? '',
|
|
22
|
+
...(archived !== undefined && { archived }),
|
|
23
|
+
}));
|
|
19
24
|
const yamlBody = stringify(declared, { singleQuote: true, lineWidth: 0 });
|
|
20
25
|
return headerLines.join('\n') + '\n' + yamlBody;
|
|
21
26
|
}
|
|
22
27
|
export async function loadRepoLabelsConfig(stderrStyle, configPath) {
|
|
23
28
|
const result = await loadValidatedConfig(configPath);
|
|
24
29
|
if (result.status === 'missing') {
|
|
25
|
-
reportError(`
|
|
30
|
+
reportError(`Config file not found at ${result.configFilePath}. Run \`release-kit sync-labels init\` first.`);
|
|
26
31
|
return undefined;
|
|
27
32
|
}
|
|
28
33
|
if (result.status === 'invalid') {
|
|
@@ -30,7 +35,7 @@ export async function loadRepoLabelsConfig(stderrStyle, configPath) {
|
|
|
30
35
|
return undefined;
|
|
31
36
|
}
|
|
32
37
|
if (result.config.repoLabels === undefined) {
|
|
33
|
-
reportError(`
|
|
38
|
+
reportError(`The \`repoLabels\` block is missing from ${result.configFilePath}. Run \`release-kit sync-labels init\` to seed one.`);
|
|
34
39
|
return undefined;
|
|
35
40
|
}
|
|
36
41
|
return result.config.repoLabels;
|
|
@@ -55,14 +60,14 @@ export async function generateCommand({ check = false, configPath, styles }) {
|
|
|
55
60
|
const presetHashes = new Map();
|
|
56
61
|
const presetNames = repoLabels.extends ?? [];
|
|
57
62
|
for (const presetName of presetNames) {
|
|
58
|
-
presetHashes.set(presetName,
|
|
63
|
+
presetHashes.set(presetName, hashPreset(presetName));
|
|
59
64
|
}
|
|
60
|
-
const content = formatLabelsYaml(labels, presetHashes, configPath);
|
|
65
|
+
const content = formatLabelsYaml(labels, presetHashes, toRootRelativePosixPath(configPath ?? CONFIG_FILE_PATH));
|
|
61
66
|
if (check) {
|
|
62
67
|
return compareAgainstCommittedFile(content);
|
|
63
68
|
}
|
|
64
69
|
try {
|
|
65
|
-
mkdirSync(dirname(LABELS_OUTPUT_PATH), { recursive: true });
|
|
70
|
+
mkdirSync(path.dirname(LABELS_OUTPUT_PATH), { recursive: true });
|
|
66
71
|
writeFileSync(LABELS_OUTPUT_PATH, content, 'utf8');
|
|
67
72
|
}
|
|
68
73
|
catch (error) {
|
|
@@ -89,3 +94,6 @@ function compareAgainstCommittedFile(expected) {
|
|
|
89
94
|
console.info(`${LABELS_OUTPUT_PATH} is up to date.`);
|
|
90
95
|
return 0;
|
|
91
96
|
}
|
|
97
|
+
function toRootRelativePosixPath(filePath) {
|
|
98
|
+
return path.relative(process.cwd(), path.resolve(filePath)).split(path.sep).join('/');
|
|
99
|
+
}
|
|
@@ -8,15 +8,14 @@ interface InitOptions {
|
|
|
8
8
|
styles: StreamStyles;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Runs the `sync-labels init` subcommand and returns the process exit code.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* Returns 0 on success, 1 on failure.
|
|
13
|
+
* Scaffolds the caller workflow and seeds the `repoLabels` block with scope labels for the workspaces and retired
|
|
14
|
+
* packages. The block goes into a new config file when none exists; an existing config is never rewritten, so the
|
|
15
|
+
* command prints the block for manual paste instead.
|
|
17
16
|
*
|
|
18
|
-
* `configPath` names the config to read.
|
|
19
|
-
* scaffolding branch
|
|
17
|
+
* `configPath` names the config to read. Because the command fails when the named config does not exist, it
|
|
18
|
+
* reaches the scaffolding branch only under the default path, and that branch always writes to `CONFIG_FILE_PATH`.
|
|
20
19
|
*/
|
|
21
20
|
export declare function syncLabelsInitCommand({ configPath, dryRun, force, styles }: InitOptions): Promise<number>;
|
|
22
21
|
export {};
|
|
@@ -57,7 +57,7 @@ export async function syncLabelsInitCommand({ configPath, dryRun, force, styles
|
|
|
57
57
|
1. Paste the block above into ${configFilePath}.
|
|
58
58
|
2. Run \`release-kit sync-labels generate\` to produce ${LABELS_OUTPUT_PATH}.
|
|
59
59
|
3. Commit the changes.
|
|
60
|
-
4. Run \`release-kit sync-labels sync\` to apply labels to
|
|
60
|
+
4. Run \`release-kit sync-labels sync\` to apply labels to the GitHub repo.
|
|
61
61
|
`);
|
|
62
62
|
return 0;
|
|
63
63
|
}
|
|
@@ -88,7 +88,7 @@ export async function syncLabelsInitCommand({ configPath, dryRun, force, styles
|
|
|
88
88
|
- ${LABELS_OUTPUT_PATH}
|
|
89
89
|
2. Customize the \`repoLabels\` block in ${CONFIG_FILE_PATH} as needed, then re-run \`release-kit sync-labels generate\`.
|
|
90
90
|
3. Commit the generated files.
|
|
91
|
-
4. Run \`release-kit sync-labels sync\` to apply labels to
|
|
91
|
+
4. Run \`release-kit sync-labels sync\` to apply labels to the GitHub repo.
|
|
92
92
|
`);
|
|
93
93
|
return 0;
|
|
94
94
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { LabelDefinition } from './types.js';
|
|
2
|
-
/** Computes SHA-256 hex digest of a preset
|
|
3
|
-
export declare function
|
|
2
|
+
/** Computes the SHA-256 hex digest of a preset's resolved labels. Throws as `loadPreset` does. */
|
|
3
|
+
export declare function hashPreset(presetName: string): string;
|
|
4
4
|
/**
|
|
5
|
-
* Loads a named preset from the bundled YAML files.
|
|
5
|
+
* Loads a named preset from the bundled YAML files. The `common` preset also defines the label of every work type in
|
|
6
|
+
* change-grammar's taxonomy, ahead of the labels in its file.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* Throws if the preset does not exist or has invalid content.
|
|
8
9
|
*/
|
|
9
10
|
export declare function loadPreset(presetName: string): LabelDefinition[];
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
+
import { CANONICAL_TAXONOMY } from '@williamthorsen/change-grammar';
|
|
4
5
|
import { findPackageRoot } from '@williamthorsen/nmr-core';
|
|
5
6
|
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
6
7
|
import { parse } from 'yaml';
|
|
7
8
|
import { isRecord } from "../typeGuards.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return createHash('sha256').update(content).digest('hex');
|
|
9
|
+
import { deriveTypeLabels } from "./typeLabels.js";
|
|
10
|
+
const TYPE_LABELS_PRESET = 'common';
|
|
11
|
+
export function hashPreset(presetName) {
|
|
12
|
+
return createHash('sha256')
|
|
13
|
+
.update(JSON.stringify(loadPreset(presetName)))
|
|
14
|
+
.digest('hex');
|
|
15
15
|
}
|
|
16
16
|
export function loadPreset(presetName) {
|
|
17
17
|
const presetPath = resolvePresetPath(presetName);
|
|
18
18
|
if (!existsSync(presetPath)) {
|
|
19
|
-
throw new Error(`Unknown preset "${presetName}".
|
|
19
|
+
throw new Error(`Unknown preset "${presetName}". The file ${presetPath} does not exist`);
|
|
20
20
|
}
|
|
21
21
|
let content;
|
|
22
22
|
try {
|
|
@@ -42,7 +42,7 @@ export function loadPreset(presetName) {
|
|
|
42
42
|
}
|
|
43
43
|
labels.push({ name, color, ...(description !== undefined && { description }) });
|
|
44
44
|
}
|
|
45
|
-
return labels;
|
|
45
|
+
return presetName === TYPE_LABELS_PRESET ? [...deriveTypeLabels(CANONICAL_TAXONOMY), ...labels] : labels;
|
|
46
46
|
}
|
|
47
47
|
function resolvePresetPath(presetName) {
|
|
48
48
|
const root = findPackageRoot(import.meta.url);
|
|
@@ -3,13 +3,13 @@ import type { LabelDefinition } from './types.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Resolves the repository's label set from a `repoLabels` config block.
|
|
5
5
|
*
|
|
6
|
-
* Resolution is an ordered fold with last-writer-wins:
|
|
6
|
+
* Resolution is an ordered fold with last-writer-wins: Presets are loaded in `extends` order (a later preset replaces
|
|
7
7
|
* an earlier preset's label of the same name), then the `labels` record is applied: An entry adds a label, replaces
|
|
8
|
-
* one an earlier layer defined, or removes it (`null`). Names match case-insensitively, as GitHub matches them,
|
|
9
|
-
* replacing layer's spelling becomes the label's name. Replacement is wholesale, so an entry omitting
|
|
10
|
-
* drops the one an earlier layer supplied rather than inheriting it.
|
|
11
|
-
* Throws on two misstatements that
|
|
12
|
-
*
|
|
13
|
-
*
|
|
8
|
+
* one that an earlier layer defined, or removes it (`null`). Names match case-insensitively, as GitHub matches them,
|
|
9
|
+
* and the replacing layer's spelling becomes the label's name. Replacement is wholesale, so an entry omitting
|
|
10
|
+
* `description` drops the one that an earlier layer supplied rather than inheriting it.
|
|
11
|
+
* Throws on two misstatements that the output diff would otherwise not show: keys in the `labels` record that differ
|
|
12
|
+
* only in case, all but one of which the fold would discard, and a dangling `null`, a removal naming a label that the
|
|
13
|
+
* `extends` presets did not define.
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveLabels(config: RepoLabelsConfig): LabelDefinition[];
|
|
@@ -17,7 +17,7 @@ export function resolveLabels(config) {
|
|
|
17
17
|
const key = toLabelKey(name);
|
|
18
18
|
if (spec === null) {
|
|
19
19
|
if (!resolved.has(key)) {
|
|
20
|
-
throw new Error(`Label '${name}' is set to null (remove), but
|
|
20
|
+
throw new Error(`Label '${name}' is set to null (remove), but the presets in 'extends' don't define it. Fix the name or delete the entry.`);
|
|
21
21
|
}
|
|
22
22
|
resolved.delete(key);
|
|
23
23
|
}
|
|
@@ -26,6 +26,7 @@ export function resolveLabels(config) {
|
|
|
26
26
|
name,
|
|
27
27
|
color: spec.color,
|
|
28
28
|
...(spec.description !== undefined && { description: spec.description }),
|
|
29
|
+
...(spec.archived !== undefined && { archived: spec.archived }),
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/** Path of the retired standalone sync-labels config file. */
|
|
2
2
|
export declare const RETIRED_SYNC_LABELS_CONFIG_PATH = ".config/sync-labels.config.ts";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Reports an actionable migration error when the retired standalone config file is present.
|
|
5
5
|
*
|
|
6
6
|
* Returns `true` when the file exists, in which case the caller must abort. The check is
|
|
7
|
-
* unconditional on config content:
|
|
8
|
-
* repo cannot silently lose its custom labels. `configPath` names the config
|
|
9
|
-
*
|
|
10
|
-
* no `--config`.
|
|
7
|
+
* unconditional on config content: File presence alone triggers it, so a half-migrated
|
|
8
|
+
* repo cannot silently lose its custom labels. `configPath` names the config into which the
|
|
9
|
+
* message directs the labels.
|
|
11
10
|
*/
|
|
12
11
|
export declare function checkRetiredSyncLabelsConfig(configPath?: string): boolean;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Validates prerequisites (gh CLI, workflow file), then triggers the sync-labels workflow
|
|
5
|
-
* via `gh workflow run`. Returns 0 on success, 1 on failure.
|
|
2
|
+
* Runs the `sync-labels sync` subcommand, which triggers the sync-labels workflow, and returns the process exit code.
|
|
6
3
|
*/
|
|
7
4
|
export declare function syncLabelsCommand(): number;
|
|
@@ -5,8 +5,8 @@ export declare function syncLabelsWorkflow(): string;
|
|
|
5
5
|
* Generates scope labels from workspace paths and retired-package names.
|
|
6
6
|
*
|
|
7
7
|
* Workspace labels are named after the path basename; retired-package labels after the unscoped package name.
|
|
8
|
-
* None
|
|
9
|
-
* the scope labels are meant to
|
|
8
|
+
* None has a description: GitHub renders a described label taller and more prominently than a bare one, and
|
|
9
|
+
* the scope labels are meant to appear as a compact group.
|
|
10
10
|
* Describing only the retired scopes would single them out for that prominence.
|
|
11
11
|
*/
|
|
12
12
|
export declare function buildScopeLabels(workspacePaths: string[], retiredNames?: string[]): LabelDefinition[];
|
|
@@ -15,5 +15,5 @@ export declare function buildScopeLabels(workspacePaths: string[], retiredNames?
|
|
|
15
15
|
* `.config/release-kit.config.ts`.
|
|
16
16
|
*/
|
|
17
17
|
export declare function renderRepoLabelsBlock(scopeLabels: LabelDefinition[]): string;
|
|
18
|
-
/** Generates a new `.config/release-kit.config.ts`
|
|
18
|
+
/** Generates a new `.config/release-kit.config.ts` containing the `repoLabels` block. */
|
|
19
19
|
export declare function repoLabelsConfigScript(scopeLabels: LabelDefinition[]): string;
|
|
@@ -6,7 +6,7 @@ name: Sync labels
|
|
|
6
6
|
on:
|
|
7
7
|
workflow_dispatch:
|
|
8
8
|
|
|
9
|
-
# Apply on merge, so that
|
|
9
|
+
# Apply on merge, so that the repository's labels match each merged version of the labels file.
|
|
10
10
|
push:
|
|
11
11
|
paths:
|
|
12
12
|
- .github/labels.yaml
|
|
@@ -19,8 +19,8 @@ on:
|
|
|
19
19
|
# Permissions are fixed when a run is created and cannot vary by trigger within a job,
|
|
20
20
|
# so applying and previewing are separate jobs.
|
|
21
21
|
jobs:
|
|
22
|
-
# The push arm applies on the default branch alone.
|
|
23
|
-
#
|
|
22
|
+
# The push arm applies on the default branch alone. The job's \`if:\` names that branch through the event payload,
|
|
23
|
+
# which keeps this file identical across repos; a \`branches:\` filter would have to name it literally.
|
|
24
24
|
sync:
|
|
25
25
|
if: github.event_name != 'pull_request' && (github.event_name != 'push' || github.ref_name == github.event.repository.default_branch)
|
|
26
26
|
permissions:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CanonicalWorkTypeEntry } from '@williamthorsen/change-grammar';
|
|
2
|
+
import type { LabelDefinition } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Builds one label per work type, named by the type's tracker label. A type without a presentation row gets its
|
|
5
|
+
* tier's default color and does not get a description.
|
|
6
|
+
*/
|
|
7
|
+
export declare function deriveTypeLabels(taxonomy: {
|
|
8
|
+
types: readonly Pick<CanonicalWorkTypeEntry, 'key' | 'tier' | 'trackerLabel'>[];
|
|
9
|
+
}): LabelDefinition[];
|
|
10
|
+
/** Color and description of each work type's label, keyed by work-type key. Descriptions fit GitHub's 100 characters. */
|
|
11
|
+
export declare const TYPE_LABEL_PRESENTATION: Readonly<Record<string, {
|
|
12
|
+
color: string;
|
|
13
|
+
description: string;
|
|
14
|
+
}>>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const FALLBACK_COLOR = 'ededed';
|
|
2
|
+
const TIER_DEFAULT_COLORS = {
|
|
3
|
+
internal: '1d76db',
|
|
4
|
+
process: 'edc287',
|
|
5
|
+
public: '0075ca',
|
|
6
|
+
};
|
|
7
|
+
export function deriveTypeLabels(taxonomy) {
|
|
8
|
+
return taxonomy.types.map(({ key, tier, trackerLabel }) => {
|
|
9
|
+
const presentation = TYPE_LABEL_PRESENTATION[key];
|
|
10
|
+
if (presentation === undefined) {
|
|
11
|
+
return { name: trackerLabel, color: TIER_DEFAULT_COLORS[tier] ?? FALLBACK_COLOR };
|
|
12
|
+
}
|
|
13
|
+
return { name: trackerLabel, ...presentation };
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export const TYPE_LABEL_PRESENTATION = {
|
|
17
|
+
ai: {
|
|
18
|
+
color: 'a2eeef',
|
|
19
|
+
description: 'Guidance on working in this repo, such as its AGENTS.md or a repo-local skill, or its agent config',
|
|
20
|
+
},
|
|
21
|
+
ci: { color: 'edc287', description: 'CI/CD tooling & configuration' },
|
|
22
|
+
deprecate: { color: 'fbca04', description: 'Marks functionality for future removal' },
|
|
23
|
+
deps: { color: 'edc287', description: 'Change to dependencies' },
|
|
24
|
+
docs: {
|
|
25
|
+
color: 'a2eeef',
|
|
26
|
+
description: 'Documentation for human readers, such as a README, a guide, or a code comment',
|
|
27
|
+
},
|
|
28
|
+
drop: { color: 'd93f0b', description: 'Removes functionality that consumers depend on' },
|
|
29
|
+
feat: { color: '0075ca', description: 'Added or improved external functionality' },
|
|
30
|
+
fix: { color: 'd73a4a', description: 'Fixes a bug' },
|
|
31
|
+
fmt: { color: 'edc287', description: 'Formatting only, with no change to content' },
|
|
32
|
+
internal: { color: '1d76db', description: 'Internal change without external impact' },
|
|
33
|
+
perf: { color: '0075ca', description: 'Improves performance without changing behavior' },
|
|
34
|
+
refactor: { color: 'edc287', description: 'Improvement to code without change in functionality' },
|
|
35
|
+
sec: { color: 'b60205', description: 'Security vulnerability or hardening' },
|
|
36
|
+
tests: { color: 'edc287', description: 'Tests' },
|
|
37
|
+
tooling: { color: 'edc287', description: 'Development tools' },
|
|
38
|
+
};
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
/** A single GitHub label definition
|
|
1
|
+
/** A single GitHub label definition, as `sync-labels generate` writes it to `.github/labels.yaml`. */
|
|
2
2
|
export interface LabelDefinition {
|
|
3
3
|
/** Display name of the label. */
|
|
4
4
|
name: string;
|
|
5
5
|
/** Hex color code without the leading `#`. */
|
|
6
6
|
color: string;
|
|
7
|
-
/** Short description shown in the GitHub UI. Absent when the label
|
|
7
|
+
/** Short description shown in the GitHub UI. Absent when the label has none. */
|
|
8
8
|
description?: string;
|
|
9
|
+
/** Marks the label for the sync to archive rather than keep active. Absent for an active label. */
|
|
10
|
+
archived?: true;
|
|
9
11
|
}
|
package/dist/esm/tagCommand.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
-
/**
|
|
2
|
+
/** Runs the CLI `tag` command: parses flags and delegates to `createTags`. */
|
|
3
3
|
export declare function tagCommand(argv: string[], styles: StreamStyles): void;
|
package/dist/esm/typeGuards.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Checks whether a value is a plain object (non-null, non-array). */
|
|
2
2
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
3
|
-
/**
|
|
3
|
+
/** Narrows an `unknown` value to `unknown[]` via `Array.isArray`, avoiding `any[]`. */
|
|
4
4
|
export declare function isUnknownArray(value: unknown): value is unknown[];
|