@williamthorsen/release-kit 5.1.0 → 5.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.
- package/CHANGELOG.md +105 -65
- package/README.md +160 -57
- package/cliff.toml.template +26 -17
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.js +96 -3
- package/dist/esm/buildChangelogEntries.d.ts +1 -0
- package/dist/esm/buildChangelogEntries.js +39 -25
- package/dist/esm/checkWorkTypesDrift.d.ts +11 -0
- package/dist/esm/checkWorkTypesDrift.js +110 -0
- package/dist/esm/collectPolicyViolations.d.ts +6 -0
- package/dist/esm/collectPolicyViolations.js +15 -0
- package/dist/esm/createGithubRelease.d.ts +12 -2
- package/dist/esm/createGithubRelease.js +12 -8
- package/dist/esm/createGithubReleaseCommand.js +10 -6
- package/dist/esm/decideRelease.d.ts +3 -0
- package/dist/esm/decideRelease.js +19 -3
- package/dist/esm/defaults.d.ts +7 -0
- package/dist/esm/defaults.js +41 -20
- package/dist/esm/deriveWorkspaceConfig.js +3 -0
- package/dist/esm/determineBumpFromCommits.d.ts +6 -1
- package/dist/esm/determineBumpFromCommits.js +9 -3
- package/dist/esm/generateChangelogs.js +14 -29
- package/dist/esm/loadConfig.js +14 -22
- package/dist/esm/parseCommitMessage.d.ts +8 -2
- package/dist/esm/parseCommitMessage.js +32 -3
- package/dist/esm/publishCommand.js +21 -2
- package/dist/esm/releasePrepare.js +39 -15
- package/dist/esm/releasePrepareMono.js +26 -3
- package/dist/esm/releasePrepareProject.js +13 -1
- package/dist/esm/renderReleaseNotes.js +2 -1
- package/dist/esm/reportPrepare.js +18 -0
- package/dist/esm/resolveCommandTags.js +16 -6
- package/dist/esm/resolveReleaseTags.d.ts +8 -1
- package/dist/esm/resolveReleaseTags.js +11 -7
- package/dist/esm/runGitCliff.d.ts +2 -0
- package/dist/esm/runGitCliff.js +27 -0
- package/dist/esm/stripEmojiPrefix.d.ts +1 -0
- package/dist/esm/stripEmojiPrefix.js +7 -0
- package/dist/esm/syncWorkTypes.d.ts +10 -0
- package/dist/esm/syncWorkTypes.js +90 -0
- package/dist/esm/types.d.ts +15 -0
- package/dist/esm/work-types.json +127 -0
- package/dist/esm/work-types.schema.json +73 -0
- package/dist/esm/workTypesData.d.ts +14 -0
- package/dist/esm/workTypesData.js +59 -0
- package/dist/esm/workTypesUtils.d.ts +5 -0
- package/dist/esm/workTypesUtils.js +16 -0
- package/package.json +6 -3
- package/dist/esm/version.d.ts +0 -1
- package/dist/esm/version.js +0 -4
package/README.md
CHANGED
|
@@ -5,49 +5,39 @@ Version-bumping and changelog-generation toolkit for release workflows.
|
|
|
5
5
|
Provides a self-contained CLI that auto-discovers workspaces from `pnpm-workspace.yaml`, parses conventional commits, determines version bumps, updates `package.json` files, and generates changelogs with `git-cliff`.
|
|
6
6
|
|
|
7
7
|
<!-- section:release-notes -->
|
|
8
|
-
## Release notes — v5.
|
|
8
|
+
## Release notes — v5.2.0 (2026-05-04)
|
|
9
9
|
|
|
10
|
-
###
|
|
10
|
+
### 🎉 Features
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- Add emojis to changelog and release-note headings (#352)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Adds emoji prefixes to the section headings rendered in `CHANGELOG.md` and release notes generated by `@williamthorsen/release-kit`. Each of the 13 default work types gets a single decorative emoji so its section is easier to spot when skimming a release: 🐛 Bug fixes, 🎉 Features, 📚 Documentation, ♻️ Refactoring, ⚡ Performance, 🔒 Security, 🧪 Tests, ⚙️ Tooling, 👷 CI, 📦 Dependencies, 🏗️ Internal, 🗑️ Deprecated, and 🤖 Agentic support. Matching of `changelogJson.devOnlySections` is emoji-tolerant: existing consumer overrides written as bare names continue to work without modification.
|
|
15
15
|
|
|
16
|
-
-
|
|
16
|
+
- Surface bang violations in release prepare reports (#359)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Release-prepare flows now surface `!`-policy violations as warnings in the prepare report. Each workspace's and project's commit window is parsed against the default policy table — `internal!` is rejected as contradictory, bare `drop:` is rejected for missing the required `!`, and so on — and any violations appear under the workspace's section in the report alongside short hash, truncated subject, type, and surface (prefix or body). A new `breakingPolicies` config field lets consumers override individual entries or pass `{}` to disable enforcement entirely. Release-time enforcement remains tolerant: violations are warnings, never failures, so a single legacy commit cannot block a release.
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
### 🐛 Bug fixes
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
- Restrict publish to publishable workspaces (#345)
|
|
23
23
|
|
|
24
|
-
-
|
|
24
|
+
Fixes an issue where `release-kit publish` failed for workspaces marked `package.json#private: true`. The command now operates only on publishable workspaces — those where `private` is absent or `false` — and the rest of the release pipeline (`tag`, `create-github-release`, `prepare`, changelog) continues to handle private workspaces unchanged. This preserves the "versioned but not published" workflow: a private workspace can still be versioned, tagged, and published as a GitHub Release; only the registry-publish step is skipped. Without `--tags`, unpublishable tags are silently filtered (an empty result prints `Nothing to publish.` and exits 0). With `--tags` naming an unpublishable workspace, `release-kit publish` exits 1 with one error per offending tag, citing `package.json#private` and the workspace path.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
- Skip tooling-only releases instead of failing (#347)
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Fixes an issue where `release-kit create-github-release --tags <tag>` exited 1 whenever the tag's changelog had no all-audience content. The reusable `create-github-release.reusable.yaml` workflow forwards `github.ref_name` into `--tags`, so tooling-only releases consistently produced failed workflow runs even though no failure occurred. The command now exits 1 only when a requested tag has no changelog entry; intentional skip reasons (`no-audience-content`, `empty-body`) are informational. A typoed tag still surfaces an error even when batched alongside successful tags, and the info summary reports the per-tag skip reason for diagnostic visibility.
|
|
29
29
|
|
|
30
|
-
-
|
|
30
|
+
- Establish canonical work-types SSOT and restore changelog section ordering (#358)
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Restores canonical section ordering in changelogs and release notes — sections were appearing in unpredictable order after the previous release added emoji prefixes to section headers. Sections now follow a stable priority: public-facing types (Features, Fixes, Security, …) first, then internal types, then process types. Release-note bullets for breaking changes carry a `🚨 **Breaking:**` prefix so they stand out at a glance. Documentation entries move out of public release notes — they continue to appear in dev changelogs.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Closes #355.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
### ⚡ Performance
|
|
37
37
|
|
|
38
|
-
-
|
|
38
|
+
- Skip npx registry revalidation when running git-cliff (#361)
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- Make `--force` and `--bump` orthogonal (#328)
|
|
43
|
-
|
|
44
|
-
Decouples `--force` and `--bump` so each flag has a single responsibility, and unifies skip semantics across the per-workspace and project pipelines.
|
|
45
|
-
|
|
46
|
-
### Documentation
|
|
47
|
-
|
|
48
|
-
- Document tag prefix collisions as general rule (#320)
|
|
49
|
-
|
|
50
|
-
Documents the strict-prefix tag-prefix collision rule as a general validation rule that applies to every release-kit consumer declaring more than one tag prefix: across active workspaces, declared legacy identities, retired packages, and the optional `project` block. Previously, the rule appeared only inside the `Project releases` validation list.
|
|
40
|
+
Speeds up `release-kit prepare` by skipping the npm registry cache-revalidation HTTP request that ran on every `git-cliff` invocation. Per-invocation overhead drops from ~4.6 s to ~2.0 s; in a four-workspace monorepo this saves about 10 seconds per run. Also suppresses a transient stderr spinner that briefly appeared during package resolution and looked like a half-complete log message. Network fallback is preserved — runs on machines with an empty npx cache still resolve `git-cliff` over the network.
|
|
51
41
|
<!-- /section:release-notes -->
|
|
52
42
|
|
|
53
43
|
## Installation
|
|
@@ -151,16 +141,17 @@ The config file supports both `export default config` and `export const config =
|
|
|
151
141
|
|
|
152
142
|
### `ReleaseKitConfig` reference
|
|
153
143
|
|
|
154
|
-
| Field
|
|
155
|
-
|
|
|
156
|
-
| `cliffConfigPath`
|
|
157
|
-
| `workspaces`
|
|
158
|
-
| `formatCommand`
|
|
159
|
-
| `versionPatterns`
|
|
160
|
-
| `scopeAliases`
|
|
161
|
-
| `workTypes`
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
144
|
+
| Field | Type | Description |
|
|
145
|
+
| ------------------ | --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
146
|
+
| `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` → `cliff.toml` → bundled template |
|
|
147
|
+
| `workspaces` | `WorkspaceOverride[]` | Override or exclude discovered workspaces (matched by `dir`) |
|
|
148
|
+
| `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
|
|
149
|
+
| `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
|
|
150
|
+
| `scopeAliases` | `Record<string, string>` | Maps shorthand scope names to canonical names in commits |
|
|
151
|
+
| `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
|
|
152
|
+
| `breakingPolicies` | `Record<string, 'forbidden' \| 'optional' \| 'required'>` | Per-type `!`-policy lookup. Defaults to `DEFAULT_BREAKING_POLICIES`. Replaces the default entirely when provided. Set to `{}` to disable enforcement |
|
|
153
|
+
| `retiredPackages` | `RetiredPackage[]` | Packages that once lived in this repo but have been extracted or removed; suppresses undeclared-tag-prefix warnings |
|
|
154
|
+
| `project` | `ProjectConfig` | Opt-in project-level release block. Declaring `project: {}` (even empty) enables a project-release stage in `prepare` |
|
|
164
155
|
|
|
165
156
|
All fields are optional.
|
|
166
157
|
|
|
@@ -313,30 +304,93 @@ interface VersionPatterns {
|
|
|
313
304
|
|
|
314
305
|
Default: `{ major: ['!'], minor: ['feat'] }`
|
|
315
306
|
|
|
316
|
-
###
|
|
307
|
+
### Work types and tiers
|
|
308
|
+
|
|
309
|
+
The canonical taxonomy lives in `packages/release-kit/src/work-types.json` and is split into three tiers that drive section rendering and audience classification.
|
|
310
|
+
|
|
311
|
+
| Tier | Key | Header | Aliases | `!` policy |
|
|
312
|
+
| -------- | ----------- | ------------------------- | ------------- | ------------ |
|
|
313
|
+
| Public | `feat` | 🎉 Features | `feature` | optional |
|
|
314
|
+
| Public | `drop` | 🪦 Removed | | **required** |
|
|
315
|
+
| Public | `deprecate` | 🗑️ Deprecated | | forbidden |
|
|
316
|
+
| Public | `fix` | 🐛 Bug fixes | `bugfix` | forbidden |
|
|
317
|
+
| Public | `sec` | 🔒 Security | `security` | optional |
|
|
318
|
+
| Public | `perf` | ⚡ Performance | `performance` | forbidden |
|
|
319
|
+
| Internal | `internal` | 🏗️ Internal features | `utility` | forbidden |
|
|
320
|
+
| Internal | `refactor` | ♻️ Refactoring | | forbidden |
|
|
321
|
+
| Internal | `tests` | 🧪 Tests | `test` | forbidden |
|
|
322
|
+
| Process | `tooling` | ⚙️ Tooling | | forbidden |
|
|
323
|
+
| Process | `ci` | 👷 CI | | forbidden |
|
|
324
|
+
| Process | `deps` | 📦 Dependencies | `dep` | forbidden |
|
|
325
|
+
| Process | `ai` | 🤖 Agentic support | | forbidden |
|
|
326
|
+
| Process | `docs` | 📚 Documentation | `doc` | forbidden |
|
|
327
|
+
| Process | `fmt` | (excluded from changelog) | | forbidden |
|
|
328
|
+
|
|
329
|
+
#### Tier semantics
|
|
330
|
+
|
|
331
|
+
- **Public** — visible to all audiences. Public-tier sections appear in both public release notes and dev changelogs.
|
|
332
|
+
- **Internal** — dev-only. Internal-tier sections appear in dev changelogs but not in public-facing release notes.
|
|
333
|
+
- **Process** — dev-only. Same audience treatment as Internal.
|
|
334
|
+
|
|
335
|
+
Section render order is **tier order (Public → Internal → Process), then row order within tier**. The bundled `cliff.toml.template` encodes this order via hidden `<!-- NN -->` HTML-comment prefixes on each parser's `group` value; tera's `group_by` filter sorts groups lexicographically (now monotonic by row number), and the body template's `striptags` filter erases the prefix from rendered headings.
|
|
336
|
+
|
|
337
|
+
#### `docs` reclassification
|
|
338
|
+
|
|
339
|
+
`docs`/Documentation has moved from the all-audience tier (where it lived before this taxonomy was formalised) to the dev-only Process tier. **Documentation commits no longer appear in public-facing release notes.** They still appear in `CHANGELOG.md` and `changelog.json` under the `audience: 'dev'` classification.
|
|
340
|
+
|
|
341
|
+
#### `utility` alias
|
|
317
342
|
|
|
318
|
-
|
|
319
|
-
| ----------- | --------------- | ------------- |
|
|
320
|
-
| `fix` | Bug fixes | `bugfix` |
|
|
321
|
-
| `deprecate` | Deprecated | |
|
|
322
|
-
| `feat` | Features | `feature` |
|
|
323
|
-
| `internal` | Internal | |
|
|
324
|
-
| `perf` | Performance | `performance` |
|
|
325
|
-
| `refactor` | Refactoring | |
|
|
326
|
-
| `sec` | Security | `security` |
|
|
327
|
-
| `tests` | Tests | `test` |
|
|
328
|
-
| `tooling` | Tooling | |
|
|
329
|
-
| `ci` | CI | |
|
|
330
|
-
| `deps` | Dependencies | `dep` |
|
|
331
|
-
| `docs` | Documentation | `doc` |
|
|
332
|
-
| `ai` | Agentic support | |
|
|
333
|
-
| `fmt` | (skipped) | |
|
|
343
|
+
`utility:` is a backward-compat alias for `internal:`. Both forms parse to the same canonical type, route to the same `🏗️ Internal features` section, and are subject to the same `!` policy.
|
|
334
344
|
|
|
335
|
-
|
|
345
|
+
#### `!` (breaking change) policy
|
|
336
346
|
|
|
337
|
-
|
|
347
|
+
Each work-type carries a `breakingPolicy` value:
|
|
338
348
|
|
|
339
|
-
|
|
349
|
+
- `optional` (`feat`, `sec`) — `!` is allowed; both `type:` and `type!:` parse cleanly.
|
|
350
|
+
- `forbidden` (most types) — `!` is a policy violation. The premise: types like `internal!`, `perf!`, `fix!` are contradictory; an internal change cannot break a consumer contract, a pure perf change preserves the contract, and a bug-fix is by definition not a contract change.
|
|
351
|
+
- `required` (`drop`) — bare `drop:` is a policy violation; only `drop!:` is accepted. The premise: removing a feature always breaks consumers; the `!` form makes that explicit.
|
|
352
|
+
|
|
353
|
+
##### Two-tier policy enforcement
|
|
354
|
+
|
|
355
|
+
The `!` policy operates at two distinct levels with different semantics:
|
|
356
|
+
|
|
357
|
+
- **Write-time** (commit-msg hook) — strict rejection. Policy violations are blocked at the gate where the author can act on them immediately. _Hook-based enforcement is tracked separately and is not yet shipped._
|
|
358
|
+
- **Release-time** (`parseCommitMessage`) — tolerant warn-and-continue. Commits already in the log cannot be rewritten, so a policy-violating commit is parsed using its canonical type with `breaking: false` (the `!` is dropped from the parse) and a `onPolicyViolation` callback fires. Callers (`decideRelease` etc.) can collect these warnings and surface them in the release report. A single legacy `internal!` in a year-old log does not block releases.
|
|
359
|
+
|
|
360
|
+
A `BREAKING CHANGE:` body footer on a `forbidden`-policy type triggers the same warning path as the prefix `!` does — the spirit of the policy is "internal/perf/etc. cannot be breaking", which must apply to both surfaces.
|
|
361
|
+
|
|
362
|
+
The release-prepare orchestrators (`releasePrepare`, `releasePrepareMono`, `releasePrepareProject`) apply `DEFAULT_BREAKING_POLICIES` automatically. Violations encountered while parsing each workspace's or project's commit window are collected onto the corresponding result's `policyViolations` field and rendered under the section in the prepare report:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
arrays
|
|
366
|
+
Found 1 commits since arrays-v1.0.0
|
|
367
|
+
⚠️ 1 policy violation:
|
|
368
|
+
· def5678 'internal!: refactor cache' — type 'internal' at prefix surface
|
|
369
|
+
Bumping versions (patch)...
|
|
370
|
+
📦 1.0.0 → 1.0.1 (patch)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
To customize, set `breakingPolicies` in `release-kit.config.ts` — provide a partial map to override individual types, or `{}` to disable enforcement entirely (the parser falls back to `'optional'` for any missing type). Violations remain warnings, never failures.
|
|
374
|
+
|
|
375
|
+
#### `🚨 **Breaking:**` bullet marker
|
|
376
|
+
|
|
377
|
+
Items whose commit subject carries the `!` prefix (e.g. `feat!`, `drop!`, `feat(api)!`) are rendered with a `🚨 **Breaking:** ` prefix on the bullet:
|
|
378
|
+
|
|
379
|
+
```markdown
|
|
380
|
+
- 🚨 **Breaking:** Drop legacy /v1 endpoint
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Only the prefix `!` triggers this marker. A `BREAKING CHANGE:` body footer on its own does **not** retroactively mark a changelog item as breaking — the changelog signal is tied to the commit-prefix policy. This avoids surprise breaking-marker appearances for older commits written under earlier conventions.
|
|
384
|
+
|
|
385
|
+
#### `fmt`
|
|
386
|
+
|
|
387
|
+
`fmt:` commits are recognized by `parseCommitMessage` (they contribute to a patch bump) but `fmt` carries `excludedFromChangelog: true`. The bundled `cliff.toml.template` skips `fmt:` commits at the parser level, so they never appear in `CHANGELOG.md`, `changelog.json`, or release notes. The label and emoji are present in `work-types.json` for schema parity with the codeassembly upstream but never render.
|
|
388
|
+
|
|
389
|
+
#### Custom work types
|
|
390
|
+
|
|
391
|
+
Work types from your config are merged with these defaults by key — your entries override or extend, they don't replace the full set. Release-notes sections are rendered in the declaration order of the merged work-types record, with any unknown titles trailing the known ones.
|
|
392
|
+
|
|
393
|
+
The default `devOnlySections` (excluded from public release notes but still written to `CHANGELOG.md`) are derived from the Internal and Process tiers (excluding `fmt`). Override via `changelogJson.devOnlySections` in your config; matching is decorator-tolerant, so a bare-name override like `['Internal features']` keeps working against the emoji-prefixed and prefix-decorated default titles.
|
|
340
394
|
|
|
341
395
|
## CLI reference
|
|
342
396
|
|
|
@@ -404,6 +458,33 @@ release-kit prepare --only arrays --set-version 1.0.0
|
|
|
404
458
|
|
|
405
459
|
An empty changelog section is expected for a bare promotion, because the changelog is generated from commits since the last tag. To include a narrative entry, land a descriptive release commit (e.g., a `feat!` describing the stable API) before running `prepare`.
|
|
406
460
|
|
|
461
|
+
### `release-kit publish`
|
|
462
|
+
|
|
463
|
+
Publish packages that have release tags on HEAD. The publish workflow's reusable workflow `publish.reusable.yaml` invokes this command in CI.
|
|
464
|
+
|
|
465
|
+
| Flag | Description |
|
|
466
|
+
| ---------------------- | ---------------------------------------------------------------------------- |
|
|
467
|
+
| `--dry-run` | Preview without publishing |
|
|
468
|
+
| `--no-git-checks` | Skip the clean-working-tree check |
|
|
469
|
+
| `--tags=tag1,tag2,...` | Only publish the named tags (comma-separated, full tag names) |
|
|
470
|
+
| `--provenance` | Generate provenance statement (requires OIDC, not supported by classic yarn) |
|
|
471
|
+
| `--help`, `-h` | Show help |
|
|
472
|
+
|
|
473
|
+
#### Publishability filter
|
|
474
|
+
|
|
475
|
+
`publish` operates only on workspaces where `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`, get a `CHANGELOG.md` entry, and get a GitHub Release via `release-kit create-github-release` — only the registry publish step is skipped. Other commands ignore this filter and operate on private workspaces unchanged.
|
|
476
|
+
|
|
477
|
+
The filter behaves differently depending on whether `--tags` is provided:
|
|
478
|
+
|
|
479
|
+
- **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.
|
|
480
|
+
- **With `--tags`** (explicit naming): if any named tag points at an unpublishable workspace, `release-kit publish` exits 1 with one error line per unpublishable tag, citing `package.json#private`. Explicit naming surfaces the contradiction rather than silently dropping the tag.
|
|
481
|
+
|
|
482
|
+
Example output when an explicit tag is unpublishable:
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
Error: basic-v1.0.0 (packages/basic) cannot be published: package.json#private is true.
|
|
486
|
+
```
|
|
487
|
+
|
|
407
488
|
### `release-kit create-github-release`
|
|
408
489
|
|
|
409
490
|
Create GitHub Releases from `changelog.json` for tags on HEAD. Independent of `npm publish`: invoking this command creates Releases regardless of whether the matching package was published.
|
|
@@ -447,6 +528,28 @@ Scaffolded files:
|
|
|
447
528
|
- `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
|
|
448
529
|
- `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
|
|
449
530
|
|
|
531
|
+
### `release-kit work-types`
|
|
532
|
+
|
|
533
|
+
Manage the canonical work-types taxonomy used by changelog and release-notes generation.
|
|
534
|
+
|
|
535
|
+
| Subcommand | Description |
|
|
536
|
+
| ---------- | ----------------------------------------------------------------------------------- |
|
|
537
|
+
| `check` | Compare the local `work-types.json` against the upstream codeassembly canonical |
|
|
538
|
+
| `sync` | Overwrite the local `work-types.json` with the upstream contents (after validation) |
|
|
539
|
+
|
|
540
|
+
`check` exit codes:
|
|
541
|
+
|
|
542
|
+
| Code | Meaning |
|
|
543
|
+
| ---- | --------------------------------------------------------------------------------- |
|
|
544
|
+
| `0` | Match (or upstream missing — transitional warning printed) |
|
|
545
|
+
| `1` | Drift detected |
|
|
546
|
+
| `2` | Network error or non-OK HTTP response |
|
|
547
|
+
| `3` | Schema mismatch (upstream JSON does not parse or fails the top-level shape check) |
|
|
548
|
+
|
|
549
|
+
The check is non-blocking initially: until codeassembly publishes its `work-types.json`, the upstream URL returns 404 and `check` exits 0 with a warning. CI flip to a blocking check is tracked as a follow-up once the upstream ships.
|
|
550
|
+
|
|
551
|
+
These commands are also exposed as `nmr work-types:check` / `nmr work-types:sync` from any package directory.
|
|
552
|
+
|
|
450
553
|
### `release-kit sync-labels`
|
|
451
554
|
|
|
452
555
|
Manage GitHub label definitions via config-driven YAML files.
|
package/cliff.toml.template
CHANGED
|
@@ -10,6 +10,13 @@
|
|
|
10
10
|
# Only ticketed commits (leading #NN, PROJ-NN, or ##) are included.
|
|
11
11
|
# Unticketed maintenance commits (deps upgrades, tooling tweaks) are skipped.
|
|
12
12
|
# Use ## as a synthetic ticket prefix for ad-hoc commits that belong in the changelog.
|
|
13
|
+
#
|
|
14
|
+
# Section order: each `group` value carries a hidden `<!-- NN -->` HTML-comment
|
|
15
|
+
# prefix encoding the canonical row position. tera's `group_by` filter sorts
|
|
16
|
+
# groups lexicographically by string, and the body template's `striptags`
|
|
17
|
+
# filter erases the comment from the rendered heading. Numbering is per
|
|
18
|
+
# unique group (not per parser entry) — all parsers routing to the same group
|
|
19
|
+
# share the same prefix.
|
|
13
20
|
|
|
14
21
|
[changelog]
|
|
15
22
|
header = """
|
|
@@ -61,24 +68,26 @@ commit_preprocessors = []
|
|
|
61
68
|
commit_parsers = [
|
|
62
69
|
{ message = "^release:", skip = true },
|
|
63
70
|
{ message = "^Merge", skip = true },
|
|
64
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
65
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
66
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
67
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?deprecate(!)?:", group = "Deprecated" },
|
|
68
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
69
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
70
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
71
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
72
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?
|
|
71
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?feat(!)?:", group = "<!-- 01 -->🎉 Features" },
|
|
72
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?feature(!)?:", group = "<!-- 01 -->🎉 Features" },
|
|
73
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?drop(!)?:", group = "<!-- 02 -->🪦 Removed" },
|
|
74
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?deprecate(!)?:", group = "<!-- 03 -->🗑️ Deprecated" },
|
|
75
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?fix(!)?:", group = "<!-- 04 -->🐛 Bug fixes" },
|
|
76
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?bugfix(!)?:", group = "<!-- 04 -->🐛 Bug fixes" },
|
|
77
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?sec(!)?:", group = "<!-- 05 -->🔒 Security" },
|
|
78
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?security(!)?:", group = "<!-- 05 -->🔒 Security" },
|
|
79
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?perf(!)?:", group = "<!-- 06 -->⚡ Performance" },
|
|
80
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?performance(!)?:", group = "<!-- 06 -->⚡ Performance" },
|
|
81
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?internal(!)?:", group = "<!-- 07 -->🏗️ Internal features" },
|
|
82
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?utility(!)?:", group = "<!-- 07 -->🏗️ Internal features" },
|
|
83
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?refactor(!)?:", group = "<!-- 08 -->♻️ Refactoring" },
|
|
84
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tests?(!)?:", group = "<!-- 09 -->🧪 Tests" },
|
|
85
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tooling(!)?:", group = "<!-- 10 -->⚙️ Tooling" },
|
|
86
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?ci(!)?:", group = "<!-- 11 -->👷 CI" },
|
|
87
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?deps?(!)?:", group = "<!-- 12 -->📦 Dependencies" },
|
|
88
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?ai(!)?:", group = "<!-- 13 -->🤖 Agentic support" },
|
|
89
|
+
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?docs?(!)?:", group = "<!-- 14 -->📚 Documentation" },
|
|
73
90
|
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?fmt(!)?:", skip = true },
|
|
74
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?internal(!)?:", group = "Internal" },
|
|
75
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?perf(!)?:", group = "Performance" },
|
|
76
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?performance(!)?:", group = "Performance" },
|
|
77
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?refactor(!)?:", group = "Refactoring" },
|
|
78
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?sec(!)?:", group = "Security" },
|
|
79
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?security(!)?:", group = "Security" },
|
|
80
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tests?(!)?:", group = "Tests" },
|
|
81
|
-
{ message = "^(\\#\\d+([.\\-]\\d+)?|\\#\\#|[A-Z]+-\\d+)\\s+([\\w-]+\\|)?tooling(!)?:", group = "Tooling" },
|
|
82
91
|
# Skip unticketed commits (maintenance, deps, initial scaffolding, etc.)
|
|
83
92
|
{ message = ".*", skip = true },
|
|
84
93
|
]
|
package/dist/esm/.cache
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
64d26b06f4a6e205faa07a507fd64938899e4d635dd02b333016caf1d937a14e
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
|
|
2
|
+
import { parseArgs, readPackageVersion, translateParseError } from "@williamthorsen/nmr-core";
|
|
3
|
+
import { checkWorkTypesDrift } from "../checkWorkTypesDrift.js";
|
|
3
4
|
import { commitCommand } from "../commitCommand.js";
|
|
4
5
|
import { createGithubReleaseCommand } from "../createGithubReleaseCommand.js";
|
|
5
6
|
import { initCommand } from "../init/initCommand.js";
|
|
@@ -10,8 +11,9 @@ import { showTagPrefixesCommand } from "../showTagPrefixesCommand.js";
|
|
|
10
11
|
import { generateCommand } from "../sync-labels/generateCommand.js";
|
|
11
12
|
import { syncLabelsInitCommand } from "../sync-labels/initCommand.js";
|
|
12
13
|
import { syncLabelsCommand } from "../sync-labels/syncCommand.js";
|
|
14
|
+
import { syncWorkTypes } from "../syncWorkTypes.js";
|
|
13
15
|
import { tagCommand } from "../tagCommand.js";
|
|
14
|
-
|
|
16
|
+
const VERSION = readPackageVersion(import.meta.url);
|
|
15
17
|
function showUsage() {
|
|
16
18
|
console.info(`
|
|
17
19
|
Usage: release-kit <command> [options]
|
|
@@ -26,6 +28,7 @@ Commands:
|
|
|
26
28
|
show-tag-prefixes Show derived and declared legacy tag prefixes per workspace
|
|
27
29
|
init Initialize release-kit in the current repository
|
|
28
30
|
sync-labels Manage GitHub label synchronization
|
|
31
|
+
work-types Check for or sync work-type taxonomy drift against the upstream canonical
|
|
29
32
|
|
|
30
33
|
Options:
|
|
31
34
|
--dry-run Preview changes without writing files
|
|
@@ -171,6 +174,49 @@ legacy tag prefixes. Surfaces any release-shaped tags whose prefix is neither a
|
|
|
171
174
|
derived prefix nor declared in \`legacyIdentities\`, with a copy-pasteable
|
|
172
175
|
config snippet.
|
|
173
176
|
|
|
177
|
+
Options:
|
|
178
|
+
--help, -h Show this help message
|
|
179
|
+
`);
|
|
180
|
+
}
|
|
181
|
+
function showWorkTypesHelp() {
|
|
182
|
+
console.info(`
|
|
183
|
+
Usage: release-kit work-types <subcommand>
|
|
184
|
+
|
|
185
|
+
Manage the canonical work-types taxonomy used by changelog and release-notes generation.
|
|
186
|
+
|
|
187
|
+
Subcommands:
|
|
188
|
+
check Compare the local work-types.json against the upstream codeassembly canonical
|
|
189
|
+
sync Overwrite the local work-types.json with the upstream contents
|
|
190
|
+
|
|
191
|
+
Exit codes (check):
|
|
192
|
+
0 Match (or upstream missing \u2014 transitional warning printed)
|
|
193
|
+
1 Drift detected
|
|
194
|
+
2 Network error
|
|
195
|
+
3 Schema mismatch
|
|
196
|
+
|
|
197
|
+
Options:
|
|
198
|
+
--help, -h Show this help message
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
function showWorkTypesCheckHelp() {
|
|
202
|
+
console.info(`
|
|
203
|
+
Usage: release-kit work-types check
|
|
204
|
+
|
|
205
|
+
Compare the local work-types.json against the upstream codeassembly canonical and report
|
|
206
|
+
drift. Exit 0 on match, 1 on drift, 0 + warning when upstream is missing (transitional),
|
|
207
|
+
2 on network error, 3 on schema mismatch.
|
|
208
|
+
|
|
209
|
+
Options:
|
|
210
|
+
--help, -h Show this help message
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
function showWorkTypesSyncHelp() {
|
|
214
|
+
console.info(`
|
|
215
|
+
Usage: release-kit work-types sync
|
|
216
|
+
|
|
217
|
+
Fetch the upstream work-types.json, validate its top-level shape, and overwrite the local
|
|
218
|
+
copy with the upstream content (formatted with 2-space indent + trailing newline).
|
|
219
|
+
|
|
174
220
|
Options:
|
|
175
221
|
--help, -h Show this help message
|
|
176
222
|
`);
|
|
@@ -179,7 +225,9 @@ function showPublishHelp() {
|
|
|
179
225
|
console.info(`
|
|
180
226
|
Usage: release-kit publish [options]
|
|
181
227
|
|
|
182
|
-
Publish packages that have release tags on HEAD.
|
|
228
|
+
Publish packages that have release tags on HEAD. Operates only on workspaces where
|
|
229
|
+
package.json#private is absent or false. Without --tags, unpublishable workspaces are
|
|
230
|
+
silently filtered out. With --tags, naming an unpublishable tag is an error.
|
|
183
231
|
|
|
184
232
|
Options:
|
|
185
233
|
--dry-run Preview without publishing
|
|
@@ -341,6 +389,51 @@ if (command === "sync-labels") {
|
|
|
341
389
|
showSyncLabelsHelp();
|
|
342
390
|
process.exit(1);
|
|
343
391
|
}
|
|
392
|
+
if (command === "work-types") {
|
|
393
|
+
const subcommand = flags[0];
|
|
394
|
+
const subflags = flags.slice(1);
|
|
395
|
+
if (subcommand === "--help" || subcommand === "-h" || subcommand === void 0) {
|
|
396
|
+
showWorkTypesHelp();
|
|
397
|
+
process.exit(0);
|
|
398
|
+
}
|
|
399
|
+
if (subcommand === "check") {
|
|
400
|
+
if (subflags.some((f) => f === "--help" || f === "-h")) {
|
|
401
|
+
showWorkTypesCheckHelp();
|
|
402
|
+
process.exit(0);
|
|
403
|
+
}
|
|
404
|
+
if (subflags.length > 0) {
|
|
405
|
+
console.error(`Error: Unknown option: ${subflags[0]}`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
const result = await checkWorkTypesDrift();
|
|
409
|
+
if (result.exitCode === 0) {
|
|
410
|
+
console.info(result.message);
|
|
411
|
+
} else {
|
|
412
|
+
console.error(result.message);
|
|
413
|
+
}
|
|
414
|
+
process.exit(result.exitCode);
|
|
415
|
+
}
|
|
416
|
+
if (subcommand === "sync") {
|
|
417
|
+
if (subflags.some((f) => f === "--help" || f === "-h")) {
|
|
418
|
+
showWorkTypesSyncHelp();
|
|
419
|
+
process.exit(0);
|
|
420
|
+
}
|
|
421
|
+
if (subflags.length > 0) {
|
|
422
|
+
console.error(`Error: Unknown option: ${subflags[0]}`);
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
const result = await syncWorkTypes();
|
|
426
|
+
if (result.exitCode === 0) {
|
|
427
|
+
console.info(result.message);
|
|
428
|
+
} else {
|
|
429
|
+
console.error(result.message);
|
|
430
|
+
}
|
|
431
|
+
process.exit(result.exitCode);
|
|
432
|
+
}
|
|
433
|
+
console.error(`Error: Unknown subcommand: ${subcommand}`);
|
|
434
|
+
showWorkTypesHelp();
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
344
437
|
console.error(`Error: Unknown command: ${command}`);
|
|
345
438
|
showUsage();
|
|
346
439
|
process.exit(1);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { GenerateChangelogOptions } from './generateChangelogs.ts';
|
|
2
2
|
import type { ChangelogEntry, ReleaseConfig } from './types.ts';
|
|
3
|
+
export declare function stripGroupDecorations(group: string): string;
|
|
3
4
|
export declare function buildChangelogEntries(config: Pick<ReleaseConfig, 'cliffConfigPath' | 'changelogJson'>, tag: string, options?: GenerateChangelogOptions): ChangelogEntry[];
|
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
1
|
import { extractVersion } from "./changelogJsonUtils.js";
|
|
2
|
+
import { DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
3
|
+
import { COMMIT_PREPROCESSOR_PATTERNS } from "./parseCommitMessage.js";
|
|
6
4
|
import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
|
|
5
|
+
import { runGitCliff } from "./runGitCliff.js";
|
|
6
|
+
import { stripEmojiPrefix } from "./stripEmojiPrefix.js";
|
|
7
7
|
import { isRecord, isUnknownArray } from "./typeGuards.js";
|
|
8
|
+
const HTML_COMMENT_PREFIX_PATTERN = /^<!--[^>]*-->/;
|
|
9
|
+
const CANONICAL_SECTION_ORDER = new Map(
|
|
10
|
+
Object.values(DEFAULT_WORK_TYPES).map((config, index) => [stripGroupDecorations(config.header), index])
|
|
11
|
+
);
|
|
12
|
+
function canonicalSectionPriority(title) {
|
|
13
|
+
const index = CANONICAL_SECTION_ORDER.get(stripGroupDecorations(title));
|
|
14
|
+
return index ?? Number.POSITIVE_INFINITY;
|
|
15
|
+
}
|
|
16
|
+
function stripGroupDecorations(group) {
|
|
17
|
+
return stripEmojiPrefix(group.replace(HTML_COMMENT_PREFIX_PATTERN, ""));
|
|
18
|
+
}
|
|
8
19
|
function buildChangelogEntries(config, tag, options) {
|
|
9
20
|
const resolvedConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
|
|
10
|
-
|
|
11
|
-
let tempDir;
|
|
12
|
-
if (resolvedConfigPath.endsWith(".template")) {
|
|
13
|
-
tempDir = mkdtempSync(join(tmpdir(), "cliff-"));
|
|
14
|
-
cliffConfigPath = join(tempDir, "cliff.toml");
|
|
15
|
-
copyFileSync(resolvedConfigPath, cliffConfigPath);
|
|
16
|
-
}
|
|
17
|
-
const args = ["--config", cliffConfigPath, "--context", "--tag", tag];
|
|
21
|
+
const cliffArgs = ["--context", "--tag", tag];
|
|
18
22
|
if (options?.tagPattern !== void 0) {
|
|
19
|
-
|
|
23
|
+
cliffArgs.push("--tag-pattern", options.tagPattern);
|
|
20
24
|
}
|
|
21
25
|
for (const includePath of options?.includePaths ?? []) {
|
|
22
|
-
|
|
26
|
+
cliffArgs.push("--include-path", includePath);
|
|
23
27
|
}
|
|
24
28
|
try {
|
|
25
|
-
const contextJson =
|
|
26
|
-
encoding: "utf8",
|
|
27
|
-
stdio: ["pipe", "pipe", "inherit"]
|
|
28
|
-
});
|
|
29
|
+
const contextJson = runGitCliff(resolvedConfigPath, cliffArgs, ["pipe", "pipe", "inherit"]);
|
|
29
30
|
const releases = parseCliffContext(contextJson);
|
|
30
31
|
const devOnlySections = new Set(config.changelogJson.devOnlySections);
|
|
31
32
|
return transformReleases(releases, devOnlySections);
|
|
@@ -33,10 +34,6 @@ function buildChangelogEntries(config, tag, options) {
|
|
|
33
34
|
throw new Error(
|
|
34
35
|
`Failed to build changelog entries for tag ${tag}: ${error instanceof Error ? error.message : String(error)}`
|
|
35
36
|
);
|
|
36
|
-
} finally {
|
|
37
|
-
if (tempDir !== void 0) {
|
|
38
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
-
}
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
function parseCliffContext(json) {
|
|
@@ -76,6 +73,7 @@ function toCliffContextCommit(value) {
|
|
|
76
73
|
}
|
|
77
74
|
function transformReleases(releases, devOnlySections) {
|
|
78
75
|
const entries = [];
|
|
76
|
+
const devOnlyNormalised = new Set([...devOnlySections].map(stripGroupDecorations));
|
|
79
77
|
for (const release of releases) {
|
|
80
78
|
if (release.version === void 0) {
|
|
81
79
|
continue;
|
|
@@ -84,9 +82,10 @@ function transformReleases(releases, devOnlySections) {
|
|
|
84
82
|
const date = release.timestamp !== void 0 ? new Date(release.timestamp * 1e3).toISOString().slice(0, 10) : "unreleased";
|
|
85
83
|
const sectionMap = /* @__PURE__ */ new Map();
|
|
86
84
|
for (const commit of release.commits ?? []) {
|
|
87
|
-
const group = commit.group ?? "Other";
|
|
85
|
+
const group = stripCommentPrefix(commit.group ?? "Other");
|
|
88
86
|
const description = extractDescription(commit.message);
|
|
89
87
|
const body = extractBody(commit.message);
|
|
88
|
+
const breaking = subjectHasBreakingMarker(commit.message);
|
|
90
89
|
let items = sectionMap.get(group);
|
|
91
90
|
if (items === void 0) {
|
|
92
91
|
items = [];
|
|
@@ -96,6 +95,9 @@ function transformReleases(releases, devOnlySections) {
|
|
|
96
95
|
if (body !== void 0) {
|
|
97
96
|
item.body = body;
|
|
98
97
|
}
|
|
98
|
+
if (breaking) {
|
|
99
|
+
item.breaking = true;
|
|
100
|
+
}
|
|
99
101
|
items.push(item);
|
|
100
102
|
}
|
|
101
103
|
const sections = [];
|
|
@@ -105,16 +107,27 @@ function transformReleases(releases, devOnlySections) {
|
|
|
105
107
|
}
|
|
106
108
|
sections.push({
|
|
107
109
|
title,
|
|
108
|
-
audience:
|
|
110
|
+
audience: devOnlyNormalised.has(stripGroupDecorations(title)) ? "dev" : "all",
|
|
109
111
|
items
|
|
110
112
|
});
|
|
111
113
|
}
|
|
114
|
+
sections.sort((a, b) => canonicalSectionPriority(a.title) - canonicalSectionPriority(b.title));
|
|
112
115
|
if (sections.length > 0) {
|
|
113
116
|
entries.push({ version, date, sections });
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
return entries;
|
|
117
120
|
}
|
|
121
|
+
function stripCommentPrefix(group) {
|
|
122
|
+
return group.replace(HTML_COMMENT_PREFIX_PATTERN, "");
|
|
123
|
+
}
|
|
124
|
+
function subjectHasBreakingMarker(message) {
|
|
125
|
+
let subject = message.split("\n", 1)[0] ?? "";
|
|
126
|
+
for (const pattern of COMMIT_PREPROCESSOR_PATTERNS) {
|
|
127
|
+
subject = subject.replace(pattern, "");
|
|
128
|
+
}
|
|
129
|
+
return /^(?:[^|]+\|)?\w+(?:\([^)]+\))?!:/.test(subject);
|
|
130
|
+
}
|
|
118
131
|
function extractDescription(message) {
|
|
119
132
|
const firstLine = message.split("\n")[0] ?? message;
|
|
120
133
|
const afterColon = firstLine.split(": ").slice(1).join(": ");
|
|
@@ -155,5 +168,6 @@ function extractBody(message) {
|
|
|
155
168
|
return lines.slice(start, end).join("\n").trim();
|
|
156
169
|
}
|
|
157
170
|
export {
|
|
158
|
-
buildChangelogEntries
|
|
171
|
+
buildChangelogEntries,
|
|
172
|
+
stripGroupDecorations
|
|
159
173
|
};
|