@williamthorsen/release-kit 6.0.0 → 8.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/CHANGELOG.md +18 -0
- package/README.md +5 -3
- package/dist/esm/assertCleanWorkingTree.js +9 -14
- package/dist/esm/bin/release-kit.js +200 -199
- package/dist/esm/buildChangelogEntries.d.ts +2 -2
- package/dist/esm/buildChangelogEntries.js +142 -149
- package/dist/esm/buildDependencyGraph.d.ts +1 -1
- package/dist/esm/buildDependencyGraph.js +60 -59
- package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -1
- package/dist/esm/buildEmptyReleaseEntry.js +12 -15
- package/dist/esm/buildReleaseSummary.d.ts +1 -1
- package/dist/esm/buildReleaseSummary.js +23 -26
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +1 -1
- package/dist/esm/buildSyntheticChangelogEntry.js +9 -12
- package/dist/esm/bumpAllVersions.d.ts +1 -1
- package/dist/esm/bumpAllVersions.js +51 -52
- package/dist/esm/bumpVersion.d.ts +1 -1
- package/dist/esm/bumpVersion.js +25 -28
- package/dist/esm/changelogJsonFile.d.ts +1 -1
- package/dist/esm/changelogJsonFile.js +59 -62
- package/dist/esm/changelogJsonUtils.d.ts +1 -1
- package/dist/esm/changelogJsonUtils.js +35 -32
- package/dist/esm/changelogOverrides.d.ts +1 -1
- package/dist/esm/changelogOverrides.js +374 -377
- package/dist/esm/checkWorkTypesDrift.js +104 -102
- package/dist/esm/collectPolicyViolations.d.ts +2 -2
- package/dist/esm/collectPolicyViolations.js +11 -14
- package/dist/esm/commitCommand.js +48 -46
- package/dist/esm/compareVersions.js +20 -23
- package/dist/esm/createGithubRelease.js +56 -59
- package/dist/esm/createGithubReleaseCommand.js +21 -23
- package/dist/esm/createTags.js +57 -56
- package/dist/esm/decideRelease.d.ts +2 -2
- package/dist/esm/decideRelease.js +27 -41
- package/dist/esm/defaults.d.ts +3 -3
- package/dist/esm/defaults.js +36 -48
- package/dist/esm/deleteFileIfExists.js +10 -12
- package/dist/esm/deriveWorkspaceConfig.d.ts +1 -1
- package/dist/esm/deriveWorkspaceConfig.js +34 -36
- package/dist/esm/detectPackageManager.js +35 -37
- package/dist/esm/detectUndeclaredTagPrefixes.js +41 -40
- package/dist/esm/determineBumpFromCommits.d.ts +2 -2
- package/dist/esm/determineBumpFromCommits.js +25 -29
- package/dist/esm/determineBumpType.d.ts +1 -1
- package/dist/esm/determineBumpType.js +31 -32
- package/dist/esm/discoverWorkspaces.js +38 -40
- package/dist/esm/format.js +6 -11
- package/dist/esm/generateChangelogs.d.ts +1 -1
- package/dist/esm/generateChangelogs.js +13 -17
- package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
- package/dist/esm/getCommitsSinceTarget.js +54 -55
- package/dist/esm/hasPrettierConfig.js +33 -35
- package/dist/esm/help/prepareHelp.js +4 -8
- package/dist/esm/index.d.ts +4 -4
- package/dist/esm/index.js +2 -8
- package/dist/esm/init/checks.js +28 -32
- package/dist/esm/init/detectRepoType.js +12 -15
- package/dist/esm/init/initCommand.js +60 -54
- package/dist/esm/init/parseJsonRecord.js +9 -11
- package/dist/esm/init/scaffold.d.ts +1 -1
- package/dist/esm/init/scaffold.js +32 -38
- package/dist/esm/init/templates.d.ts +1 -1
- package/dist/esm/init/templates.js +16 -22
- package/dist/esm/injectReleaseNotesIntoReadme.js +42 -49
- package/dist/esm/injectSection.js +17 -27
- package/dist/esm/loadConfig.d.ts +1 -1
- package/dist/esm/loadConfig.js +206 -225
- package/dist/esm/parseCommitMessage.d.ts +1 -1
- package/dist/esm/parseCommitMessage.js +72 -76
- package/dist/esm/parseRequestedTags.js +6 -9
- package/dist/esm/prepareCommand.d.ts +1 -1
- package/dist/esm/prepareCommand.js +225 -235
- package/dist/esm/previewTagPrefixes.d.ts +1 -1
- package/dist/esm/previewTagPrefixes.js +101 -96
- package/dist/esm/propagateBumps.d.ts +2 -2
- package/dist/esm/propagateBumps.js +48 -50
- package/dist/esm/publish.d.ts +2 -2
- package/dist/esm/publish.js +22 -28
- package/dist/esm/publishCommand.js +75 -80
- package/dist/esm/pushCommand.js +33 -34
- package/dist/esm/pushRelease.d.ts +1 -1
- package/dist/esm/pushRelease.js +21 -24
- package/dist/esm/readCurrentVersion.js +13 -17
- package/dist/esm/releasePrepare.d.ts +1 -1
- package/dist/esm/releasePrepare.js +213 -231
- package/dist/esm/releasePrepareMono.d.ts +2 -2
- package/dist/esm/releasePrepareMono.js +434 -494
- package/dist/esm/releasePrepareProject.d.ts +2 -2
- package/dist/esm/releasePrepareProject.js +143 -153
- package/dist/esm/renderChangelogMarkdown.d.ts +1 -1
- package/dist/esm/renderChangelogMarkdown.js +20 -24
- package/dist/esm/renderReleaseNotes.d.ts +1 -1
- package/dist/esm/renderReleaseNotes.js +50 -55
- package/dist/esm/reportPrepare.d.ts +1 -1
- package/dist/esm/reportPrepare.js +230 -231
- package/dist/esm/resolveCliffConfigPath.js +18 -23
- package/dist/esm/resolveCommandTags.d.ts +1 -1
- package/dist/esm/resolveCommandTags.js +47 -45
- package/dist/esm/resolveReleaseNotesConfig.d.ts +1 -1
- package/dist/esm/resolveReleaseNotesConfig.js +39 -43
- package/dist/esm/resolveReleaseTags.d.ts +1 -1
- package/dist/esm/resolveReleaseTags.js +39 -40
- package/dist/esm/runGitCliff.js +27 -30
- package/dist/esm/showTagPrefixesCommand.js +48 -70
- package/dist/esm/stripEmojiPrefix.js +2 -5
- package/dist/esm/stripScope.js +18 -21
- package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
- package/dist/esm/sync-labels/generateCommand.js +51 -53
- package/dist/esm/sync-labels/initCommand.js +46 -48
- package/dist/esm/sync-labels/loadSyncLabelsConfig.d.ts +1 -1
- package/dist/esm/sync-labels/loadSyncLabelsConfig.js +46 -52
- package/dist/esm/sync-labels/presets.d.ts +1 -1
- package/dist/esm/sync-labels/presets.js +40 -43
- package/dist/esm/sync-labels/resolveLabels.d.ts +1 -1
- package/dist/esm/sync-labels/resolveLabels.js +28 -35
- package/dist/esm/sync-labels/syncCommand.js +30 -31
- package/dist/esm/sync-labels/templates.d.ts +1 -1
- package/dist/esm/sync-labels/templates.js +23 -26
- package/dist/esm/sync-labels/types.js +1 -0
- package/dist/esm/syncWorkTypes.js +83 -84
- package/dist/esm/tagCommand.js +12 -14
- package/dist/esm/typeGuards.js +4 -8
- package/dist/esm/types.js +66 -60
- package/dist/esm/validateConfig.d.ts +1 -1
- package/dist/esm/validateConfig.js +109 -115
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +2 -2
- package/dist/esm/validateOnlyExcludesStrandedDependents.js +97 -91
- package/dist/esm/validateOverridesCommand.d.ts +2 -2
- package/dist/esm/validateOverridesCommand.js +110 -111
- package/dist/esm/workTypesData.js +57 -60
- package/dist/esm/workTypesUtils.js +16 -21
- package/dist/esm/writeReleaseNotesPreviews.js +55 -57
- package/package.json +3 -2
- package/dist/esm/.cache +0 -1
|
@@ -1,58 +1,56 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from
|
|
2
|
-
import { dirname } from
|
|
3
|
-
import { reportError } from
|
|
4
|
-
import { stringify } from
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
4
|
+
import { stringify } from 'yaml';
|
|
5
5
|
import { loadSyncLabelsConfig, SYNC_LABELS_CONFIG_PATH } from "./loadSyncLabelsConfig.js";
|
|
6
6
|
import { hashPresetFile } from "./presets.js";
|
|
7
7
|
import { resolveLabels } from "./resolveLabels.js";
|
|
8
|
-
const LABELS_OUTPUT_PATH =
|
|
9
|
-
function formatLabelsYaml(labels, presetHashes) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
export const LABELS_OUTPUT_PATH = '.github/labels.yaml';
|
|
9
|
+
export function formatLabelsYaml(labels, presetHashes) {
|
|
10
|
+
const headerLines = ['# Generated by release-kit sync-labels — do not edit.', `# Source: ${SYNC_LABELS_CONFIG_PATH}`];
|
|
11
|
+
for (const [name, hash] of [...presetHashes.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
12
|
+
headerLines.push(`# ${name} preset hash: ${hash}`);
|
|
13
|
+
}
|
|
14
|
+
const yamlBody = stringify(labels, { singleQuote: true, lineWidth: 0 });
|
|
15
|
+
return headerLines.join('\n') + '\n' + yamlBody;
|
|
16
16
|
}
|
|
17
|
-
async function generateCommand() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
presetHashes
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
17
|
+
export async function generateCommand() {
|
|
18
|
+
let config;
|
|
19
|
+
try {
|
|
20
|
+
config = await loadSyncLabelsConfig();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24
|
+
reportError(`Failed to load config: ${message}`);
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
if (config === undefined) {
|
|
28
|
+
reportError(`No config file found at ${SYNC_LABELS_CONFIG_PATH}. Run \`release-kit sync-labels init\` first.`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
let labels;
|
|
32
|
+
try {
|
|
33
|
+
labels = resolveLabels(config);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
37
|
+
reportError(`Failed to resolve labels: ${message}`);
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
const presetHashes = new Map();
|
|
41
|
+
for (const presetName of config.presets ?? []) {
|
|
42
|
+
presetHashes.set(presetName, hashPresetFile(presetName));
|
|
43
|
+
}
|
|
44
|
+
const content = formatLabelsYaml(labels, presetHashes);
|
|
45
|
+
try {
|
|
46
|
+
mkdirSync(dirname(LABELS_OUTPUT_PATH), { recursive: true });
|
|
47
|
+
writeFileSync(LABELS_OUTPUT_PATH, content, 'utf8');
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
reportError(`Failed to write ${LABELS_OUTPUT_PATH}: ${message}`);
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
console.info(`Generated ${LABELS_OUTPUT_PATH} with ${String(labels.length)} labels.`);
|
|
55
|
+
return 0;
|
|
53
56
|
}
|
|
54
|
-
export {
|
|
55
|
-
LABELS_OUTPUT_PATH,
|
|
56
|
-
formatLabelsYaml,
|
|
57
|
-
generateCommand
|
|
58
|
-
};
|
|
@@ -1,52 +1,53 @@
|
|
|
1
|
-
import { reportWriteResult, writeFileWithCheck } from
|
|
1
|
+
import { reportWriteResult, writeFileWithCheck } from '@williamthorsen/nmr-core';
|
|
2
2
|
import { discoverWorkspaces } from "../discoverWorkspaces.js";
|
|
3
3
|
import { generateCommand, LABELS_OUTPUT_PATH } from "./generateCommand.js";
|
|
4
4
|
import { SYNC_LABELS_CONFIG_PATH } from "./loadSyncLabelsConfig.js";
|
|
5
5
|
import { buildScopeLabels, syncLabelsConfigScript, syncLabelsWorkflow } from "./templates.js";
|
|
6
|
-
const WORKFLOW_PATH =
|
|
7
|
-
async function syncLabelsInitCommand({ dryRun, force }) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
6
|
+
const WORKFLOW_PATH = '.github/workflows/sync-labels.yaml';
|
|
7
|
+
export async function syncLabelsInitCommand({ dryRun, force }) {
|
|
8
|
+
if (dryRun) {
|
|
9
|
+
console.info('[dry-run mode]');
|
|
10
|
+
}
|
|
11
|
+
console.info('\n> Discovering workspaces');
|
|
12
|
+
let workspacePaths;
|
|
13
|
+
try {
|
|
14
|
+
workspacePaths = await discoverWorkspaces();
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18
|
+
process.stderr.write(` Failed to discover workspaces: ${message}\n`);
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
const scopeLabels = workspacePaths === undefined ? [] : buildScopeLabels(workspacePaths);
|
|
22
|
+
if (workspacePaths === undefined) {
|
|
23
|
+
console.info(' No pnpm workspaces found (single-package repo)');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
console.info(` Found ${String(workspacePaths.length)} workspaces`);
|
|
27
|
+
}
|
|
28
|
+
console.info('\n> Scaffolding files');
|
|
29
|
+
const workflowResult = writeFileWithCheck(WORKFLOW_PATH, syncLabelsWorkflow(), { dryRun, overwrite: force });
|
|
30
|
+
const configResult = writeFileWithCheck(SYNC_LABELS_CONFIG_PATH, syncLabelsConfigScript(scopeLabels), {
|
|
31
|
+
dryRun,
|
|
32
|
+
overwrite: force,
|
|
33
|
+
});
|
|
34
|
+
reportWriteResult(workflowResult, dryRun);
|
|
35
|
+
reportWriteResult(configResult, dryRun);
|
|
36
|
+
if (workflowResult.outcome === 'failed' || configResult.outcome === 'failed') {
|
|
37
|
+
process.stderr.write('Failed to scaffold one or more files.\n');
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
if (dryRun) {
|
|
41
|
+
console.info(`\n> [dry-run] Would generate ${LABELS_OUTPUT_PATH}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.info('\n> Generating labels');
|
|
45
|
+
const generateExitCode = await generateCommand();
|
|
46
|
+
if (generateExitCode !== 0) {
|
|
47
|
+
return generateExitCode;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
40
50
|
console.info(`
|
|
41
|
-
> [dry-run] Would generate ${LABELS_OUTPUT_PATH}`);
|
|
42
|
-
} else {
|
|
43
|
-
console.info("\n> Generating labels");
|
|
44
|
-
const generateExitCode = await generateCommand();
|
|
45
|
-
if (generateExitCode !== 0) {
|
|
46
|
-
return generateExitCode;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
console.info(`
|
|
50
51
|
> Next steps
|
|
51
52
|
1. Review the generated files:
|
|
52
53
|
- ${WORKFLOW_PATH}
|
|
@@ -56,8 +57,5 @@ async function syncLabelsInitCommand({ dryRun, force }) {
|
|
|
56
57
|
3. Commit the generated files.
|
|
57
58
|
4. Run \`release-kit sync-labels sync\` to apply labels to your GitHub repo.
|
|
58
59
|
`);
|
|
59
|
-
|
|
60
|
+
return 0;
|
|
60
61
|
}
|
|
61
|
-
export {
|
|
62
|
-
syncLabelsInitCommand
|
|
63
|
-
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { SyncLabelsConfig } from './types.
|
|
1
|
+
import type { SyncLabelsConfig } from './types.js';
|
|
2
2
|
export declare const SYNC_LABELS_CONFIG_PATH = ".config/sync-labels.config.ts";
|
|
3
3
|
export declare function loadSyncLabelsConfig(): Promise<SyncLabelsConfig | undefined>;
|
|
@@ -1,59 +1,53 @@
|
|
|
1
|
-
import { existsSync } from
|
|
2
|
-
import path from
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { isRecord } from "../typeGuards.js";
|
|
4
|
-
const SYNC_LABELS_CONFIG_PATH =
|
|
5
|
-
async function loadSyncLabelsConfig() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
return validateSyncLabelsConfig(resolved);
|
|
4
|
+
export const SYNC_LABELS_CONFIG_PATH = '.config/sync-labels.config.ts';
|
|
5
|
+
export async function loadSyncLabelsConfig() {
|
|
6
|
+
const absoluteConfigPath = path.resolve(process.cwd(), SYNC_LABELS_CONFIG_PATH);
|
|
7
|
+
if (!existsSync(absoluteConfigPath)) {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
const { createJiti } = await import('jiti');
|
|
11
|
+
const jiti = createJiti(import.meta.url);
|
|
12
|
+
const imported = await jiti.import(absoluteConfigPath);
|
|
13
|
+
if (!isRecord(imported)) {
|
|
14
|
+
throw new Error(`Config file must export an object, got ${Array.isArray(imported) ? 'array' : typeof imported}`);
|
|
15
|
+
}
|
|
16
|
+
const resolved = imported.default ?? imported.config;
|
|
17
|
+
if (resolved === undefined) {
|
|
18
|
+
throw new Error('Config file must have a default export or a named `config` export (e.g., `export default { ... }` or `export const config = { ... }`)');
|
|
19
|
+
}
|
|
20
|
+
return validateSyncLabelsConfig(resolved);
|
|
23
21
|
}
|
|
24
22
|
function validateSyncLabelsConfig(value) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
let presets;
|
|
29
|
-
if (value.presets !== void 0) {
|
|
30
|
-
if (!Array.isArray(value.presets) || !value.presets.every((p) => typeof p === "string")) {
|
|
31
|
-
throw new Error("Config `presets` must be an array of strings");
|
|
23
|
+
if (!isRecord(value)) {
|
|
24
|
+
throw new Error(`Config must be an object, got ${typeof value}`);
|
|
32
25
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
26
|
+
let presets;
|
|
27
|
+
if (value.presets !== undefined) {
|
|
28
|
+
if (!Array.isArray(value.presets) || !value.presets.every((p) => typeof p === 'string')) {
|
|
29
|
+
throw new Error('Config `presets` must be an array of strings');
|
|
30
|
+
}
|
|
31
|
+
presets = value.presets;
|
|
39
32
|
}
|
|
40
|
-
labels
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
let labels;
|
|
34
|
+
if (value.labels !== undefined) {
|
|
35
|
+
if (!Array.isArray(value.labels)) {
|
|
36
|
+
throw new TypeError('Config `labels` must be an array');
|
|
37
|
+
}
|
|
38
|
+
labels = [];
|
|
39
|
+
for (const label of value.labels) {
|
|
40
|
+
if (!isRecord(label)) {
|
|
41
|
+
throw new Error('Each label must be an object with `name`, `color`, and `description`');
|
|
42
|
+
}
|
|
43
|
+
if (typeof label.name !== 'string' || typeof label.color !== 'string' || typeof label.description !== 'string') {
|
|
44
|
+
throw new TypeError('Each label must have string `name`, `color`, and `description` fields');
|
|
45
|
+
}
|
|
46
|
+
labels.push({ name: label.name, color: label.color, description: label.description });
|
|
47
|
+
}
|
|
49
48
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
49
|
+
return {
|
|
50
|
+
...(presets !== undefined && { presets }),
|
|
51
|
+
...(labels !== undefined && { labels }),
|
|
52
|
+
};
|
|
55
53
|
}
|
|
56
|
-
export {
|
|
57
|
-
SYNC_LABELS_CONFIG_PATH,
|
|
58
|
-
loadSyncLabelsConfig
|
|
59
|
-
};
|
|
@@ -1,50 +1,47 @@
|
|
|
1
|
-
import { createHash } from
|
|
2
|
-
import { existsSync, readFileSync } from
|
|
3
|
-
import { resolve } from
|
|
4
|
-
import { findPackageRoot } from
|
|
5
|
-
import { parse } from
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { findPackageRoot } from '@williamthorsen/nmr-core';
|
|
5
|
+
import { parse } from 'yaml';
|
|
6
6
|
import { isRecord } from "../typeGuards.js";
|
|
7
7
|
function resolvePresetPath(presetName) {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
const root = findPackageRoot(import.meta.url);
|
|
9
|
+
return resolve(root, 'presets', 'labels', `${presetName}.yaml`);
|
|
10
10
|
}
|
|
11
|
-
function hashPresetFile(presetName) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
export function hashPresetFile(presetName) {
|
|
12
|
+
const presetPath = resolvePresetPath(presetName);
|
|
13
|
+
if (!existsSync(presetPath)) {
|
|
14
|
+
throw new Error(`Unknown preset "${presetName}". No file found at ${presetPath}`);
|
|
15
|
+
}
|
|
16
|
+
const content = readFileSync(presetPath, 'utf8');
|
|
17
|
+
return createHash('sha256').update(content).digest('hex');
|
|
18
18
|
}
|
|
19
|
-
function loadPreset(presetName) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const labels = [];
|
|
36
|
-
for (const entry of parsed) {
|
|
37
|
-
if (!isRecord(entry)) {
|
|
38
|
-
throw new Error(`Preset "${presetName}" contains an invalid label entry: ${JSON.stringify(entry)}`);
|
|
19
|
+
export function loadPreset(presetName) {
|
|
20
|
+
const presetPath = resolvePresetPath(presetName);
|
|
21
|
+
if (!existsSync(presetPath)) {
|
|
22
|
+
throw new Error(`Unknown preset "${presetName}". No file found at ${presetPath}`);
|
|
23
|
+
}
|
|
24
|
+
let content;
|
|
25
|
+
try {
|
|
26
|
+
content = readFileSync(presetPath, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30
|
+
throw new Error(`Failed to read preset "${presetName}": ${message}`);
|
|
31
|
+
}
|
|
32
|
+
const parsed = parse(content);
|
|
33
|
+
if (!Array.isArray(parsed)) {
|
|
34
|
+
throw new TypeError(`Preset "${presetName}" must be a YAML array of label definitions`);
|
|
39
35
|
}
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
const labels = [];
|
|
37
|
+
for (const entry of parsed) {
|
|
38
|
+
if (!isRecord(entry)) {
|
|
39
|
+
throw new Error(`Preset "${presetName}" contains an invalid label entry: ${JSON.stringify(entry)}`);
|
|
40
|
+
}
|
|
41
|
+
if (typeof entry.name !== 'string' || typeof entry.color !== 'string' || typeof entry.description !== 'string') {
|
|
42
|
+
throw new TypeError(`Preset "${presetName}" contains a label with invalid fields: ${JSON.stringify(entry)}`);
|
|
43
|
+
}
|
|
44
|
+
labels.push({ name: entry.name, color: entry.color, description: entry.description });
|
|
42
45
|
}
|
|
43
|
-
labels
|
|
44
|
-
}
|
|
45
|
-
return labels;
|
|
46
|
+
return labels;
|
|
46
47
|
}
|
|
47
|
-
export {
|
|
48
|
-
hashPresetFile,
|
|
49
|
-
loadPreset
|
|
50
|
-
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { LabelDefinition, SyncLabelsConfig } from './types.
|
|
1
|
+
import type { LabelDefinition, SyncLabelsConfig } from './types.js';
|
|
2
2
|
export declare function resolveLabels(config: SyncLabelsConfig): LabelDefinition[];
|
|
@@ -1,43 +1,36 @@
|
|
|
1
1
|
import { loadPreset } from "./presets.js";
|
|
2
|
-
function resolveLabels(config) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
export function resolveLabels(config) {
|
|
3
|
+
const presetLabels = [];
|
|
4
|
+
for (const presetName of config.presets ?? []) {
|
|
5
|
+
const loaded = loadPreset(presetName);
|
|
6
|
+
presetLabels.push(...loaded);
|
|
7
|
+
}
|
|
8
|
+
const customLabels = config.labels ?? [];
|
|
9
|
+
detectCollisions(presetLabels, customLabels);
|
|
10
|
+
return sortLabels([...presetLabels, ...customLabels]);
|
|
11
11
|
}
|
|
12
12
|
function detectCollisions(presetLabels, customLabels) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const seenPresetNames = new Set();
|
|
14
|
+
const withinPresetDuplicates = [];
|
|
15
|
+
for (const label of presetLabels) {
|
|
16
|
+
if (seenPresetNames.has(label.name)) {
|
|
17
|
+
withinPresetDuplicates.push(label.name);
|
|
18
|
+
}
|
|
19
|
+
seenPresetNames.add(label.name);
|
|
20
|
+
}
|
|
21
|
+
if (withinPresetDuplicates.length > 0) {
|
|
22
|
+
throw new Error(`Label name collision within presets: the following labels are defined by multiple presets: ${withinPresetDuplicates.join(', ')}. Remove the duplicates or use different presets.`);
|
|
23
|
+
}
|
|
24
|
+
const conflicts = [];
|
|
25
|
+
for (const label of customLabels) {
|
|
26
|
+
if (seenPresetNames.has(label.name)) {
|
|
27
|
+
conflicts.push(label.name);
|
|
28
|
+
}
|
|
18
29
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (withinPresetDuplicates.length > 0) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`Label name collision within presets: the following labels are defined by multiple presets: ${withinPresetDuplicates.join(", ")}. Remove the duplicates or use different presets.`
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
const conflicts = [];
|
|
27
|
-
for (const label of customLabels) {
|
|
28
|
-
if (seenPresetNames.has(label.name)) {
|
|
29
|
-
conflicts.push(label.name);
|
|
30
|
+
if (conflicts.length > 0) {
|
|
31
|
+
throw new Error(`Label name collision: the following labels appear in both presets and custom labels: ${conflicts.join(', ')}. Remove duplicates from your custom labels or use a different name.`);
|
|
30
32
|
}
|
|
31
|
-
}
|
|
32
|
-
if (conflicts.length > 0) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`Label name collision: the following labels appear in both presets and custom labels: ${conflicts.join(", ")}. Remove duplicates from your custom labels or use a different name.`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
33
|
}
|
|
38
34
|
function sortLabels(labels) {
|
|
39
|
-
|
|
35
|
+
return [...labels].sort((a, b) => a.name.localeCompare(b.name));
|
|
40
36
|
}
|
|
41
|
-
export {
|
|
42
|
-
resolveLabels
|
|
43
|
-
};
|
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
import { execSync } from
|
|
2
|
-
import { existsSync } from
|
|
3
|
-
import { reportError } from
|
|
4
|
-
const WORKFLOW_FILE =
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { reportError } from '@williamthorsen/nmr-core';
|
|
4
|
+
const WORKFLOW_FILE = '.github/workflows/sync-labels.yaml';
|
|
5
5
|
function checkGhAvailable() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
try {
|
|
7
|
+
execSync('gh --version', { stdio: 'pipe' });
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
12
13
|
}
|
|
13
|
-
function syncLabelsCommand() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
14
|
+
export function syncLabelsCommand() {
|
|
15
|
+
if (!checkGhAvailable()) {
|
|
16
|
+
reportError('The `gh` CLI is not installed or not in PATH. Install it from https://cli.github.com/');
|
|
17
|
+
return 1;
|
|
18
|
+
}
|
|
19
|
+
if (!existsSync(WORKFLOW_FILE)) {
|
|
20
|
+
reportError(`${WORKFLOW_FILE} not found. Run \`release-kit sync-labels init\` first.`);
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
execSync('gh workflow run sync-labels.yaml', { stdio: 'inherit' });
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
28
|
+
reportError(`Failed to trigger workflow: ${message}`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
console.info('Workflow triggered successfully. View runs at: gh run list --workflow=sync-labels.yaml');
|
|
32
|
+
return 0;
|
|
31
33
|
}
|
|
32
|
-
export {
|
|
33
|
-
syncLabelsCommand
|
|
34
|
-
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { LabelDefinition } from './types.
|
|
1
|
+
import type { LabelDefinition } from './types.js';
|
|
2
2
|
export declare function syncLabelsWorkflow(): string;
|
|
3
3
|
export declare function buildScopeLabels(workspacePaths: string[]): LabelDefinition[];
|
|
4
4
|
export declare function syncLabelsConfigScript(scopeLabels: LabelDefinition[]): string;
|