pm-changelog 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/LICENSE +21 -0
- package/README.md +318 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +310 -0
- package/dist/cli.js.map +1 -0
- package/dist/extension.d.ts +7 -0
- package/dist/extension.d.ts.map +1 -0
- package/dist/extension.js +101 -0
- package/dist/extension.js.map +1 -0
- package/dist/generator.d.ts +8 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +353 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +71 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/manifest.json +18 -0
- package/package.json +106 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased - 2026-05-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Regenerate release changelog from pm items (pmc-a6qg)
|
|
8
|
+
- Harden npm package metadata and CI release gates (pmc-otpe)
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- Audit git history for private data exposure (pmc-91po)
|
|
13
|
+
|
|
14
|
+
### Other
|
|
15
|
+
|
|
16
|
+
- Final release verification and npm publication audit (pmc-jk1a)
|
|
17
|
+
- Release pm-changelog 0.1.0 as a production-ready pm package (pmc-ysps)
|
|
18
|
+
- Document pm governance for the package lifecycle (pmc-xl68)
|
|
19
|
+
- Align GitHub repository settings for public package release (pmc-w1sp)
|
|
20
|
+
- Verify pm-changelog in a clean temporary project (pmc-800h)
|
|
21
|
+
|
|
22
|
+
## 0.1.0 - 2026-05-17
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Initial `pm-changelog` CLI for generating `CHANGELOG.md` from pm item JSON or `pm list-all --json`.
|
|
27
|
+
- Programmatic APIs for creating, merging, reading, and writing changelogs from Node.js scripts and CI runners.
|
|
28
|
+
- Package metadata for `pm install github.com/unbraind/pm-changelog --project`, `pm install npm:pm-changelog --project`, and catalog discovery.
|
|
29
|
+
- `--release-version` for the pm extension command so release headings do not collide with the global `pm --version` flag.
|
|
30
|
+
- Custom pm executable support via `--pm-bin` and `readPmItems({ pmBin })`.
|
|
31
|
+
- Programmatic runner wrapper support with `readPmItems({ pmArgs, cwd, env })`.
|
|
32
|
+
- pm-cli extension command: `pm changelog generate`.
|
|
33
|
+
- GitHub Actions support with JSON summaries, check mode, prepend mode, and `$GITHUB_OUTPUT` fields.
|
|
34
|
+
- Optional `$GITHUB_STEP_SUMMARY` publishing via `--github-step-summary`.
|
|
35
|
+
- GitHub Actions CI workflow for validating package builds and tests.
|
|
36
|
+
- Release and milestone grouping for projects that store release metadata on pm items.
|
|
37
|
+
- Tracked built runtime output so GitHub and local pm package installs work without a separate build step.
|
|
38
|
+
|
|
39
|
+
### Security
|
|
40
|
+
|
|
41
|
+
- Item URLs are omitted by default.
|
|
42
|
+
- Opt-in item links strip credentials, query strings, and fragments before markdown output.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 unbraind
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
# pm-changelog
|
|
2
|
+
|
|
3
|
+
Generate `CHANGELOG.md` from pm-cli items for local releases, GitHub Actions, runners, and scripts.
|
|
4
|
+
|
|
5
|
+
The package provides:
|
|
6
|
+
|
|
7
|
+
- `pm-changelog`, a standalone CLI that reads `pm list-all --json` or JSON input
|
|
8
|
+
- `createChangelog()`, `generateChangelog()`, `mergeChangelog()`, and `writeChangelog()` programmatic APIs
|
|
9
|
+
- `pm changelog generate`, a pm-cli extension command
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
Install as a pm package from GitHub:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pm install github.com/unbraind/pm-changelog --project
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then run the extension command:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pm changelog generate --mode prepend --output CHANGELOG.md
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Install the standalone CLI/API package from npm:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install --save-dev pm-changelog @unbrained/pm-cli
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use the local checkout for development:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Local checkout extension install:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pm install ./pm-changelog --project
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The repository tracks `dist/` intentionally so GitHub and local pm package installs work without a build step. npm packaging still runs `npm run build` through `prepack`.
|
|
45
|
+
|
|
46
|
+
Package metadata is declared in `package.json` under `pm`, and the runtime extension manifest is `manifest.json` at the package root.
|
|
47
|
+
|
|
48
|
+
Supported package-manager sources:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pm install github.com/unbraind/pm-changelog --project
|
|
52
|
+
pm install npm:pm-changelog --project
|
|
53
|
+
pm install ./pm-changelog --project
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Package Layout
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
pm-changelog/
|
|
60
|
+
manifest.json # pm extension manifest loaded by the package manager
|
|
61
|
+
package.json # npm metadata plus pm package catalog/install metadata
|
|
62
|
+
LICENSE # MIT license for npm and public repository consumers
|
|
63
|
+
dist/ # built CLI, API, and extension runtime tracked for pm installs
|
|
64
|
+
src/ # TypeScript source
|
|
65
|
+
test/ # node:test coverage for generator, CLI, and runner behavior
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Release Verification
|
|
69
|
+
|
|
70
|
+
Run the full local release gate before tagging or publishing:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npm run release:check
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The release gate type-checks the TypeScript source, runs the full test suite, audits production dependencies, verifies the npm package contents with a dry run, and checks that `CHANGELOG.md` is current.
|
|
77
|
+
|
|
78
|
+
## CLI
|
|
79
|
+
|
|
80
|
+
Generate `CHANGELOG.md` from the current pm project:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
pm-changelog
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Generate release notes for a CI release:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pm-changelog --pm-root . --version "$GITHUB_REF_NAME" --since 2026-05-01
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Create or update `CHANGELOG.md` while preserving older entries:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --output CHANGELOG.md
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
After building, the package also exposes npm scripts for projects that install it locally:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm run changelog -- --version "$GITHUB_REF_NAME"
|
|
102
|
+
npm run changelog:check -- --version "$GITHUB_REF_NAME"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Emit runner-readable metadata:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --json
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Fail CI if the committed changelog is stale without rewriting it:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --check
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Expose summary values to later GitHub Actions steps:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --json --github-output
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Append the generated changelog markdown to the GitHub Actions job summary:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pm-changelog --mode prepend --version "$GITHUB_REF_NAME" --github-step-summary
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Print markdown instead of writing a file:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pm-changelog --stdout --version 1.2.0
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Read JSON from a previous step:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pm list-all --json | pm-changelog --stdin --stdout
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use a pinned or wrapped pm executable in a runner:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pm-changelog --pm-bin ./node_modules/.bin/pm --mode prepend --version "$GITHUB_REF_NAME"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Pass runner-specific arguments and a working directory to wrapped pm commands:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
pm-changelog --pm-bin ./pm-wrapper --pm-arg --profile --pm-arg ci --pm-cwd "$GITHUB_WORKSPACE" --mode prepend
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Generate one section per `release` metadata value from pm items:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pm-changelog --group-by release --mode prepend --output CHANGELOG.md
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Useful options:
|
|
160
|
+
|
|
161
|
+
| Option | Default | Description |
|
|
162
|
+
|---|---:|---|
|
|
163
|
+
| `--output <file>` | `CHANGELOG.md` | Output path |
|
|
164
|
+
| `--stdout` | false | Print markdown instead of writing a file |
|
|
165
|
+
| `--input <file>` | - | Read pm JSON from a file |
|
|
166
|
+
| `--stdin` | false | Read pm JSON from stdin |
|
|
167
|
+
| `--pm-root <dir>` | - | Run `pm --path <dir> list-all --json` |
|
|
168
|
+
| `--pm-bin <file>` | `pm` | pm executable to run, useful for pinned local installs and runner wrappers |
|
|
169
|
+
| `--pm-arg <arg>` | - | Extra argument passed before `list-all --json`; repeat for multiple args |
|
|
170
|
+
| `--pm-cwd <dir>` | - | Working directory for running pm |
|
|
171
|
+
| `--version <version>` | `Unreleased` | Version heading |
|
|
172
|
+
| `--date <date>` | today | Release date |
|
|
173
|
+
| `--since <date>` | - | Include items changed on or after date |
|
|
174
|
+
| `--until <date>` | - | Include items changed on or before date |
|
|
175
|
+
| `--status <list>` | `closed` | Comma-separated statuses |
|
|
176
|
+
| `--group-by <mode>` | `version` | `version`, `release`, or `milestone` |
|
|
177
|
+
| `--mode <mode>` | `replace` | `replace` or `prepend` existing changelog |
|
|
178
|
+
| `--json` | false | Print JSON summary for automation |
|
|
179
|
+
| `--check` | false | Do not write; exit 1 if the output file would change |
|
|
180
|
+
| `--github-output` | false | Write `output`, `mode`, `action`, `changed`, `item_count`, and `bytes` to `$GITHUB_OUTPUT` |
|
|
181
|
+
| `--github-step-summary` | false | Append generated markdown to `$GITHUB_STEP_SUMMARY` |
|
|
182
|
+
| `--include-empty` | false | Emit an empty section when no items match |
|
|
183
|
+
| `--include-links` | false | Include item `url` values in generated entries |
|
|
184
|
+
|
|
185
|
+
## pm-cli command
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
pm changelog generate
|
|
189
|
+
pm changelog generate --release-version 1.2.0 --output CHANGELOG.md
|
|
190
|
+
pm changelog generate --stdout --group-by milestone
|
|
191
|
+
pm changelog generate --stdout --group-by release
|
|
192
|
+
pm changelog generate --mode prepend --release-version "$GITHUB_REF_NAME"
|
|
193
|
+
pm changelog generate --check --mode prepend --release-version "$GITHUB_REF_NAME"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
The pm extension command uses `--release-version` because `pm --version` is a global CLI flag. The standalone `pm-changelog` binary uses `--version`.
|
|
197
|
+
|
|
198
|
+
## Programmatic API
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
import { readPmItems, writeChangelog } from "pm-changelog";
|
|
202
|
+
|
|
203
|
+
const items = readPmItems({
|
|
204
|
+
pmRoot: process.cwd(),
|
|
205
|
+
pmBin: "./node_modules/.bin/pm",
|
|
206
|
+
});
|
|
207
|
+
const result = writeChangelog({
|
|
208
|
+
items,
|
|
209
|
+
output: "CHANGELOG.md",
|
|
210
|
+
mode: "prepend",
|
|
211
|
+
groupBy: "release",
|
|
212
|
+
since: process.env.CHANGELOG_SINCE,
|
|
213
|
+
includeLinks: false,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
console.log({
|
|
217
|
+
action: result.action,
|
|
218
|
+
changed: result.changed,
|
|
219
|
+
items: result.itemCount,
|
|
220
|
+
output: result.output,
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Use `version` when a runner is generating one release section from the current job context. Use `groupBy: "release"` or `--group-by release` when pm items already carry release metadata and a runner should rebuild multiple sections in one pass.
|
|
225
|
+
|
|
226
|
+
Item links are omitted by default so public CI jobs do not accidentally publish private tracker URLs. Pass `--include-links` or `includeLinks: true` when item URLs are safe to expose. When links are included, credentials, query strings, and fragments are stripped before markdown is emitted.
|
|
227
|
+
|
|
228
|
+
You can also pass items directly:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { generateChangelog } from "pm-changelog";
|
|
232
|
+
|
|
233
|
+
const markdown = generateChangelog({
|
|
234
|
+
version: "1.2.0",
|
|
235
|
+
items: [
|
|
236
|
+
{
|
|
237
|
+
id: "pm-123",
|
|
238
|
+
title: "Fix CSV import status handling",
|
|
239
|
+
status: "closed",
|
|
240
|
+
type: "Bug",
|
|
241
|
+
tags: ["fix"],
|
|
242
|
+
updated_at: "2026-05-17T09:00:00Z",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Runner wrappers can provide extra pm arguments, a working directory, and environment:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { readPmItems } from "pm-changelog";
|
|
252
|
+
|
|
253
|
+
const items = readPmItems({
|
|
254
|
+
pmBin: process.env.PM_BIN ?? "pm",
|
|
255
|
+
pmArgs: ["--profile", "ci"],
|
|
256
|
+
cwd: process.env.GITHUB_WORKSPACE,
|
|
257
|
+
env: process.env,
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Categorization
|
|
262
|
+
|
|
263
|
+
Items are grouped into Keep a Changelog-style sections using `type`, `tags`, and title keywords:
|
|
264
|
+
|
|
265
|
+
- `Added`: feature, feat, added, add, new
|
|
266
|
+
- `Changed`: change, refactor, update, improve
|
|
267
|
+
- `Fixed`: fix, bug, hotfix, regression
|
|
268
|
+
- `Removed`: removed, delete
|
|
269
|
+
- `Security`: security, CVE, vulnerability
|
|
270
|
+
- `Deprecated`: deprecated, deprecation
|
|
271
|
+
- `Other`: anything else
|
|
272
|
+
|
|
273
|
+
## GitHub Actions Example
|
|
274
|
+
|
|
275
|
+
```yaml
|
|
276
|
+
name: Changelog
|
|
277
|
+
|
|
278
|
+
on:
|
|
279
|
+
workflow_dispatch:
|
|
280
|
+
release:
|
|
281
|
+
types: [published]
|
|
282
|
+
|
|
283
|
+
jobs:
|
|
284
|
+
changelog:
|
|
285
|
+
runs-on: ubuntu-latest
|
|
286
|
+
permissions:
|
|
287
|
+
contents: write
|
|
288
|
+
steps:
|
|
289
|
+
- uses: actions/checkout@v4
|
|
290
|
+
- uses: actions/setup-node@v4
|
|
291
|
+
with:
|
|
292
|
+
node-version: 20
|
|
293
|
+
- run: npm ci
|
|
294
|
+
- run: npm run build
|
|
295
|
+
- name: Generate changelog
|
|
296
|
+
id: changelog
|
|
297
|
+
run: node dist/cli.js --mode prepend --version "${GITHUB_REF_NAME}" --output CHANGELOG.md --json --github-output --github-step-summary
|
|
298
|
+
- name: Commit changelog
|
|
299
|
+
if: steps.changelog.outputs.changed == 'true'
|
|
300
|
+
run: |
|
|
301
|
+
git config user.name "github-actions[bot]"
|
|
302
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
303
|
+
git add CHANGELOG.md
|
|
304
|
+
git commit -m "docs: update changelog"
|
|
305
|
+
git push
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## Build
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npm run build
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
TypeScript 5, ES2022 target, NodeNext module resolution.
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { stdin } from "node:process";
|
|
5
|
+
import { createChangelog, mergeChangelog, parsePmItemsJson, readPmItems, writeChangelog, } from "./generator.js";
|
|
6
|
+
async function main() {
|
|
7
|
+
const options = parseArgs(process.argv.slice(2));
|
|
8
|
+
const items = await loadItems(options);
|
|
9
|
+
const outputPath = resolve(options.output);
|
|
10
|
+
if (!options.stdout) {
|
|
11
|
+
const result = writeChangelog({
|
|
12
|
+
items,
|
|
13
|
+
output: outputPath,
|
|
14
|
+
title: options.title,
|
|
15
|
+
version: options.version,
|
|
16
|
+
date: options.date,
|
|
17
|
+
since: options.since,
|
|
18
|
+
until: options.until,
|
|
19
|
+
includeStatuses: options.statuses,
|
|
20
|
+
groupBy: options.groupBy,
|
|
21
|
+
includeEmpty: options.includeEmpty,
|
|
22
|
+
includeLinks: options.includeLinks,
|
|
23
|
+
mode: options.mode,
|
|
24
|
+
check: options.check,
|
|
25
|
+
});
|
|
26
|
+
const summary = buildSummary(options, result, outputPath);
|
|
27
|
+
if (options.githubOutput)
|
|
28
|
+
writeGitHubOutput(summary);
|
|
29
|
+
if (options.githubStepSummary)
|
|
30
|
+
writeGitHubStepSummary(result.markdown);
|
|
31
|
+
if (options.json) {
|
|
32
|
+
process.stdout.write(JSON.stringify(summary) + "\n");
|
|
33
|
+
}
|
|
34
|
+
else if (options.check) {
|
|
35
|
+
console.error(result.changed ? `Changelog is out of date: ${outputPath}` : `Changelog is up to date: ${outputPath}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error(`Wrote ${outputPath}`);
|
|
39
|
+
}
|
|
40
|
+
if (options.check && result.changed)
|
|
41
|
+
process.exit(1);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const generated = createChangelog({
|
|
45
|
+
items,
|
|
46
|
+
title: options.title,
|
|
47
|
+
version: options.version,
|
|
48
|
+
date: options.date,
|
|
49
|
+
since: options.since,
|
|
50
|
+
until: options.until,
|
|
51
|
+
includeStatuses: options.statuses,
|
|
52
|
+
groupBy: options.groupBy,
|
|
53
|
+
includeEmpty: options.includeEmpty,
|
|
54
|
+
includeLinks: options.includeLinks,
|
|
55
|
+
});
|
|
56
|
+
const existing = options.mode === "prepend" && existsSync(outputPath)
|
|
57
|
+
? readFileSync(outputPath, "utf-8")
|
|
58
|
+
: undefined;
|
|
59
|
+
const merged = options.mode === "prepend"
|
|
60
|
+
? mergeChangelog(existing, generated.markdown, { title: options.title })
|
|
61
|
+
: { markdown: generated.markdown, action: "replaced", changed: true };
|
|
62
|
+
if (options.stdout) {
|
|
63
|
+
if (options.json) {
|
|
64
|
+
const summary = buildSummary(options, {
|
|
65
|
+
output: outputPath,
|
|
66
|
+
markdown: merged.markdown,
|
|
67
|
+
action: merged.action,
|
|
68
|
+
changed: merged.changed,
|
|
69
|
+
itemCount: generated.itemCount,
|
|
70
|
+
bytes: Buffer.byteLength(merged.markdown, "utf-8"),
|
|
71
|
+
});
|
|
72
|
+
if (options.githubOutput)
|
|
73
|
+
writeGitHubOutput(summary);
|
|
74
|
+
if (options.githubStepSummary)
|
|
75
|
+
writeGitHubStepSummary(merged.markdown);
|
|
76
|
+
process.stdout.write(JSON.stringify(summary) + "\n");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (options.githubStepSummary)
|
|
80
|
+
writeGitHubStepSummary(merged.markdown);
|
|
81
|
+
process.stdout.write(merged.markdown);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function parseArgs(args) {
|
|
86
|
+
const options = {
|
|
87
|
+
output: "CHANGELOG.md",
|
|
88
|
+
stdout: false,
|
|
89
|
+
json: false,
|
|
90
|
+
stdin: false,
|
|
91
|
+
pmArgs: [],
|
|
92
|
+
groupBy: "version",
|
|
93
|
+
includeEmpty: false,
|
|
94
|
+
includeLinks: false,
|
|
95
|
+
mode: "replace",
|
|
96
|
+
check: false,
|
|
97
|
+
githubOutput: false,
|
|
98
|
+
githubStepSummary: false,
|
|
99
|
+
};
|
|
100
|
+
for (let i = 0; i < args.length; i++) {
|
|
101
|
+
const arg = args[i];
|
|
102
|
+
switch (arg) {
|
|
103
|
+
case "--help":
|
|
104
|
+
case "-h":
|
|
105
|
+
printHelp();
|
|
106
|
+
process.exit(0);
|
|
107
|
+
case "--output":
|
|
108
|
+
case "-o":
|
|
109
|
+
options.output = requireValue(args, ++i, arg);
|
|
110
|
+
break;
|
|
111
|
+
case "--stdout":
|
|
112
|
+
options.stdout = true;
|
|
113
|
+
break;
|
|
114
|
+
case "--json":
|
|
115
|
+
options.json = true;
|
|
116
|
+
break;
|
|
117
|
+
case "--check":
|
|
118
|
+
options.check = true;
|
|
119
|
+
break;
|
|
120
|
+
case "--github-output":
|
|
121
|
+
case "--set-output":
|
|
122
|
+
options.githubOutput = true;
|
|
123
|
+
break;
|
|
124
|
+
case "--github-step-summary":
|
|
125
|
+
options.githubStepSummary = true;
|
|
126
|
+
break;
|
|
127
|
+
case "--input":
|
|
128
|
+
case "-i":
|
|
129
|
+
options.input = requireValue(args, ++i, arg);
|
|
130
|
+
break;
|
|
131
|
+
case "--stdin":
|
|
132
|
+
options.stdin = true;
|
|
133
|
+
break;
|
|
134
|
+
case "--pm-root":
|
|
135
|
+
options.pmRoot = requireValue(args, ++i, arg);
|
|
136
|
+
break;
|
|
137
|
+
case "--pm-bin":
|
|
138
|
+
options.pmBin = requireValue(args, ++i, arg);
|
|
139
|
+
break;
|
|
140
|
+
case "--pm-arg":
|
|
141
|
+
options.pmArgs.push(requireAnyValue(args, ++i, arg));
|
|
142
|
+
break;
|
|
143
|
+
case "--pm-cwd":
|
|
144
|
+
options.pmCwd = requireValue(args, ++i, arg);
|
|
145
|
+
break;
|
|
146
|
+
case "--title":
|
|
147
|
+
options.title = requireValue(args, ++i, arg);
|
|
148
|
+
break;
|
|
149
|
+
case "--version":
|
|
150
|
+
options.version = requireValue(args, ++i, arg);
|
|
151
|
+
break;
|
|
152
|
+
case "--date":
|
|
153
|
+
options.date = requireValue(args, ++i, arg);
|
|
154
|
+
break;
|
|
155
|
+
case "--since":
|
|
156
|
+
options.since = requireValue(args, ++i, arg);
|
|
157
|
+
break;
|
|
158
|
+
case "--until":
|
|
159
|
+
options.until = requireValue(args, ++i, arg);
|
|
160
|
+
break;
|
|
161
|
+
case "--status":
|
|
162
|
+
case "--statuses":
|
|
163
|
+
options.statuses = requireValue(args, ++i, arg)
|
|
164
|
+
.split(",")
|
|
165
|
+
.map((status) => status.trim())
|
|
166
|
+
.filter(Boolean);
|
|
167
|
+
break;
|
|
168
|
+
case "--group-by":
|
|
169
|
+
options.groupBy = parseGroupBy(requireValue(args, ++i, arg));
|
|
170
|
+
break;
|
|
171
|
+
case "--mode":
|
|
172
|
+
options.mode = parseMode(requireValue(args, ++i, arg));
|
|
173
|
+
break;
|
|
174
|
+
case "--include-empty":
|
|
175
|
+
options.includeEmpty = true;
|
|
176
|
+
break;
|
|
177
|
+
case "--include-links":
|
|
178
|
+
options.includeLinks = true;
|
|
179
|
+
break;
|
|
180
|
+
case "--no-links":
|
|
181
|
+
options.includeLinks = false;
|
|
182
|
+
break;
|
|
183
|
+
default:
|
|
184
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return options;
|
|
188
|
+
}
|
|
189
|
+
async function loadItems(options) {
|
|
190
|
+
if (options.stdin) {
|
|
191
|
+
return parsePmItemsJson(await readStdin());
|
|
192
|
+
}
|
|
193
|
+
if (options.input) {
|
|
194
|
+
return parsePmItemsJson(readFileSync(resolve(options.input), "utf-8"));
|
|
195
|
+
}
|
|
196
|
+
return readPmItems({
|
|
197
|
+
pmRoot: options.pmRoot,
|
|
198
|
+
pmBin: options.pmBin,
|
|
199
|
+
pmArgs: options.pmArgs,
|
|
200
|
+
cwd: options.pmCwd ? resolve(options.pmCwd) : undefined,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function readStdin() {
|
|
204
|
+
return new Promise((resolvePromise, reject) => {
|
|
205
|
+
let data = "";
|
|
206
|
+
stdin.setEncoding("utf-8");
|
|
207
|
+
stdin.on("data", (chunk) => {
|
|
208
|
+
data += chunk;
|
|
209
|
+
});
|
|
210
|
+
stdin.on("end", () => resolvePromise(data));
|
|
211
|
+
stdin.on("error", reject);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function parseGroupBy(value) {
|
|
215
|
+
if (value === "version" || value === "release" || value === "milestone")
|
|
216
|
+
return value;
|
|
217
|
+
throw new Error("--group-by must be 'version', 'release', or 'milestone'");
|
|
218
|
+
}
|
|
219
|
+
function parseMode(value) {
|
|
220
|
+
if (value === "replace" || value === "prepend")
|
|
221
|
+
return value;
|
|
222
|
+
throw new Error("--mode must be 'replace' or 'prepend'");
|
|
223
|
+
}
|
|
224
|
+
function buildSummary(options, result, output = result.output) {
|
|
225
|
+
return {
|
|
226
|
+
output,
|
|
227
|
+
mode: options.mode,
|
|
228
|
+
action: result.action,
|
|
229
|
+
changed: result.changed,
|
|
230
|
+
itemCount: result.itemCount,
|
|
231
|
+
bytes: result.bytes,
|
|
232
|
+
check: options.check,
|
|
233
|
+
markdown: options.stdout ? result.markdown : undefined,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function writeGitHubOutput(summary) {
|
|
237
|
+
const githubOutput = process.env.GITHUB_OUTPUT;
|
|
238
|
+
if (!githubOutput) {
|
|
239
|
+
throw new Error("--github-output requires the GITHUB_OUTPUT environment variable");
|
|
240
|
+
}
|
|
241
|
+
const lines = [
|
|
242
|
+
`output=${String(summary.output ?? "")}`,
|
|
243
|
+
`mode=${String(summary.mode ?? "")}`,
|
|
244
|
+
`action=${String(summary.action ?? "")}`,
|
|
245
|
+
`changed=${String(summary.changed ?? "")}`,
|
|
246
|
+
`item_count=${String(summary.itemCount ?? "")}`,
|
|
247
|
+
`bytes=${String(summary.bytes ?? "")}`,
|
|
248
|
+
];
|
|
249
|
+
appendFileSync(githubOutput, `${lines.join("\n")}\n`, "utf-8");
|
|
250
|
+
}
|
|
251
|
+
function writeGitHubStepSummary(markdown) {
|
|
252
|
+
const githubStepSummary = process.env.GITHUB_STEP_SUMMARY;
|
|
253
|
+
if (!githubStepSummary) {
|
|
254
|
+
throw new Error("--github-step-summary requires the GITHUB_STEP_SUMMARY environment variable");
|
|
255
|
+
}
|
|
256
|
+
appendFileSync(githubStepSummary, `${markdown.trimEnd()}\n`, "utf-8");
|
|
257
|
+
}
|
|
258
|
+
function requireValue(args, index, flag) {
|
|
259
|
+
const value = args[index];
|
|
260
|
+
if (!value || value.startsWith("--")) {
|
|
261
|
+
throw new Error(`${flag} requires a value`);
|
|
262
|
+
}
|
|
263
|
+
return value;
|
|
264
|
+
}
|
|
265
|
+
function requireAnyValue(args, index, flag) {
|
|
266
|
+
const value = args[index];
|
|
267
|
+
if (!value) {
|
|
268
|
+
throw new Error(`${flag} requires a value`);
|
|
269
|
+
}
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
function printHelp() {
|
|
273
|
+
process.stdout.write(`pm-changelog
|
|
274
|
+
|
|
275
|
+
Generate CHANGELOG.md from pm-cli items.
|
|
276
|
+
|
|
277
|
+
Usage:
|
|
278
|
+
pm-changelog [options]
|
|
279
|
+
|
|
280
|
+
Options:
|
|
281
|
+
-o, --output <file> Write changelog to a file (default: CHANGELOG.md)
|
|
282
|
+
--stdout Print markdown instead of writing a file
|
|
283
|
+
--json Print a JSON summary for CI/runners
|
|
284
|
+
--check Do not write; exit 1 when output would change
|
|
285
|
+
--github-output Write summary fields to $GITHUB_OUTPUT
|
|
286
|
+
--github-step-summary Append generated markdown to $GITHUB_STEP_SUMMARY
|
|
287
|
+
-i, --input <file> Read pm JSON from a file instead of running pm
|
|
288
|
+
--stdin Read pm JSON from stdin
|
|
289
|
+
--pm-root <dir> pm project root for "pm --path <dir> list-all --json"
|
|
290
|
+
--pm-bin <file> pm executable to run (default: pm)
|
|
291
|
+
--pm-arg <arg> Extra argument passed before "list-all --json" (repeatable)
|
|
292
|
+
--pm-cwd <dir> Working directory for running pm
|
|
293
|
+
--title <text> Changelog title (default: Changelog)
|
|
294
|
+
--version <version> Version heading (default: Unreleased)
|
|
295
|
+
--date <date> Release date (default: today)
|
|
296
|
+
--since <date> Include items changed on or after this date
|
|
297
|
+
--until <date> Include items changed on or before this date
|
|
298
|
+
--status <list> Comma-separated statuses (default: closed)
|
|
299
|
+
--group-by <mode> version, release, or milestone (default: version)
|
|
300
|
+
--mode <mode> replace or prepend existing changelog (default: replace)
|
|
301
|
+
--include-empty Emit an empty release section when no items match
|
|
302
|
+
--include-links Include item URLs in generated entries (default: false)
|
|
303
|
+
`);
|
|
304
|
+
}
|
|
305
|
+
main().catch((error) => {
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
console.error(message);
|
|
308
|
+
process.exit(1);
|
|
309
|
+
});
|
|
310
|
+
//# sourceMappingURL=cli.js.map
|