@williamthorsen/release-kit 14.1.0 → 14.2.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 (92) hide show
  1. package/.meta/changelog.json +125 -0
  2. package/.readyup/kits/default.js +63 -6
  3. package/.readyup/kits/npm-auto-publish.js +47 -35
  4. package/.readyup/manifest.json +12 -7
  5. package/CHANGELOG.md +33 -0
  6. package/README.md +18 -8
  7. package/dist/esm/assertTaggedBaseline.d.ts +2 -2
  8. package/dist/esm/attachChangelogDiagnostics.d.ts +2 -2
  9. package/dist/esm/bin/release-kit.js +5 -4
  10. package/dist/esm/buildChangelogEntries.d.ts +3 -3
  11. package/dist/esm/buildChangelogEntries.js +3 -3
  12. package/dist/esm/buildDependencyGraph.d.ts +11 -11
  13. package/dist/esm/buildDependencyGraph.js +13 -13
  14. package/dist/esm/buildReleaseSummary.d.ts +3 -3
  15. package/dist/esm/buildReleaseSummary.js +4 -12
  16. package/dist/esm/changelogArchive.d.ts +14 -0
  17. package/dist/esm/changelogArchive.js +21 -0
  18. package/dist/esm/changelogJsonFile.d.ts +2 -2
  19. package/dist/esm/changelogOverrides.d.ts +9 -8
  20. package/dist/esm/changelogOverrides.js +3 -6
  21. package/dist/esm/createGithubReleaseCommand.d.ts +2 -1
  22. package/dist/esm/createGithubReleaseCommand.js +7 -4
  23. package/dist/esm/deriveDirectoryConfig.d.ts +13 -0
  24. package/dist/esm/deriveDirectoryConfig.js +59 -0
  25. package/dist/esm/deriveWorkspaceConfig.js +0 -1
  26. package/dist/esm/discoverWorkspaces.d.ts +3 -3
  27. package/dist/esm/findTransitiveDependents.js +3 -3
  28. package/dist/esm/findUnroutedEntryScopes.d.ts +2 -2
  29. package/dist/esm/formatDirectoryPublishSkip.d.ts +3 -0
  30. package/dist/esm/formatDirectoryPublishSkip.js +3 -0
  31. package/dist/esm/formatExcludedSkip.d.ts +5 -2
  32. package/dist/esm/formatExcludedSkip.js +2 -2
  33. package/dist/esm/formatProjectPublishSkip.d.ts +3 -0
  34. package/dist/esm/formatProjectPublishSkip.js +3 -0
  35. package/dist/esm/generateChangelogs.d.ts +7 -7
  36. package/dist/esm/generateChangelogs.js +2 -2
  37. package/dist/esm/help/prepareHelp.d.ts +1 -1
  38. package/dist/esm/help/prepareHelp.js +7 -5
  39. package/dist/esm/init/templates.js +1 -1
  40. package/dist/esm/loadConfig.d.ts +12 -12
  41. package/dist/esm/loadConfig.js +79 -30
  42. package/dist/esm/prepareCommand.d.ts +1 -1
  43. package/dist/esm/prepareCommand.js +47 -46
  44. package/dist/esm/prepareTarget.d.ts +103 -0
  45. package/dist/esm/prepareTarget.js +274 -0
  46. package/dist/esm/previewTagPrefixes.d.ts +5 -3
  47. package/dist/esm/previewTagPrefixes.js +58 -13
  48. package/dist/esm/propagateBumps.js +1 -1
  49. package/dist/esm/publishCommand.js +13 -1
  50. package/dist/esm/readChangelogSections.d.ts +6 -4
  51. package/dist/esm/readChangelogSections.js +18 -4
  52. package/dist/esm/releasePlan.d.ts +4 -2
  53. package/dist/esm/releasePlan.js +34 -12
  54. package/dist/esm/releasePrepareTargets.d.ts +47 -0
  55. package/dist/esm/releasePrepareTargets.js +335 -0
  56. package/dist/esm/renderChangelogMarkdown.d.ts +23 -0
  57. package/dist/esm/renderChangelogMarkdown.js +47 -11
  58. package/dist/esm/reportPrepare.d.ts +2 -3
  59. package/dist/esm/reportPrepare.js +24 -12
  60. package/dist/esm/resolveCommandTags.d.ts +3 -1
  61. package/dist/esm/resolveCommandTags.js +15 -22
  62. package/dist/esm/resolveReleaseDeclaration.d.ts +45 -0
  63. package/dist/esm/resolveReleaseDeclaration.js +153 -0
  64. package/dist/esm/resolveReleaseTags.d.ts +14 -3
  65. package/dist/esm/resolveReleaseTags.js +36 -15
  66. package/dist/esm/resolveReleaseTargets.d.ts +29 -0
  67. package/dist/esm/resolveReleaseTargets.js +131 -0
  68. package/dist/esm/sync-labels/generateCommand.js +2 -1
  69. package/dist/esm/sync-labels/resolveLabels.d.ts +4 -3
  70. package/dist/esm/sync-labels/resolveLabels.js +30 -1
  71. package/dist/esm/sync-labels/types.d.ts +2 -0
  72. package/dist/esm/types.d.ts +251 -167
  73. package/dist/esm/types.js +22 -0
  74. package/dist/esm/validateConfig.js +80 -21
  75. package/dist/esm/validateOverridesCommand.d.ts +1 -1
  76. package/dist/esm/validateOverridesCommand.js +17 -28
  77. package/docs/api.md +25 -58
  78. package/docs/changelogs.md +26 -6
  79. package/docs/configuration.md +79 -16
  80. package/docs/labels.md +25 -2
  81. package/docs/project-releases.md +42 -2
  82. package/docs/publishing.md +3 -3
  83. package/docs/releasing.md +4 -4
  84. package/docs/work-types.md +1 -1
  85. package/package.json +2 -2
  86. package/presets/labels/common.yaml +11 -0
  87. package/dist/esm/releasePrepare.d.ts +0 -36
  88. package/dist/esm/releasePrepare.js +0 -230
  89. package/dist/esm/releasePrepareMono.d.ts +0 -29
  90. package/dist/esm/releasePrepareMono.js +0 -503
  91. package/dist/esm/releasePrepareProject.d.ts +0 -47
  92. package/dist/esm/releasePrepareProject.js +0 -164
