@williamthorsen/release-kit 4.8.0 → 5.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +134 -4
  2. package/README.md +404 -40
  3. package/cliff.toml.template +2 -1
  4. package/dist/esm/.cache +1 -1
  5. package/dist/esm/assertCleanWorkingTree.js +1 -1
  6. package/dist/esm/bin/release-kit.js +45 -14
  7. package/dist/esm/buildChangelogEntries.d.ts +3 -0
  8. package/dist/esm/{generateChangelogJson.js → buildChangelogEntries.js} +40 -77
  9. package/dist/esm/buildDependencyGraph.d.ts +4 -3
  10. package/dist/esm/buildDependencyGraph.js +18 -11
  11. package/dist/esm/buildReleaseSummary.js +12 -4
  12. package/dist/esm/buildSyntheticChangelogEntry.d.ts +5 -0
  13. package/dist/esm/buildSyntheticChangelogEntry.js +13 -0
  14. package/dist/esm/bumpAllVersions.d.ts +1 -0
  15. package/dist/esm/bumpAllVersions.js +16 -2
  16. package/dist/esm/bumpVersion.js +3 -0
  17. package/dist/esm/changelogJsonFile.d.ts +4 -0
  18. package/dist/esm/changelogJsonFile.js +68 -0
  19. package/dist/esm/commitCommand.js +1 -1
  20. package/dist/esm/compareVersions.d.ts +1 -0
  21. package/dist/esm/compareVersions.js +27 -0
  22. package/dist/esm/createGithubRelease.d.ts +6 -2
  23. package/dist/esm/createGithubRelease.js +17 -17
  24. package/dist/esm/createGithubReleaseCommand.d.ts +1 -0
  25. package/dist/esm/createGithubReleaseCommand.js +41 -0
  26. package/dist/esm/decideRelease.d.ts +25 -0
  27. package/dist/esm/decideRelease.js +28 -0
  28. package/dist/esm/defaults.d.ts +1 -0
  29. package/dist/esm/defaults.js +7 -3
  30. package/dist/esm/deriveWorkspaceConfig.d.ts +2 -0
  31. package/dist/esm/deriveWorkspaceConfig.js +37 -0
  32. package/dist/esm/detectUndeclaredTagPrefixes.d.ts +7 -0
  33. package/dist/esm/detectUndeclaredTagPrefixes.js +46 -0
  34. package/dist/esm/generateChangelogs.d.ts +1 -1
  35. package/dist/esm/generateChangelogs.js +14 -3
  36. package/dist/esm/getCommitsSinceTarget.d.ts +1 -1
  37. package/dist/esm/getCommitsSinceTarget.js +8 -4
  38. package/dist/esm/index.d.ts +2 -39
  39. package/dist/esm/index.js +0 -75
  40. package/dist/esm/init/initCommand.js +1 -1
  41. package/dist/esm/init/scaffold.d.ts +1 -1
  42. package/dist/esm/init/scaffold.js +8 -5
  43. package/dist/esm/init/templates.d.ts +1 -0
  44. package/dist/esm/init/templates.js +35 -5
  45. package/dist/esm/injectReleaseNotesIntoReadme.d.ts +6 -1
  46. package/dist/esm/injectReleaseNotesIntoReadme.js +20 -7
  47. package/dist/esm/loadConfig.d.ts +12 -2
  48. package/dist/esm/loadConfig.js +161 -14
  49. package/dist/esm/parseRequestedTags.d.ts +1 -0
  50. package/dist/esm/parseRequestedTags.js +10 -0
  51. package/dist/esm/prepareCommand.d.ts +3 -1
  52. package/dist/esm/prepareCommand.js +121 -31
  53. package/dist/esm/previewTagPrefixes.d.ts +30 -0
  54. package/dist/esm/previewTagPrefixes.js +120 -0
  55. package/dist/esm/propagateBumps.d.ts +1 -0
  56. package/dist/esm/propagateBumps.js +1 -1
  57. package/dist/esm/publish.d.ts +0 -1
  58. package/dist/esm/publish.js +3 -3
  59. package/dist/esm/publishCommand.js +18 -14
  60. package/dist/esm/pushCommand.js +5 -4
  61. package/dist/esm/readCurrentVersion.d.ts +1 -0
  62. package/dist/esm/readCurrentVersion.js +21 -0
  63. package/dist/esm/releasePrepare.d.ts +2 -0
  64. package/dist/esm/releasePrepare.js +140 -54
  65. package/dist/esm/releasePrepareMono.js +312 -143
  66. package/dist/esm/releasePrepareProject.d.ts +9 -0
  67. package/dist/esm/releasePrepareProject.js +109 -0
  68. package/dist/esm/renderReleaseNotes.d.ts +1 -0
  69. package/dist/esm/renderReleaseNotes.js +29 -2
  70. package/dist/esm/reportPrepare.js +146 -73
  71. package/dist/esm/resolveCliffConfigPath.js +1 -1
  72. package/dist/esm/resolveCommandTags.d.ts +1 -1
  73. package/dist/esm/resolveCommandTags.js +17 -13
  74. package/dist/esm/resolveReleaseNotesConfig.d.ts +8 -1
  75. package/dist/esm/resolveReleaseNotesConfig.js +17 -7
  76. package/dist/esm/resolveReleaseTags.d.ts +2 -1
  77. package/dist/esm/resolveReleaseTags.js +19 -14
  78. package/dist/esm/showTagPrefixesCommand.d.ts +1 -0
  79. package/dist/esm/showTagPrefixesCommand.js +84 -0
  80. package/dist/esm/sync-labels/initCommand.js +1 -1
  81. package/dist/esm/sync-labels/presets.js +1 -1
  82. package/dist/esm/tagCommand.js +1 -1
  83. package/dist/esm/types.d.ts +77 -19
  84. package/dist/esm/validateConfig.js +205 -36
  85. package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -0
  86. package/dist/esm/validateOnlyExcludesStrandedDependents.js +109 -0
  87. package/dist/esm/version.d.ts +1 -1
  88. package/dist/esm/version.js +1 -1
  89. package/dist/esm/writeReleaseNotesPreviews.d.ts +18 -0
  90. package/dist/esm/writeReleaseNotesPreviews.js +65 -0
  91. package/package.json +5 -2
  92. package/presets/labels/common.yaml +9 -6
  93. package/schemas/label-map.json +24 -0
  94. package/dist/esm/component.d.ts +0 -2
  95. package/dist/esm/component.js +0 -14
  96. package/dist/esm/findPackageRoot.d.ts +0 -1
  97. package/dist/esm/findPackageRoot.js +0 -17
  98. package/dist/esm/generateChangelogJson.d.ts +0 -7
  99. package/dist/esm/githubReleaseCommand.d.ts +0 -1
  100. package/dist/esm/githubReleaseCommand.js +0 -35