@@ -1,6 +1,6 @@
1
1
  # Project releases
2
2
 
3
- How to add a project-level release to a monorepo, what the `project` block accepts, and how that release interacts with `prepare`'s flags.
3
+ How to add a project-level release to a monorepo, what the `project` block accepts, how that release interacts with `prepare`'s flags, and how to declare it as a `releases` composite instead.
4
4
 
5
5
  Some monorepos release a single combined deliverable, such as a Chrome extension, a CLI binary, or a packaged desktop app, for which the per-workspace tags and changelogs alone do not describe what the user actually receives. Declare the optional `project` block to add a project-level release stage that runs alongside the per-workspace pipeline.
6
6
 
@@ -69,6 +69,46 @@ CLI flag interactions:
69
69
  - `--bump=major|minor|patch` propagates to the project release as a level chooser. It does not trigger a release on its own when the window doesn't have any commits or any bump-worthy commits.
70
70
  - `--force` runs the project release even when the history since the last project tag doesn't have any commits or any bump-worthy commits. Defaults to patch when `--bump` is not given; combine with `--bump=X` to release at a different level.
71
71
  - `--only` narrows the run to the named workspaces and their dependents and skips the project release, which is reported as a warning. The project release rolls up every contributing workspace, and `--only` changes which workspaces those are, so rolling up a narrowed set would release a project version covering work that the run excluded. Run a full `prepare` (without `--only`) to include the project release.
72
- - `--set-version` is rejected with an error when `project` is configured. `--set-version` sets individual workspaces' versions, but a project release rolls up every contributing workspace; the two semantics don't compose. To use `--set-version`, run on a config without a `project` block.
72
+ - `--set-version` is rejected with an error when `project` or a `releases` composite is configured. `--set-version` sets individual workspaces' versions, but a project release rolls up every contributing workspace; the two semantics don't compose. To use `--set-version`, run on a config without a `project` block.
73
+
74
+ A `releases` composite has a name, so `--only` can select it: `--only suite` prepares the composite alone, from its own window, and `--only nmr,suite` prepares `nmr`, its dependents, and the composite. A run narrowed by `--only` that doesn't name the composite skips it with a warning.
73
75
 
74
76
  How `--bump` and `--force` combine for every target is in [Releasing](releasing.md#release-kit-prepare).
77
+
78
+ ## Moving to `releases`
79
+
80
+ The [`releases`](configuration.md#releases) block declares the same targets by name, and every setting of the `workspaces` and `project` blocks has a counterpart in it, except `project.paths`. A config uses one surface or the other; tag prefixes are unchanged either way.
81
+
82
+ | Legacy setting | `releases` entry |
83
+ | ------------------------------------------------- | ----------------------------------------------------- |
84
+ | `workspaces: [{ dir: 'x', shouldExclude: true }]` | `x: false` |
85
+ | `workspaces: [{ dir: 'x', legacyIdentities }]` | `x: { path: 'packages/x', legacyIdentities }` |
86
+ | `project: {}` | `<name>: { includes: [...every retained workspace] }` |
87
+ | `project: { tagPrefix: 'app-v' }` | `<name>: { includes: [...], tagPrefix: 'app-v' }` |
88
+ | `project: { paths }` | No counterpart; keep the `project` block |
89
+
90
+ Unlike a `project` block, a composite releases only its listed members' commits, and it has a name that `--only` can select. A workspace left out of `includes` still releases on its own.
91
+
92
+ A `project` block whose window covers the whole repo only because every workspace is excluded maps to the [repo-root target](configuration.md#the-repo-root), `<name>: { path: '.' }`, together with a `false` entry for each excluded workspace. The root target reads the whole repo by declaration, so a workspace added later doesn't narrow it.
93
+
94
+ ## Publishing and GitHub Releases
95
+
96
+ `push`, `publish`, and `create-github-release` resolve the project tag on HEAD through the `tagPrefix` of the `project` block or the `releases` composite, alongside the workspace tags:
97
+
98
+ - `release-kit push` pushes the project tag with the release commit, including when the project tag is the only release tag on HEAD.
99
+ - `release-kit publish` never publishes the project tag, because the project does not have an npm package. When `--tags` names it, `publish` warns `Skipping v0.13.0: a project tag is not an npm package.` and publishes nothing for it.
100
+ - `release-kit create-github-release` creates a GitHub Release for the project tag from `./.meta/changelog.json`, whatever the root `package.json#private` says. A private workspace's tag is still skipped, as [Publishing](publishing.md#publishability-filter) describes.
101
+
102
+ The `create-github-release.yaml` workflow that `release-kit init` scaffolds for a monorepo triggers only on `'*-v[0-9]*.[0-9]*.[0-9]*'`, which requires a literal `-v` and does not match a `v<version>` project tag. Add the project pattern to its tag filter:
103
+
104
+ ```yaml
105
+ on:
106
+ push:
107
+ tags:
108
+ - '*-v[0-9]*.[0-9]*.[0-9]*'
109
+ - 'v[0-9]*.[0-9]*.[0-9]*'
110
+ ```
111
+
112
+ A GitHub tag filter matches from the start of the tag, so `'v[0-9]*.[0-9]*.[0-9]*'` does not match a workspace tag such as `core-v1.0.0`. A custom `tagPrefix` needs its own pattern, such as `'app-v[0-9]*.[0-9]*.[0-9]*'` for `tagPrefix: 'app-v'`. `publish.yaml` does not need the project pattern, because `publish` skips the project tag.
113
+
114
+ When every workspace sets `private: true`, `init` does not create `create-github-release.yaml` (see [Releasing](releasing.md)). A project-release repo of that shape adds the workflow itself.
@@ -8,14 +8,14 @@ Publishes packages that have release tags on HEAD. The publish workflow's reusab
8
8
 
9
9
  ### Publishability filter
10
10
 
11
- `publish` and `create-github-release` operate only on workspaces whose `package.json#private` is absent or `false`. A workspace marked `private: true` is "versioned but not published": It can still be tagged by `release-kit tag` and get a `CHANGELOG.md` entry, but it is skipped by both `release-kit publish` (no registry publish) and `release-kit create-github-release` (no GitHub Release). `private: true` alone excludes a workspace from publish and announce; it doesn't require any extra release config. Other commands ignore this filter and operate on private workspaces unchanged.
11
+ `publish` and `create-github-release` operate only on workspaces whose `package.json#private` is absent or `false`. A workspace marked `private: true` is "versioned but not published": It can still be tagged by `release-kit tag` and get a `CHANGELOG.md` entry, but it is skipped by both `release-kit publish` (no registry publish) and `release-kit create-github-release` (no GitHub Release). `private: true` alone excludes a workspace from publish and announce; it doesn't require any extra release config. Other commands ignore this filter and operate on private workspaces unchanged. A project tag is the exception: `publish` never publishes it, and `create-github-release` creates a Release for it whatever the root `package.json#private` says (see [Project releases](project-releases.md#publishing-and-github-releases)).
12
12
 
13
13
  An unpublishable tag is always skipped cleanly, never fatal. For `release-kit publish`, whether the skip is announced depends on how the tag was resolved:
14
14
 
15
15
  - **Without `--tags`** (implicit resolution): Unpublishable tags on HEAD are silently filtered. The pre-publish listing shows only the publishable subset. If the filter empties the set, `release-kit publish` prints `Nothing to publish.` and exits 0.
16
16
  - **With `--tags`** (explicit naming): Each named tag pointing at an unpublishable workspace is skipped with a warning, and any publishable tags named in the same command are still published.
17
17
 
18
- `release-kit create-github-release` skips unpublishable workspaces with a warning regardless of `--tags`; an all-private tag set is a clean no-op.
18
+ `release-kit create-github-release` skips unpublishable workspaces with a warning regardless of `--tags`; an all-private tag set is a clean no-op. A project tag is not skipped.
19
19
 
20
20
  Example warning when an explicit tag is unpublishable:
21
21
 
@@ -25,6 +25,6 @@ Skipping basic-v1.0.0 (packages/basic): package.json#private is true.
25
25
 
26
26
  ## `release-kit create-github-release`
27
27
 
28
- Creates GitHub Releases from `changelog.json` for tags on HEAD. Independent of `npm publish`: Invoking this command creates Releases for publishable packages whether or not they were published to a registry. Private workspaces are skipped (see [Publishability filter](#publishability-filter)).
28
+ Creates GitHub Releases from `changelog.json` for tags on HEAD. Independent of `npm publish`: Invoking this command creates Releases for publishable packages whether or not they were published to a registry. Private workspaces are skipped, and a project tag gets a Release (see [Publishability filter](#publishability-filter)).
29
29
 
30
30
  When `--tags` is omitted, every release tag pointing at HEAD is processed. The CLI requires the `gh` CLI on `PATH` and `contents: write` permission. The bundled `create-github-release.reusable.yaml` GitHub Actions workflow runs this command in CI.
package/docs/releasing.md CHANGED
@@ -15,7 +15,7 @@ Scaffolded files:
15
15
  - `.github/workflows/release.yaml`: Workflow that delegates to a reusable release workflow
16
16
  - `.config/release-kit.config.ts`: Starter config with commented-out customization examples (with `--with-config`)
17
17
 
18
- When every workspace, or the single package, sets `private: true`, `init` skips `create-github-release.yaml` and `publish.yaml`, `--force` included, and reports each skip: `publish` and `create-github-release` skip private packages, so neither workflow would do anything. Its next steps then leave out the provenance and trusted-publisher steps.
18
+ When every workspace, or the single package, sets `private: true`, `init` skips `create-github-release.yaml` and `publish.yaml`, `--force` included, and reports each skip: `publish` and `create-github-release` skip private packages, so neither workflow would do anything. A repo with a `project` block is the exception: `create-github-release` still creates a Release for its project tag, so that repo adds `create-github-release.yaml` itself (see [Project releases](project-releases.md#publishing-and-github-releases)). Its next steps then leave out the provenance and trusted-publisher steps.
19
19
 
20
20
  ## `release-kit update-templates`
21
21
 
@@ -31,7 +31,7 @@ The workflows are managed files: release-kit renders them, and a change made by
31
31
 
32
32
  Runs release preparation with automatic workspace discovery.
33
33
 
34
- `--only` names the workspaces from which the release starts, by package directory name (e.g., `arrays`, `release-kit`). The run also releases every workspace that depends on one of them, directly or transitively: A dependent with commits of its own releases at its own bump, and one without releases at a propagated patch bump, so that it publishes against the new version. Workspaces outside that set are left out of the run.
34
+ `--only` names the release targets from which the release starts, by target name: a workspace's directory name (e.g., `arrays`, `release-kit`) unless a [`releases`](configuration.md#releases) entry renames it, or a `releases` composite's name. A named composite releases from its own window, whether or not the run includes its members; a run narrowed by `--only` skips every composite that it doesn't name. The run also releases every workspace that depends on one of them, directly or transitively: A dependent with commits of its own releases at its own bump, and one without releases at a propagated patch bump, so that it publishes against the new version. Workspaces outside that set are left out of the run.
35
35
 
36
36
  `prepare` reads each target's history once and builds the changelog items of its unreleased commits. The bump is the highest level that those items call for, under [`versionPatterns`](configuration.md#versionpatterns), so the bump and the changelog describe the same release: A `feat` entry in a [change-record block](changelogs.md#change-record-blocks) bumps minor under a `docs:` title, and a breaking item bumps major.
37
37
 
@@ -88,7 +88,7 @@ docs/*.v*.md
88
88
 
89
89
  The `--set-version` flag is a first-class escape hatch for the cases in which the derived bump produces the wrong version, most notably when promoting a pre-1.0 package to 1.0.0. Pre-1.0 packages collapse a `feat!` breaking change to a minor bump (matching semantic-release's `initialMajor: false` and release-please's `bump-minor-pre-major`), so a deliberate promotion to 1.0.0 must be requested explicitly.
90
90
 
91
- In a single-package repo, the value is the version: `--set-version 1.0.0`. In a monorepo, it names the workspace with each version, as comma-separated `<workspace>@N.N.N` entries: `--set-version arrays@1.0.0,strings@2.0.0`. Each named workspace releases at its version whether or not it has commits, and starts the release like a workspace named by `--only`.
91
+ In a single-package repo, the value is the version: `--set-version 1.0.0`. In a monorepo, it names the workspace or [directory target](configuration.md#directory-targets) with each version, as comma-separated `<name>@N.N.N` entries: `--set-version arrays@1.0.0,strings@2.0.0`. Each named target releases at its version whether or not it has commits, and starts the release like a target named by `--only`.
92
92
 
93
93
  The flag validates that:
94
94
 
@@ -126,7 +126,7 @@ The `init` command scaffolds a release workflow at `.github/workflows/release.ya
126
126
 
127
127
  | Input | Type | Description |
128
128
  | ------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
129
- | `only` | string | Workspaces to release, with their dependents (comma-separated, leave empty for all) |
129
+ | `only` | string | Release targets to release, with their dependents (comma-separated, leave empty for all) |
130
130
  | `bump` | choice | Bump type: `auto` (default, detects it from commits), `patch`, `minor`, or `major` |
131
131
  | `force` | boolean | Release even when the window doesn't have any commits or any bump-worthy commits (defaults to patch; combine with `bump` for a different level) |
132
132
 
@@ -51,7 +51,7 @@ Each work-type has a `breakingPolicy` value:
51
51
 
52
52
  A `BREAKING CHANGE:` body footer on a `forbidden`-policy type triggers the same warning path as the prefix `!` does: A type that cannot be breaking is parsed as non-breaking whichever surface has the signal. The warning is all that a footer triggers; it doesn't raise a bump (see [`🚨 **Breaking:**` bullet marker](#-breaking-bullet-marker)).
53
53
 
54
- The release-prepare orchestrators (`releasePrepare`, `releasePrepareMono`, `releasePrepareProject`) apply `DEFAULT_BREAKING_POLICIES` automatically. Violations in the titles and change-record entries of each workspace's or project's unreleased window are collected onto the corresponding result's `policyViolations` field, on a skipped result as on a released one, and rendered under the section in the prepare report:
54
+ The release-prepare orchestrator (`releasePrepareTargets`) applies `DEFAULT_BREAKING_POLICIES` automatically. Violations in the titles and change-record entries of each workspace's or project's unreleased window are collected onto the corresponding result's `policyViolations` field, on a skipped result as on a released one, and rendered under the section in the prepare report:
55
55
 
56
56
  ```
57
57
  arrays
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@williamthorsen/release-kit",
3
- "version": "14.1.0",
3
+ "version": "14.2.0",
4
4
  "description": "Version-bumping and changelog-generation toolkit for release workflows",
5
5
  "keywords": [
6
6
  "changelog",
@@ -51,7 +51,7 @@
51
51
  ],
52
52
  "dependencies": {
53
53
  "@williamthorsen/change-grammar": "0.2.0",
54
- "@williamthorsen/nmr-core": "0.13.2",
54
+ "@williamthorsen/nmr-core": "0.13.3",
55
55
  "@williamthorsen/toolbelt.errors": "0.8.3",
56
56
  "json-stringify-pretty-compact": "4.0.0",
57
57
  "semver": "7.8.5",
@@ -53,6 +53,17 @@
53
53
  - name: 'value:questionable'
54
54
  color: c5def5
55
55
 
56
+ # Confidence
57
+ - name: 'confidence:high'
58
+ color: 0b7285
59
+ description: Clearly worth doing
60
+ - name: 'confidence:medium'
61
+ color: 3bc9db
62
+ description: Worth doing, but arguable
63
+ - name: 'confidence:low'
64
+ color: 99e9f2
65
+ description: Not worth a ticket now
66
+
56
67
  # Triage
57
68
  - name: duplicate
58
69
  color: cfd3d7
@@ -1,36 +0,0 @@
1
- import type { ReleasePlan } from './releasePlan.js';
2
- import type { PrepareConfig, ReleaseType } from './types.js';
3
- /** Options for the release preparation workflow. */
4
- export interface ReleasePrepareOptions {
5
- /**
6
- * Release even when the range since the last tag doesn't contain any commits or any bump-worthy commits.
7
- * Orthogonal to `bumpOverride`: When `bumpOverride` is not given, the release falls back to `patch`.
8
- */
9
- force?: boolean;
10
- /** Level of the release, in place of the level that the changelog items call for; it doesn't trigger a release. */
11
- bumpOverride?: ReleaseType;
12
- /**
13
- * Explicit target version (canonical `N.N.N`) that bypasses commit-derived bump logic and takes precedence over
14
- * `bumpOverride` and `force`.
15
- */
16
- setVersion?: string;
17
- /**
18
- * If true, plan per-workspace release-notes previews under `{workspacePath}/docs/`
19
- * (`README.v{version}.md` and `RELEASE_NOTES.v{version}.md`) from each workspace's
20
- * changelog entries. Requires `config.changelogJson.enabled`; when disabled,
21
- * the planner records a warning on the plan and doesn't plan any previews.
22
- */
23
- withReleaseNotes?: boolean;
24
- }
25
- /**
26
- * Orchestrates the release preparation workflow for a single package.
27
- *
28
- * 1. Reads the release history once, and stops when the current version is recorded but untagged.
29
- * 2. Decides the release from the history's bump, `--force`, and `--bump` (or takes `--set-version`).
30
- * 3. Plans the bump of every configured `package.json` version field.
31
- * 4. Plans the changelogs from the same history, and the release-notes previews when requested.
32
- * 5. Renders the optional format command.
33
- *
34
- * Returns the plan without writing any file.
35
- */
36
- export declare function releasePrepare(config: PrepareConfig, options: ReleasePrepareOptions): ReleasePlan;
@@ -1,230 +0,0 @@
1
- import { join as joinPath } from 'node:path';
2
- import { assertTaggedBaseline, findUntaggedBaseline } from "./assertTaggedBaseline.js";
3
- import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
4
- import { readReleaseHistory, toReleaseEntries } from "./buildChangelogEntries.js";
5
- import { buildReleaseSummary } from "./buildReleaseSummary.js";
6
- import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
7
- import { applyChangelogOverrides, formatStaleOverrideKeyWarning, loadOverridesForScopes, } from "./changelogOverrides.js";
8
- import { decideRelease } from "./decideRelease.js";
9
- import { deriveSectionOrder } from "./deriveReleaseNotesConfig.js";
10
- import { hasPrettierConfig } from "./hasPrettierConfig.js";
11
- import { resolveWorkTypes } from "./loadConfig.js";
12
- import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
13
- import { planVersionBump, planVersionSet } from "./planVersionBump.js";
14
- import { planPreservedSections } from "./readChangelogSections.js";
15
- import { renderChangelogMarkdown } from "./renderChangelogMarkdown.js";
16
- export function releasePrepare(config, options) {
17
- const { bumpOverride, force, setVersion, withReleaseNotes } = options;
18
- const writes = [];
19
- const overridesResult = loadOverridesForScopes({ project: '.' });
20
- if (overridesResult.errors.length > 0) {
21
- throw new Error(`Failed to load changelog overrides:\n - ${overridesResult.errors.join('\n - ')}`);
22
- }
23
- const overrides = overridesResult.project;
24
- const history = readReleaseHistory(config, { tagPrefixes: [config.tagPrefix] });
25
- assertTaggedBaseline([
26
- findUntaggedBaseline({
27
- label: 'package',
28
- packageFiles: config.packageFiles,
29
- changelogPaths: config.changelogPaths,
30
- tagPrefixes: [config.tagPrefix],
31
- previousTag: history.previousTag,
32
- }, config),
33
- ]);
34
- const { commits } = history.unreleased;
35
- const tag = history.previousTag;
36
- const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
37
- let releaseType;
38
- let bump;
39
- if (setVersion === undefined) {
40
- const decision = decideRelease({
41
- naturalBump: history.unreleased.bump,
42
- commitCount: commits.length,
43
- force,
44
- bumpOverride,
45
- skipReasons: {
46
- noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
47
- noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
48
- },
49
- });
50
- if (decision.outcome === 'skip') {
51
- const skipped = buildSkippedSinglePackage(history, decision.skipReason);
52
- return {
53
- workspaces: [skipped],
54
- tags: [],
55
- writes: [],
56
- summary: '',
57
- formatCommand: undefined,
58
- };
59
- }
60
- releaseType = decision.releaseType;
61
- bump = planVersionBump(config.packageFiles, releaseType);
62
- }
63
- else {
64
- bump = planVersionSet(config.packageFiles, setVersion);
65
- }
66
- writes.push(...bump.writes);
67
- const newTag = `${config.tagPrefix}${bump.newVersion}`;
68
- const planWarnings = [];
69
- const changelogs = planSinglePackageChangelogs({
70
- config,
71
- history,
72
- newTag,
73
- overrides,
74
- overrideWarnings: planWarnings,
75
- });
76
- const { changelogFiles, changelogJsonFiles, changelogPreservation } = changelogs;
77
- const previewWrites = planSinglePackagePreviews(withReleaseNotes === true, config, newTag, changelogs.entries, planWarnings);
78
- writes.push(...changelogs.writes, ...previewWrites);
79
- const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? 'npx prettier --write' : undefined);
80
- let formatCommand;
81
- if (formatCommandStr !== undefined) {
82
- const modifiedFiles = [
83
- ...config.packageFiles,
84
- ...config.changelogPaths.map((changelogPath) => joinPath(changelogPath, 'CHANGELOG.md')),
85
- ...changelogJsonFiles,
86
- ];
87
- formatCommand = { command: `${formatCommandStr} ${modifiedFiles.join(' ')}`, files: modifiedFiles };
88
- }
89
- const released = buildReleasedSinglePackage({
90
- history,
91
- bump,
92
- newTag,
93
- changelogFiles,
94
- changelogPreservation,
95
- releaseType,
96
- bumpOverride: setVersion === undefined ? bumpOverride : undefined,
97
- setVersion,
98
- previewFiles: previewWrites.map((write) => write.path),
99
- });
100
- const plan = {
101
- workspaces: [released],
102
- tags: [newTag],
103
- writes,
104
- summary: buildReleaseSummary({ workspaces: [released] }),
105
- formatCommand,
106
- };
107
- if (planWarnings.length > 0) {
108
- plan.warnings = planWarnings;
109
- }
110
- return plan;
111
- }
112
- function buildSkippedSinglePackage(history, skipReason) {
113
- const { unreleased } = history;
114
- const skipped = {
115
- status: 'skipped',
116
- commitCount: unreleased.commits.length,
117
- parsedCommitCount: unreleased.parsedCommitCount,
118
- skipReason,
119
- };
120
- if (history.previousTag !== undefined) {
121
- skipped.previousTag = history.previousTag;
122
- }
123
- if (unreleased.unparseableCommits !== undefined) {
124
- skipped.unparseableCommits = unreleased.unparseableCommits;
125
- }
126
- attachChangelogDiagnostics(skipped, unreleased.diagnostics);
127
- return skipped;
128
- }
129
- function buildReleasedSinglePackage(args) {
130
- const { history, bump, newTag, changelogFiles, releaseType, bumpOverride, setVersion } = args;
131
- const { commits, diagnostics, parsedCommitCount, unparseableCommits } = history.unreleased;
132
- const released = {
133
- status: 'released',
134
- commitCount: commits.length,
135
- currentVersion: bump.currentVersion,
136
- newVersion: bump.newVersion,
137
- tag: newTag,
138
- bumpedFiles: bump.writes.map((write) => write.path),
139
- changelogFiles,
140
- commits,
141
- };
142
- if (history.previousTag !== undefined) {
143
- released.previousTag = history.previousTag;
144
- }
145
- if (releaseType !== undefined) {
146
- released.releaseType = releaseType;
147
- released.parsedCommitCount = parsedCommitCount;
148
- if (unparseableCommits !== undefined) {
149
- released.unparseableCommits = unparseableCommits;
150
- }
151
- }
152
- if (bumpOverride !== undefined) {
153
- released.bumpOverride = bumpOverride;
154
- }
155
- attachChangelogDiagnostics(released, diagnostics);
156
- if (setVersion !== undefined) {
157
- released.setVersion = setVersion;
158
- }
159
- if (args.previewFiles.length > 0) {
160
- released.previewFiles = args.previewFiles;
161
- }
162
- if (args.changelogPreservation.length > 0) {
163
- released.changelogPreservation = args.changelogPreservation;
164
- }
165
- return released;
166
- }
167
- function planSinglePackageChangelogs(args) {
168
- const { config, history, newTag, overrides, overrideWarnings } = args;
169
- const today = new Date().toISOString().slice(0, 10);
170
- const builtEntries = toReleaseEntries(history, newTag, today);
171
- const applied = applyChangelogOverrides(builtEntries, overrides);
172
- if (applied.errors.length > 0) {
173
- throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
174
- }
175
- overrideWarnings.push(...applied.warnings);
176
- const matchedSet = new Set(applied.matchedKeys);
177
- for (const overrideKey of overrides.keys()) {
178
- if (!matchedSet.has(overrideKey)) {
179
- overrideWarnings.push(formatStaleOverrideKeyWarning(overrideKey));
180
- }
181
- }
182
- const sectionOrder = deriveSectionOrder(resolveWorkTypes(config.workTypes));
183
- const changelogFiles = [];
184
- const changelogJsonFiles = [];
185
- const changelogPreservation = [];
186
- const writes = [];
187
- let firstMergedEntries = [];
188
- for (const changelogPath of config.changelogPaths) {
189
- const jsonPath = resolveChangelogJsonPath(config, changelogPath);
190
- const mergedEntries = mergeChangelogEntriesWithDisk(jsonPath, applied.entries);
191
- if (changelogFiles.length === 0) {
192
- firstMergedEntries = mergedEntries;
193
- }
194
- if (config.changelogJson.enabled) {
195
- writes.push({ path: jsonPath, content: renderChangelogJson(mergedEntries) });
196
- changelogJsonFiles.push(jsonPath);
197
- }
198
- const changelogFile = joinPath(changelogPath, 'CHANGELOG.md');
199
- const preserved = planPreservedSections(changelogFile, mergedEntries);
200
- writes.push({
201
- path: changelogFile,
202
- content: renderChangelogMarkdown(mergedEntries, { sectionOrder, preservedSections: preserved.sections }),
203
- });
204
- changelogFiles.push(changelogFile);
205
- if (preserved.preservation !== undefined) {
206
- changelogPreservation.push(preserved.preservation);
207
- }
208
- }
209
- return { changelogFiles, changelogJsonFiles, changelogPreservation, entries: firstMergedEntries, writes };
210
- }
211
- function planSinglePackagePreviews(withReleaseNotes, config, newTag, entries, warnings) {
212
- if (!withReleaseNotes) {
213
- return [];
214
- }
215
- if (!config.changelogJson.enabled) {
216
- warnings.push('--with-release-notes requires changelogJson.enabled; skipping preview generation');
217
- return [];
218
- }
219
- if (config.changelogPaths.length === 0) {
220
- return [];
221
- }
222
- const previews = planReleaseNotesPreviews({
223
- workspacePath: '.',
224
- tag: newTag,
225
- entries,
226
- sectionOrder: deriveSectionOrder(resolveWorkTypes(config.workTypes)),
227
- });
228
- warnings.push(...previews.warnings);
229
- return previews.writes;
230
- }
@@ -1,29 +0,0 @@
1
- import type { ReleasePlan } from './releasePlan.js';
2
- import type { ReleasePrepareOptions } from './releasePrepare.js';
3
- import type { MonorepoPrepareConfig } from './types.js';
4
- /** Options for monorepo release preparation. */
5
- export interface MonorepoPrepareOptions extends Omit<ReleasePrepareOptions, 'setVersion'> {
6
- /**
7
- * Workspace `dir`s from which the release starts; the run plans them and their transitive dependents. Present only
8
- * for a narrowed run, which skips the project release: The project tier rolls up every contributing workspace, and
9
- * the narrowing has changed which workspaces those are.
10
- */
11
- only?: readonly string[];
12
- /**
13
- * Explicit target versions (canonical `N.N.N`), keyed by workspace `dir`. Each named workspace starts the release
14
- * and releases at its version, which takes precedence over `bumpOverride` and `force`.
15
- */
16
- setVersions?: ReadonlyMap<string, string>;
17
- }
18
- /**
19
- * Orchestrates release preparation for a monorepo with multiple workspaces.
20
- *
21
- * Phase 0: Selects the planned workspaces: those from which the release starts and their transitive dependents.
22
- * Phase 1: Determines direct bumps from commits for each workspace.
23
- * Phase 2: Propagates bumps to dependents.
24
- * Phase 2b: Sorts the full release set topologically.
25
- * Phase 3: Plans bumps and changelogs in dependency order.
26
- * Phase 3b: Plans the project release.
27
- * Phase 4: Renders the format command.
28
- */
29
- export declare function releasePrepareMono(fullConfig: MonorepoPrepareConfig, options: MonorepoPrepareOptions): ReleasePlan;