@@ -0,0 +1 @@
1
+ export declare function createGithubReleaseCommand(argv: string[]): Promise<void>;
@@ -0,0 +1,41 @@
1
+ import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
2
+ import { createGithubReleases } from "./createGithubRelease.js";
3
+ import { parseRequestedTags } from "./parseRequestedTags.js";
4
+ import { resolveCommandTags } from "./resolveCommandTags.js";
5
+ import { resolveReleaseNotesConfig } from "./resolveReleaseNotesConfig.js";
6
+ const createGithubReleaseFlagSchema = {
7
+ dryRun: { long: "--dry-run", type: "boolean" },
8
+ tags: { long: "--tags", type: "string" }
9
+ };
10
+ async function createGithubReleaseCommand(argv) {
11
+ let parsed;
12
+ try {
13
+ parsed = parseArgs(argv, createGithubReleaseFlagSchema);
14
+ } catch (error) {
15
+ console.error(`Error: ${translateParseError(error)}`);
16
+ process.exit(1);
17
+ }
18
+ const { dryRun } = parsed.flags;
19
+ const requestedTags = parseRequestedTags(parsed.flags.tags);
20
+ const resolvedTags = await resolveCommandTags(requestedTags);
21
+ const { changelogJsonOutputPath, sectionOrder } = await resolveReleaseNotesConfig({ strictLoad: true });
22
+ let outcome;
23
+ try {
24
+ outcome = createGithubReleases(resolvedTags, changelogJsonOutputPath, dryRun, sectionOrder);
25
+ } catch (error) {
26
+ console.error(`Error creating GitHub Releases: ${error instanceof Error ? error.message : String(error)}`);
27
+ process.exit(1);
28
+ }
29
+ if (requestedTags !== void 0 && outcome.created.length === 0) {
30
+ console.error(
31
+ `Error: no GitHub Releases were created for requested tags: ${outcome.skipped.join(", ")}. Each was skipped (missing changelog entry, no all-audience content, or empty rendered body).`
32
+ );
33
+ process.exit(1);
34
+ }
35
+ if (outcome.skipped.length > 0) {
36
+ console.info(`Skipped ${outcome.skipped.length} tag(s) with no releasable content: ${outcome.skipped.join(", ")}.`);
37
+ }
38
+ }
39
+ export {
40
+ createGithubReleaseCommand
41
+ };
@@ -0,0 +1,25 @@
1
+ import type { Commit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.ts';
2
+ export interface DecideReleaseArgs {
3
+ commits: readonly Commit[];
4
+ force?: boolean | undefined;
5
+ bumpOverride: ReleaseType | undefined;
6
+ workTypes: Record<string, WorkTypeConfig>;
7
+ versionPatterns: VersionPatterns;
8
+ scopeAliases: Record<string, string> | undefined;
9
+ skipReasons: {
10
+ noCommits: string;
11
+ noBumpWorthy: string;
12
+ };
13
+ }
14
+ export type DecideReleaseResult = {
15
+ outcome: 'release';
16
+ releaseType: ReleaseType;
17
+ parsedCommitCount: number;
18
+ unparseableCommits: Commit[] | undefined;
19
+ } | {
20
+ outcome: 'skip';
21
+ skipReason: string;
22
+ parsedCommitCount: number;
23
+ unparseableCommits: Commit[] | undefined;
24
+ };
25
+ export declare function decideRelease(args: DecideReleaseArgs): DecideReleaseResult;
@@ -0,0 +1,28 @@
1
+ import { determineBumpType } from "./determineBumpType.js";
2
+ import { parseCommitMessage } from "./parseCommitMessage.js";
3
+ function decideRelease(args) {
4
+ const { commits, force = false, bumpOverride, workTypes, versionPatterns, scopeAliases, skipReasons } = args;
5
+ const parsedCommits = [];
6
+ const unparseable = [];
7
+ for (const commit of commits) {
8
+ const parsed = parseCommitMessage(commit.message, commit.hash, workTypes, scopeAliases);
9
+ if (parsed === void 0) {
10
+ unparseable.push(commit);
11
+ } else {
12
+ parsedCommits.push(parsed);
13
+ }
14
+ }
15
+ const parsedCommitCount = parsedCommits.length;
16
+ const unparseableCommits = unparseable.length > 0 ? unparseable : void 0;
17
+ const naturalBump = determineBumpType(parsedCommits, workTypes, versionPatterns);
18
+ const shouldRelease = naturalBump !== void 0 || force;
19
+ if (!shouldRelease) {
20
+ const skipReason = commits.length === 0 ? skipReasons.noCommits : skipReasons.noBumpWorthy;
21
+ return { outcome: "skip", skipReason, parsedCommitCount, unparseableCommits };
22
+ }
23
+ const releaseType = bumpOverride ?? naturalBump ?? "patch";
24
+ return { outcome: "release", releaseType, parsedCommitCount, unparseableCommits };
25
+ }
26
+ export {
27
+ decideRelease
28
+ };
@@ -3,3 +3,4 @@ export declare const DEFAULT_WORK_TYPES: Record<string, WorkTypeConfig>;
3
3
  export declare const DEFAULT_VERSION_PATTERNS: VersionPatterns;
4
4
  export declare const DEFAULT_CHANGELOG_JSON_CONFIG: ChangelogJsonConfig;
5
5
  export declare const DEFAULT_RELEASE_NOTES_CONFIG: ReleaseNotesConfig;
6
+ export declare const DEFAULT_PROJECT_TAG_PREFIX = "v";
@@ -11,6 +11,9 @@ const DEFAULT_WORK_TYPES = {
11
11
  ci: { header: "CI" },
12
12
  deps: { header: "Dependencies", aliases: ["dep"] },
13
13
  docs: { header: "Documentation", aliases: ["doc"] },
14
+ ai: { header: "Agentic support" },
15
+ // `fmt` is retained for bump-determination (`parseCommitMessage`), even though
16
+ // `cliff.toml.template` skips `fmt:` commits so they never enter the changelog.
14
17
  fmt: { header: "Formatting" }
15
18
  };
16
19
  const DEFAULT_VERSION_PATTERNS = {
@@ -20,14 +23,15 @@ const DEFAULT_VERSION_PATTERNS = {
20
23
  const DEFAULT_CHANGELOG_JSON_CONFIG = {
21
24
  enabled: true,
22
25
  outputPath: ".meta/changelog.json",
23
- devOnlySections: ["CI", "Dependencies", "Formatting", "Internal", "Tests", "Tooling"]
26
+ devOnlySections: ["Agentic support", "CI", "Dependencies", "Internal", "Refactoring", "Tests", "Tooling"]
24
27
  };
25
28
  const DEFAULT_RELEASE_NOTES_CONFIG = {
26
- shouldInjectIntoReadme: false,
27
- shouldCreateGithubRelease: false
29
+ shouldInjectIntoReadme: false
28
30
  };
31
+ const DEFAULT_PROJECT_TAG_PREFIX = "v";
29
32
  export {
30
33
  DEFAULT_CHANGELOG_JSON_CONFIG,
34
+ DEFAULT_PROJECT_TAG_PREFIX,
31
35
  DEFAULT_RELEASE_NOTES_CONFIG,
32
36
  DEFAULT_VERSION_PATTERNS,
33
37
  DEFAULT_WORK_TYPES
@@ -0,0 +1,2 @@
1
+ import type { WorkspaceConfig } from './types.ts';
2
+ export declare function deriveWorkspaceConfig(workspacePath: string): WorkspaceConfig;
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { basename } from "node:path";
3
+ import { isRecord } from "./typeGuards.js";
4
+ function deriveWorkspaceConfig(workspacePath) {
5
+ const dir = basename(workspacePath);
6
+ const packageJsonPath = `${workspacePath}/package.json`;
7
+ let parsed;
8
+ try {
9
+ parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
10
+ } catch (error) {
11
+ const message = error instanceof Error ? error.message : String(error);
12
+ throw new Error(`Failed to read ${packageJsonPath}: ${message}`);
13
+ }
14
+ const name = isRecord(parsed) ? parsed.name : void 0;
15
+ if (typeof name !== "string" || name.length === 0) {
16
+ throw new Error(`${packageJsonPath} is missing a 'name' field (required for tag derivation).`);
17
+ }
18
+ const unscopedName = stripNpmScope(name);
19
+ return {
20
+ dir,
21
+ name,
22
+ tagPrefix: `${unscopedName}-v`,
23
+ workspacePath,
24
+ packageFiles: [packageJsonPath],
25
+ changelogPaths: [workspacePath],
26
+ paths: [`${workspacePath}/**`]
27
+ };
28
+ }
29
+ function stripNpmScope(name) {
30
+ if (name.startsWith("@") && name.includes("/")) {
31
+ return name.slice(name.indexOf("/") + 1);
32
+ }
33
+ return name;
34
+ }
35
+ export {
36
+ deriveWorkspaceConfig
37
+ };
@@ -0,0 +1,7 @@
1
+ export interface UndeclaredTagPrefix {
2
+ prefix: string;
3
+ tagCount: number;
4
+ exampleTags: string[];
5
+ suggestedDir: string;
6
+ }
7
+ export declare function detectUndeclaredTagPrefixes(knownPrefixes: readonly string[]): UndeclaredTagPrefix[];
@@ -0,0 +1,46 @@
1
+ import { execFileSync } from "node:child_process";
2
+ const EXAMPLE_TAG_LIMIT = 3;
3
+ const CANDIDATE_TAG_PATTERN = /^(?<prefix>[a-z][a-z0-9-]*-v)\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/;
4
+ function detectUndeclaredTagPrefixes(knownPrefixes) {
5
+ const known = new Set(knownPrefixes);
6
+ let rawOutput;
7
+ try {
8
+ rawOutput = execFileSync("git", ["tag", "--list"], {
9
+ encoding: "utf8",
10
+ stdio: ["pipe", "pipe", "pipe"]
11
+ });
12
+ } catch {
13
+ return [];
14
+ }
15
+ const grouped = /* @__PURE__ */ new Map();
16
+ for (const line of rawOutput.split("\n")) {
17
+ const tag = line.trim();
18
+ if (tag === "") continue;
19
+ const match = CANDIDATE_TAG_PATTERN.exec(tag);
20
+ if (match === null) continue;
21
+ const prefix = match.groups?.prefix ?? "";
22
+ if (prefix === "" || known.has(prefix)) continue;
23
+ let tags = grouped.get(prefix);
24
+ if (tags === void 0) {
25
+ tags = [];
26
+ grouped.set(prefix, tags);
27
+ }
28
+ tags.push(tag);
29
+ }
30
+ const results = [];
31
+ for (const [prefix, tags] of grouped) {
32
+ results.push({
33
+ prefix,
34
+ tagCount: tags.length,
35
+ exampleTags: tags.slice(0, EXAMPLE_TAG_LIMIT),
36
+ suggestedDir: stripTrailingTagMarker(prefix)
37
+ });
38
+ }
39
+ return results.sort((a, b) => a.prefix.localeCompare(b.prefix));
40
+ }
41
+ function stripTrailingTagMarker(prefix) {
42
+ return prefix.endsWith("-v") ? prefix.slice(0, -2) : prefix;
43
+ }
44
+ export {
45
+ detectUndeclaredTagPrefixes
46
+ };
@@ -1,5 +1,5 @@
1
1
  import type { ReleaseConfig } from './types.ts';
2
- export declare function buildTagPattern(tagPrefix: string): string;
2
+ export declare function buildTagPattern(tagPrefixes: readonly string[]): string;
3
3
  export interface GenerateChangelogOptions {
4
4
  tagPattern?: string;
5
5
  includePaths?: string[];
@@ -3,8 +3,19 @@ import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
5
  import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
6
- function buildTagPattern(tagPrefix) {
7
- return `${tagPrefix}[0-9].*`;
6
+ function buildTagPattern(tagPrefixes) {
7
+ if (tagPrefixes.length === 0) {
8
+ throw new Error("buildTagPattern: tagPrefixes must contain at least one entry");
9
+ }
10
+ if (tagPrefixes.length === 1) {
11
+ const single = tagPrefixes[0] ?? "";
12
+ return `${single}[0-9].*`;
13
+ }
14
+ const escaped = tagPrefixes.map(escapeRegex);
15
+ return `(${escaped.join("|")})[0-9].*`;
16
+ }
17
+ function escapeRegex(value) {
18
+ return value.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
8
19
  }
9
20
  function generateChangelog(config, changelogPath, tag, dryRun, options) {
10
21
  const resolvedConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
@@ -41,7 +52,7 @@ function generateChangelog(config, changelogPath, tag, dryRun, options) {
41
52
  }
42
53
  }
43
54
  function generateChangelogs(config, tag, dryRun) {
44
- const tagPattern = buildTagPattern(config.tagPrefix);
55
+ const tagPattern = buildTagPattern([config.tagPrefix]);
45
56
  const results = [];
46
57
  for (const changelogPath of config.changelogPaths) {
47
58
  results.push(...generateChangelog(config, changelogPath, tag, dryRun, { tagPattern }));
@@ -1,5 +1,5 @@
1
1
  import type { Commit } from './types.ts';
2
- export declare function getCommitsSinceTarget(tagPrefix: string, paths?: string[]): {
2
+ export declare function getCommitsSinceTarget(tagPrefixes: readonly string[], paths?: string[]): {
3
3
  tag: string | undefined;
4
4
  commits: Commit[];
5
5
  };
@@ -6,9 +6,13 @@ function isNoTagError(err) {
6
6
  function errorMessage(err) {
7
7
  return err instanceof Error ? err.message : String(err);
8
8
  }
9
- function findLatestTag(tagPrefix) {
9
+ function findLatestTag(tagPrefixes) {
10
+ if (tagPrefixes.length === 0) {
11
+ throw new Error("findLatestTag: tagPrefixes must contain at least one entry");
12
+ }
13
+ const matchArgs = tagPrefixes.map((prefix) => `--match=${prefix}*`);
10
14
  try {
11
- const tagResult = execFileSync("git", ["describe", "--tags", "--abbrev=0", `--match=${tagPrefix}*`], {
15
+ const tagResult = execFileSync("git", ["describe", "--tags", "--abbrev=0", ...matchArgs], {
12
16
  encoding: "utf8",
13
17
  stdio: ["pipe", "pipe", "pipe"]
14
18
  }).trim();
@@ -35,8 +39,8 @@ function parseLogOutput(logOutput) {
35
39
  }
36
40
  return commits;
37
41
  }
38
- function getCommitsSinceTarget(tagPrefix, paths) {
39
- const tag = findLatestTag(tagPrefix);
42
+ function getCommitsSinceTarget(tagPrefixes, paths) {
43
+ const tag = findLatestTag(tagPrefixes);
40
44
  const range = tag === void 0 ? "HEAD" : `${tag}..HEAD`;
41
45
  const format = `%s${FIELD_SEPARATOR}%H`;
42
46
  const args = ["log", range, `--pretty=format:${format}`];
@@ -1,39 +1,2 @@
1
- export type { CreateTagsOptions } from './createTags.ts';
2
- export type { PackageManager } from './detectPackageManager.ts';
3
- export type { GenerateChangelogOptions } from './generateChangelogs.ts';
4
- export type { PublishOptions } from './publish.ts';
5
- export type { ReleasePrepareOptions } from './releasePrepare.ts';
6
- export type { ResolvedTag } from './resolveReleaseTags.ts';
7
- export type { LabelDefinition, SyncLabelsConfig } from './sync-labels/types.ts';
8
- export type { BumpResult, ChangelogAudience, ChangelogEntry, ChangelogItem, ChangelogJsonConfig, ChangelogSection, Commit, ComponentConfig, ComponentOverride, ComponentPrepareResult, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseNotesConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
9
- export { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from './defaults.ts';
10
- export { buildReleaseSummary } from './buildReleaseSummary.ts';
11
- export { bumpAllVersions } from './bumpAllVersions.ts';
12
- export { bumpVersion } from './bumpVersion.ts';
13
- export { commitCommand } from './commitCommand.ts';
14
- export { component } from './component.ts';
15
- export type { CreateGithubReleaseOptions } from './createGithubRelease.ts';
16
- export { createGithubRelease, createGithubReleases } from './createGithubRelease.ts';
17
- export { createTags } from './createTags.ts';
18
- export { deleteFileIfExists } from './deleteFileIfExists.ts';
19
- export { detectPackageManager } from './detectPackageManager.ts';
20
- export { determineBumpType } from './determineBumpType.ts';
21
- export { discoverWorkspaces } from './discoverWorkspaces.ts';
22
- export { generateChangelogJson, generateSyntheticChangelogJson } from './generateChangelogJson.ts';
23
- export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
24
- export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
25
- export { injectReleaseNotesIntoReadme, resolveReadmePath } from './injectReleaseNotesIntoReadme.ts';
26
- export { injectSection } from './injectSection.ts';
27
- export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
28
- export { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
29
- export { publishPackage } from './publish.ts';
30
- export type { PushReleaseOptions } from './pushRelease.ts';
31
- export { pushRelease } from './pushRelease.ts';
32
- export { releasePrepare } from './releasePrepare.ts';
33
- export { releasePrepareMono } from './releasePrepareMono.ts';
34
- export type { RenderOptions } from './renderReleaseNotes.ts';
35
- export { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from './renderReleaseNotes.ts';
36
- export { reportPrepare } from './reportPrepare.ts';
37
- export { resolveCommandTags } from './resolveCommandTags.ts';
38
- export { resolveReleaseTags } from './resolveReleaseTags.ts';
39
- export { stripScope } from './stripScope.ts';
1
+ export type { SyncLabelsConfig } from './sync-labels/types.ts';
2
+ export type { ReleaseKitConfig } from './types.ts';
package/dist/esm/index.js CHANGED
@@ -1,75 +0,0 @@
1
- import {
2
- DEFAULT_CHANGELOG_JSON_CONFIG,
3
- DEFAULT_RELEASE_NOTES_CONFIG,
4
- DEFAULT_VERSION_PATTERNS,
5
- DEFAULT_WORK_TYPES
6
- } from "./defaults.js";
7
- import { buildReleaseSummary } from "./buildReleaseSummary.js";
8
- import { bumpAllVersions } from "./bumpAllVersions.js";
9
- import { bumpVersion } from "./bumpVersion.js";
10
- import { commitCommand } from "./commitCommand.js";
11
- import { component } from "./component.js";
12
- import { createGithubRelease, createGithubReleases } from "./createGithubRelease.js";
13
- import { createTags } from "./createTags.js";
14
- import { deleteFileIfExists } from "./deleteFileIfExists.js";
15
- import { detectPackageManager } from "./detectPackageManager.js";
16
- import { determineBumpType } from "./determineBumpType.js";
17
- import { discoverWorkspaces } from "./discoverWorkspaces.js";
18
- import { generateChangelogJson, generateSyntheticChangelogJson } from "./generateChangelogJson.js";
19
- import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
20
- import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
21
- import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
22
- import { injectSection } from "./injectSection.js";
23
- import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
24
- import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
25
- import { publishPackage } from "./publish.js";
26
- import { pushRelease } from "./pushRelease.js";
27
- import { releasePrepare } from "./releasePrepare.js";
28
- import { releasePrepareMono } from "./releasePrepareMono.js";
29
- import { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
30
- import { reportPrepare } from "./reportPrepare.js";
31
- import { resolveCommandTags } from "./resolveCommandTags.js";
32
- import { resolveReleaseTags } from "./resolveReleaseTags.js";
33
- import { stripScope } from "./stripScope.js";
34
- export {
35
- COMMIT_PREPROCESSOR_PATTERNS,
36
- DEFAULT_CHANGELOG_JSON_CONFIG,
37
- DEFAULT_RELEASE_NOTES_CONFIG,
38
- DEFAULT_VERSION_PATTERNS,
39
- DEFAULT_WORK_TYPES,
40
- RELEASE_SUMMARY_FILE,
41
- RELEASE_TAGS_FILE,
42
- buildReleaseSummary,
43
- bumpAllVersions,
44
- bumpVersion,
45
- commitCommand,
46
- component,
47
- createGithubRelease,
48
- createGithubReleases,
49
- createTags,
50
- deleteFileIfExists,
51
- detectPackageManager,
52
- determineBumpType,
53
- discoverWorkspaces,
54
- generateChangelog,
55
- generateChangelogJson,
56
- generateChangelogs,
57
- generateSyntheticChangelogJson,
58
- getCommitsSinceTarget,
59
- injectReleaseNotesIntoReadme,
60
- injectSection,
61
- matchesAudience,
62
- parseCommitMessage,
63
- publishPackage,
64
- pushRelease,
65
- releasePrepare,
66
- releasePrepareMono,
67
- renderReleaseNotesMulti,
68
- renderReleaseNotesSingle,
69
- reportPrepare,
70
- resolveCommandTags,
71
- resolveReadmePath,
72
- resolveReleaseTags,
73
- stripScope,
74
- writeReleaseTags
75
- };
@@ -1,4 +1,4 @@
1
- import { printError, printStep, printSuccess, reportWriteResult } from "@williamthorsen/node-monorepo-core";
1
+ import { printError, printStep, printSuccess, reportWriteResult } from "@williamthorsen/nmr-core";
2
2
  import { hasPackageJson, isGitRepo, usesPnpm } from "./checks.js";
3
3
  import { detectRepoType } from "./detectRepoType.js";
4
4
  import { scaffoldFiles } from "./scaffold.js";
@@ -1,4 +1,4 @@
1
- import type { WriteResult } from '@williamthorsen/node-monorepo-core';
1
+ import type { WriteResult } from '@williamthorsen/nmr-core';
2
2
  import type { RepoType } from './detectRepoType.ts';
3
3
  interface ScaffoldOptions {
4
4
  repoType: RepoType;
@@ -1,8 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { writeFileWithCheck } from "@williamthorsen/node-monorepo-core";
4
- import { findPackageRoot } from "../findPackageRoot.js";
5
- import { publishWorkflow, releaseConfigScript, releaseWorkflow } from "./templates.js";
3
+ import { findPackageRoot, writeFileWithCheck } from "@williamthorsen/nmr-core";
4
+ import { createGithubReleaseWorkflow, publishWorkflow, releaseConfigScript, releaseWorkflow } from "./templates.js";
6
5
  function copyCliffTemplate(dryRun, overwrite) {
7
6
  const destPath = ".config/git-cliff.toml";
8
7
  const root = findPackageRoot(import.meta.url);
@@ -21,8 +20,12 @@ function copyCliffTemplate(dryRun, overwrite) {
21
20
  }
22
21
  function scaffoldFiles({ repoType, dryRun, overwrite, withConfig }) {
23
22
  const results = [
24
- writeFileWithCheck(".github/workflows/release.yaml", releaseWorkflow(repoType), { dryRun, overwrite }),
25
- writeFileWithCheck(".github/workflows/publish.yaml", publishWorkflow(repoType), { dryRun, overwrite })
23
+ writeFileWithCheck(".github/workflows/create-github-release.yaml", createGithubReleaseWorkflow(repoType), {
24
+ dryRun,
25
+ overwrite
26
+ }),
27
+ writeFileWithCheck(".github/workflows/publish.yaml", publishWorkflow(repoType), { dryRun, overwrite }),
28
+ writeFileWithCheck(".github/workflows/release.yaml", releaseWorkflow(repoType), { dryRun, overwrite })
26
29
  ];
27
30
  if (withConfig) {
28
31
  results.push(
@@ -1,4 +1,5 @@
1
1
  import type { RepoType } from './detectRepoType.ts';
2
2
  export declare function releaseConfigScript(repoType: RepoType): string;
3
3
  export declare function publishWorkflow(repoType: RepoType): string;
4
+ export declare function createGithubReleaseWorkflow(repoType: RepoType): string;
4
5
  export declare function releaseWorkflow(repoType: RepoType): string;
@@ -3,8 +3,12 @@ function releaseConfigScript(repoType) {
3
3
  return `import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
4
4
 
5
5
  const config: ReleaseKitConfig = {
6
- // Uncomment to exclude components from release processing:
7
- // components: [
6
+ releaseNotes: {
7
+ shouldInjectIntoReadme: true,
8
+ },
9
+
10
+ // Uncomment to exclude workspaces from release processing:
11
+ // workspaces: [
8
12
  // { dir: 'my-package', shouldExclude: true },
9
13
  // ],
10
14
 
@@ -23,6 +27,10 @@ export default config;
23
27
  return `import type { ReleaseKitConfig } from '@williamthorsen/release-kit';
24
28
 
25
29
  const config: ReleaseKitConfig = {
30
+ releaseNotes: {
31
+ shouldInjectIntoReadme: true,
32
+ },
33
+
26
34
  // Formatting: prettier is auto-detected. Set formatCommand to override.
27
35
 
28
36
  // Uncomment to override the default version patterns:
@@ -54,6 +62,27 @@ jobs:
54
62
  uses: williamthorsen/node-monorepo-tools/.github/workflows/publish.reusable.yaml@workflow/publish-v1
55
63
  with:
56
64
  provenance: true
65
+ tags: \${{ github.ref_name }}
66
+ `;
67
+ }
68
+ function createGithubReleaseWorkflow(repoType) {
69
+ const tagPattern = repoType === "monorepo" ? "'*-v[0-9]*.[0-9]*.[0-9]*'" : "'v[0-9]*.[0-9]*.[0-9]*'";
70
+ return `# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
71
+ name: Create GitHub Release
72
+
73
+ on:
74
+ push:
75
+ tags:
76
+ - ${tagPattern}
77
+
78
+ permissions:
79
+ contents: write
80
+
81
+ jobs:
82
+ create-github-release:
83
+ uses: williamthorsen/node-monorepo-tools/.github/workflows/create-github-release.reusable.yaml@workflow/create-github-release-v1
84
+ with:
85
+ tag: \${{ github.ref_name }}
57
86
  `;
58
87
  }
59
88
  function releaseWorkflow(repoType) {
@@ -65,7 +94,7 @@ on:
65
94
  workflow_dispatch:
66
95
  inputs:
67
96
  only:
68
- description: 'Components to release (comma-separated, leave empty for all)'
97
+ description: 'Workspaces to release (comma-separated, leave empty for all)'
69
98
  required: false
70
99
  type: string
71
100
  bump:
@@ -78,7 +107,7 @@ on:
78
107
  - minor
79
108
  - major
80
109
  force:
81
- description: 'Force a release even when there are no commits since the last tag (requires --bump)'
110
+ description: 'Force a release even when no commits or no bump-worthy commits exist (defaults to patch; combine with --bump for a different level)'
82
111
  required: false
83
112
  type: boolean
84
113
  default: false
@@ -112,7 +141,7 @@ on:
112
141
  - minor
113
142
  - major
114
143
  force:
115
- description: 'Force a release even when there are no commits since the last tag (requires --bump)'
144
+ description: 'Force a release even when no commits or no bump-worthy commits exist (defaults to patch; combine with --bump for a different level)'
116
145
  required: false
117
146
  type: boolean
118
147
  default: false
@@ -130,6 +159,7 @@ jobs:
130
159
  `;
131
160
  }
132
161
  export {
162
+ createGithubReleaseWorkflow,
133
163
  publishWorkflow,
134
164
  releaseConfigScript,
135
165
  releaseWorkflow
@@ -1,2 +1,7 @@
1
- export declare function injectReleaseNotesIntoReadme(readmePath: string, changelogJsonPath: string, tag: string): string | undefined;
1
+ export interface RenderedInjectedReadme {
2
+ injectedReadme: string;
3
+ releaseNotesMarkdown: string;
4
+ }
5
+ export declare function renderInjectedReadme(readme: string, changelogJsonPath: string, tag: string, sectionOrder?: string[]): RenderedInjectedReadme | undefined;
6
+ export declare function injectReleaseNotesIntoReadme(readmePath: string, changelogJsonPath: string, tag: string, sectionOrder?: string[]): string | undefined;
2
7
  export declare function resolveReadmePath(workspacePath: string): string | undefined;
@@ -3,12 +3,11 @@ import { join } from "node:path";
3
3
  import { extractVersion, readChangelogEntries } from "./changelogJsonUtils.js";
4
4
  import { injectSection } from "./injectSection.js";
5
5
  import { matchesAudience, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
6
- function injectReleaseNotesIntoReadme(readmePath, changelogJsonPath, tag) {
6
+ function renderInjectedReadme(readme, changelogJsonPath, tag, sectionOrder) {
7
7
  if (!existsSync(changelogJsonPath)) {
8
8
  console.warn(`Warning: ${changelogJsonPath} not found; skipping README injection`);
9
9
  return void 0;
10
10
  }
11
- const originalReadme = readFileSync(readmePath, "utf8");
12
11
  const version = extractVersion(tag);
13
12
  const entries = readChangelogEntries(changelogJsonPath);
14
13
  if (entries === void 0) {
@@ -20,16 +19,29 @@ function injectReleaseNotesIntoReadme(readmePath, changelogJsonPath, tag) {
20
19
  console.warn(`Warning: no changelog entry for version ${version}; skipping README injection`);
21
20
  return void 0;
22
21
  }
23
- const releaseNotesMarkdown = renderReleaseNotesSingle(entry, {
22
+ const renderedSections = renderReleaseNotesSingle(entry, {
24
23
  filter: matchesAudience("all"),
25
- includeHeading: false
24
+ includeHeading: false,
25
+ ...sectionOrder === void 0 ? {} : { sectionOrder }
26
26
  });
27
- if (releaseNotesMarkdown.trimEnd().length === 0) {
27
+ if (renderedSections.trimEnd().length === 0) {
28
28
  console.warn(`Warning: no user-facing release notes for version ${version}; skipping README injection`);
29
29
  return void 0;
30
30
  }
31
- const injected = injectSection(originalReadme, "release-notes", releaseNotesMarkdown.trimEnd());
32
- writeFileSync(readmePath, injected, "utf8");
31
+ const labeledHeading = `## Release notes \u2014 v${version} (${entry.date})`;
32
+ const releaseNotesMarkdown = `${labeledHeading}
33
+
34
+ ${renderedSections.trimEnd()}`;
35
+ const injectedReadme = injectSection(readme, "release-notes", releaseNotesMarkdown);
36
+ return { injectedReadme, releaseNotesMarkdown };
37
+ }
38
+ function injectReleaseNotesIntoReadme(readmePath, changelogJsonPath, tag, sectionOrder) {
39
+ const originalReadme = readFileSync(readmePath, "utf8");
40
+ const rendered = renderInjectedReadme(originalReadme, changelogJsonPath, tag, sectionOrder);
41
+ if (rendered === void 0) {
42
+ return void 0;
43
+ }
44
+ writeFileSync(readmePath, rendered.injectedReadme, "utf8");
33
45
  return originalReadme;
34
46
  }
35
47
  function resolveReadmePath(workspacePath) {
@@ -38,5 +50,6 @@ function resolveReadmePath(workspacePath) {
38
50
  }
39
51
  export {
40
52
  injectReleaseNotesIntoReadme,
53
+ renderInjectedReadme,
41
54
  resolveReadmePath
42
55
  };
@@ -1,5 +1,15 @@
1
- import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseKitConfig } from './types.ts';
1
+ import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseKitConfig, WorkTypeConfig } from './types.ts';
2
+ export declare const ROOT_PACKAGE_JSON_PATH = "package.json";
3
+ export declare function readRootPackageVersion(): {
4
+ exists: boolean;
5
+ version: string | undefined;
6
+ };
2
7
  export declare const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
3
8
  export declare function loadConfig(): Promise<unknown>;
4
- export declare function mergeMonorepoConfig(discoveredPaths: string[], userConfig: ReleaseKitConfig | undefined): MonorepoReleaseConfig;
9
+ export interface RootPackageInfo {
10
+ exists: boolean;
11
+ version: string | undefined;
12
+ }
13
+ export declare function mergeMonorepoConfig(discoveredPaths: string[], userConfig: ReleaseKitConfig | undefined, rootPackage?: RootPackageInfo): MonorepoReleaseConfig;
5
14
  export declare function mergeSinglePackageConfig(userConfig: ReleaseKitConfig | undefined): ReleaseConfig;
15
+ export declare function resolveWorkTypes(userWorkTypes?: Record<string, WorkTypeConfig>): Record<string, WorkTypeConfig>;