├── .github ├── dependabot.yml └── workflows │ └── test-and-release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── auth.js ├── changelog-maker.js ├── collect-commit-labels.js ├── commit-to-list.js ├── commit-to-output.js ├── find-matching-prs.js ├── format.js ├── group-commits.js ├── groups.js ├── package.json ├── process-commits.js ├── reverts.js └── test.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: 'github-actions' 4 | directory: '/' 5 | schedule: 6 | interval: 'daily' 7 | commit-message: 8 | prefix: 'chore' 9 | include: 'scope' 10 | - package-ecosystem: 'npm' 11 | directory: '/' 12 | schedule: 13 | interval: 'daily' 14 | commit-message: 15 | prefix: 'chore' 16 | include: 'scope' 17 | -------------------------------------------------------------------------------- /.github/workflows/test-and-release.yml: -------------------------------------------------------------------------------- 1 | name: Test & Maybe Release 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | node: [18.x, 20.x, lts/*, current] 9 | # not quite windows ready, halp! os: [macos-latest, ubuntu-latest, windows-latest] 10 | os: [macos-latest, ubuntu-latest] 11 | runs-on: ${{ matrix.os }} 12 | steps: 13 | - name: Checkout Repository 14 | uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - name: Use Node.js ${{ matrix.node }} 18 | uses: actions/setup-node@v4.4.0 19 | with: 20 | node-version: ${{ matrix.node }} 21 | - name: Set up ghauth config (Ubuntu) 22 | run: | 23 | mkdir -p ~/.config/changelog-maker/ 24 | echo '{"user": "nodejs", "token": "'${{ secrets.GITHUB_TOKEN }}'"}' > ~/.config/changelog-maker/config.json 25 | if: startsWith(matrix.os, 'ubuntu') 26 | 27 | - name: Set up ghauth config (macOS) 28 | run: | 29 | mkdir -p ~/Library/Application\ Support/changelog-maker/ 30 | echo '{"user": "nodejs", "token": "'${{ secrets.GITHUB_TOKEN }}'"}' > ~/Library/Application\ Support/changelog-maker/config.json 31 | if: startsWith(matrix.os, 'macos') 32 | 33 | - name: Set up ghauth config (Windows) 34 | run: | 35 | mkdir "%LOCALAPPDATA%\changelog-maker\" 36 | echo {"user": "nodejs", "token": "${{ secrets.GITHUB_TOKEN }}"} > "%LOCALAPPDATA%\changelog-maker\config.json" 37 | shell: cmd 38 | if: startsWith(matrix.os, 'windows') 39 | - name: Install Dependencies 40 | run: | 41 | npm install --no-progress 42 | - name: Run tests 43 | run: | 44 | npm config set script-shell bash 45 | npm run test:ci 46 | release: 47 | name: Release 48 | permissions: 49 | contents: write 50 | needs: test 51 | runs-on: ubuntu-latest 52 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 53 | steps: 54 | - name: Checkout 55 | uses: actions/checkout@v4 56 | with: 57 | fetch-depth: 0 58 | - name: Setup Node.js 59 | uses: actions/setup-node@v4.4.0 60 | with: 61 | node-version: lts/* 62 | - name: Install dependencies 63 | run: | 64 | npm install --no-progress --no-package-lock --no-save 65 | - name: Build 66 | run: | 67 | npm run build 68 | - name: Install plugins 69 | run: | 70 | npm install \ 71 | @semantic-release/commit-analyzer \ 72 | conventional-changelog-conventionalcommits \ 73 | @semantic-release/release-notes-generator \ 74 | @semantic-release/npm \ 75 | @semantic-release/github \ 76 | @semantic-release/git \ 77 | @semantic-release/changelog \ 78 | --no-progress --no-package-lock --no-save 79 | - name: Release 80 | env: 81 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 82 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 83 | run: npx semantic-release 84 | 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .nyc_output/ 4 | .tap 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [4.4.2](https://github.com/nodejs/changelog-maker/compare/v4.4.1...v4.4.2) (2025-04-15) 2 | 3 | ### Trivial Changes 4 | 5 | * **deps:** bump actions/setup-node from 4.3.0 to 4.4.0 ([#181](https://github.com/nodejs/changelog-maker/issues/181)) ([9b57cb4](https://github.com/nodejs/changelog-maker/commit/9b57cb479d2402f146665856d1d766e539154fa1)) 6 | 7 | ## [4.4.1](https://github.com/nodejs/changelog-maker/compare/v4.4.0...v4.4.1) (2025-03-18) 8 | 9 | ### Trivial Changes 10 | 11 | * **deps:** bump actions/setup-node from 4.2.0 to 4.3.0 ([594f7b8](https://github.com/nodejs/changelog-maker/commit/594f7b8e8860a1899640b35efb456abf9d01f7b4)) 12 | 13 | ## [4.4.0](https://github.com/nodejs/changelog-maker/compare/v4.3.3...v4.4.0) (2025-02-19) 14 | 15 | ### Features 16 | 17 | * split commit-to-list to its own file ([#178](https://github.com/nodejs/changelog-maker/issues/178)) ([6d9fb9a](https://github.com/nodejs/changelog-maker/commit/6d9fb9a46258a8e8f42cddee05ef145908b690b3)) 18 | 19 | ## [4.3.3](https://github.com/nodejs/changelog-maker/compare/v4.3.2...v4.3.3) (2025-01-27) 20 | 21 | ### Trivial Changes 22 | 23 | * **deps:** bump actions/setup-node from 4.1.0 to 4.2.0 ([#175](https://github.com/nodejs/changelog-maker/issues/175)) ([04df5de](https://github.com/nodejs/changelog-maker/commit/04df5de4d28b098a7e785b8b6f102c2388423dc9)) 24 | 25 | ## [4.3.2](https://github.com/nodejs/changelog-maker/compare/v4.3.1...v4.3.2) (2025-01-16) 26 | 27 | ### Bug Fixes 28 | 29 | * add whitespace after cveId ([#174](https://github.com/nodejs/changelog-maker/issues/174)) ([fc15ea1](https://github.com/nodejs/changelog-maker/commit/fc15ea1b2c333ee7db0603484a6588dca23aea8e)) 30 | 31 | ## [4.3.1](https://github.com/nodejs/changelog-maker/compare/v4.3.0...v4.3.1) (2024-12-02) 32 | 33 | ### Trivial Changes 34 | 35 | * **deps-dev:** bump tap from 19.2.5 to 21.0.1 ([2a91332](https://github.com/nodejs/changelog-maker/commit/2a91332951968e859dadfacf47796cf9be5aec97)) 36 | 37 | ## [4.3.0](https://github.com/nodejs/changelog-maker/compare/v4.2.1...v4.3.0) (2024-12-02) 38 | 39 | ### Features 40 | 41 | * **conventionalcommits:** make group matching regexp cover CC style ([6d6ef98](https://github.com/nodejs/changelog-maker/commit/6d6ef98c144f4aee86c12d3b199852f3018534ee)) 42 | 43 | ### Trivial Changes 44 | 45 | * **cc:** make work ([1914a85](https://github.com/nodejs/changelog-maker/commit/1914a8503e82e115bbb6c1254c7ed2e899a21969)) 46 | * **deps:** bump actions/setup-node from 4.0.2 to 4.1.0 ([f3b5e33](https://github.com/nodejs/changelog-maker/commit/f3b5e33338fed91475e59f49df7cc128fa55634a)) 47 | 48 | ### Tests 49 | 50 | * **cc,yay:** add test case for conventional commits ([375e0b7](https://github.com/nodejs/changelog-maker/commit/375e0b7d489b1f02d1c7ff9a45e02f4e21fa56ad)) 51 | 52 | ## [4.2.1](https://github.com/nodejs/changelog-maker/compare/v4.2.0...v4.2.1) (2024-11-29) 53 | 54 | ### Trivial Changes 55 | 56 | * **deps-dev:** bump tap from 18.8.0 to 19.0.0 ([#165](https://github.com/nodejs/changelog-maker/issues/165)) ([fce4380](https://github.com/nodejs/changelog-maker/commit/fce438030c3826c20f2677150288fa81a4ec9692)) 57 | 58 | ## [4.2.0](https://github.com/nodejs/changelog-maker/compare/v4.1.1...v4.2.0) (2024-11-28) 59 | 60 | ### Features 61 | 62 | * add cveId support to commmit output ([43d428b](https://github.com/nodejs/changelog-maker/commit/43d428b3d206f950fda0e4209f984ab8b834893f)) 63 | 64 | ### Tests 65 | 66 | * add cveId tests ([f2f235b](https://github.com/nodejs/changelog-maker/commit/f2f235bb05a7240584054c5a8bb12410cb49f7b6)) 67 | 68 | ## [4.1.1](https://github.com/nodejs/changelog-maker/compare/v4.1.0...v4.1.1) (2024-02-07) 69 | 70 | 71 | ### Trivial Changes 72 | 73 | * **deps:** bump actions/setup-node from 4.0.1 to 4.0.2 ([#160](https://github.com/nodejs/changelog-maker/issues/160)) ([766aa1e](https://github.com/nodejs/changelog-maker/commit/766aa1ef9802397d3b5efd92f369ce6528270c35)) 74 | 75 | ## [4.1.0](https://github.com/nodejs/changelog-maker/compare/v4.0.1...v4.1.0) (2024-01-10) 76 | 77 | 78 | ### Features 79 | 80 | * allow commit message ONLY with --messageonly ([#156](https://github.com/nodejs/changelog-maker/issues/156)) ([e069500](https://github.com/nodejs/changelog-maker/commit/e06950091765b2b0b470d9cc5c5cc619819f2e6b)) 81 | 82 | ## [4.0.1](https://github.com/nodejs/changelog-maker/compare/v4.0.0...v4.0.1) (2024-01-01) 83 | 84 | 85 | ### Trivial Changes 86 | 87 | * **deps:** bump actions/setup-node from 4.0.0 to 4.0.1 ([b1603c8](https://github.com/nodejs/changelog-maker/commit/b1603c85da2c050ca905d74296368d30eca33fe2)) 88 | 89 | ## [4.0.0](https://github.com/nodejs/changelog-maker/compare/v3.2.7...v4.0.0) (2023-12-12) 90 | 91 | 92 | ### ⚠ BREAKING CHANGES 93 | 94 | * **deps:** bump ghauth from 5.0.2 to 6.0.0 (#157) 95 | 96 | ### Trivial Changes 97 | 98 | * **deps:** bump ghauth from 5.0.2 to 6.0.0 ([#157](https://github.com/nodejs/changelog-maker/issues/157)) ([d75c5d2](https://github.com/nodejs/changelog-maker/commit/d75c5d21ddcb2e32b4acdab7bd529c13080ef816)) 99 | 100 | ## [3.2.7](https://github.com/nodejs/changelog-maker/compare/v3.2.6...v3.2.7) (2023-12-12) 101 | 102 | 103 | ### Trivial Changes 104 | 105 | * drop 16.x and add 20.x to CI ([cc0ecb8](https://github.com/nodejs/changelog-maker/commit/cc0ecb85bc75af438786e97196a560139045663a)) 106 | 107 | ## [3.2.6](https://github.com/nodejs/changelog-maker/compare/v3.2.5...v3.2.6) (2023-10-25) 108 | 109 | 110 | ### Trivial Changes 111 | 112 | * **deps:** bump actions/setup-node from 3.8.1 to 4.0.0 ([184e2bb](https://github.com/nodejs/changelog-maker/commit/184e2bb5d2644f414cc9b7b4d816b09fc2a4c4c0)) 113 | 114 | ## [3.2.5](https://github.com/nodejs/changelog-maker/compare/v3.2.4...v3.2.5) (2023-09-27) 115 | 116 | 117 | ### Bug Fixes 118 | 119 | * --allow-incomplete-coverage ([575a432](https://github.com/nodejs/changelog-maker/commit/575a432fa0889d03753e3666f942a0d1ace865ca)) 120 | * make test pass ([5e011e0](https://github.com/nodejs/changelog-maker/commit/5e011e0946deb507b9ca46e62dd40e5ee1b186ed)) 121 | 122 | 123 | ### Trivial Changes 124 | 125 | * **deps:** bump @octokit/graphql from 5.0.6 to 7.0.1 ([e0385f1](https://github.com/nodejs/changelog-maker/commit/e0385f16d18eeba0f10a7a2bbd54f210c3984f0d)) 126 | * **deps:** bump actions/checkout from 3 to 4 ([006c2d0](https://github.com/nodejs/changelog-maker/commit/006c2d04d4186287616537f509d702da9187894a)) 127 | * **deps:** bump actions/setup-node from 3.6.0 to 3.8.0 ([2e8bce3](https://github.com/nodejs/changelog-maker/commit/2e8bce3687c4f1369d5c1bb6adfbee6bb1bd2f51)) 128 | * **deps:** bump actions/setup-node from 3.8.0 to 3.8.1 ([46deecf](https://github.com/nodejs/changelog-maker/commit/46deecf379e56fadf0f0e8e22cdf77d0a115d7e1)) 129 | * update dependencies ([7881e1b](https://github.com/nodejs/changelog-maker/commit/7881e1bc0639ecc7e1cdd934bc24373542a0971a)) 130 | 131 | ## [3.2.4](https://github.com/nodejs/changelog-maker/compare/v3.2.3...v3.2.4) (2023-05-15) 132 | 133 | 134 | ### Trivial Changes 135 | 136 | * **deps:** bump remark-preset-lint-node from 3.4.0 to 4.0.0 ([7eae6ff](https://github.com/nodejs/changelog-maker/commit/7eae6ffe93ae346f8a017b5328c7d263843eeb17)) 137 | 138 | ## [3.2.3](https://github.com/nodejs/changelog-maker/compare/v3.2.2...v3.2.3) (2023-04-10) 139 | 140 | 141 | ### Bug Fixes 142 | 143 | * update releaser action ([027c606](https://github.com/nodejs/changelog-maker/commit/027c606c61951769a9d8de17abb1bc1bd5780282)) 144 | 145 | ## [3.2.2](https://github.com/nodejs/changelog-maker/compare/v3.2.1...v3.2.2) (2023-04-06) 146 | 147 | 148 | ### Trivial Changes 149 | 150 | * **deps:** bump commit-stream from 1.1.0 to 2.0.1 ([8821209](https://github.com/nodejs/changelog-maker/commit/8821209da3dae7ac2c6cf46285c02dedca4493c0)) 151 | 152 | ## [3.2.1](https://github.com/nodejs/changelog-maker/compare/v3.2.0...v3.2.1) (2022-07-12) 153 | 154 | 155 | ### Trivial Changes 156 | 157 | * **deps:** bump @octokit/graphql from 4.8.0 to 5.0.0 ([#132](https://github.com/nodejs/changelog-maker/issues/132)) ([a44c88e](https://github.com/nodejs/changelog-maker/commit/a44c88e33a47a415f18c0187e124c65e4ced440e)) 158 | 159 | ## [3.2.0](https://github.com/nodejs/changelog-maker/compare/v3.1.0...v3.2.0) (2022-06-16) 160 | 161 | 162 | ### Features 163 | 164 | * --find-matching-prs for commits without PR-URL ([#130](https://github.com/nodejs/changelog-maker/issues/130)) ([0aa7a2e](https://github.com/nodejs/changelog-maker/commit/0aa7a2ef785bc7d81e0442d3e57ccfb98f8a7ae0)) 165 | 166 | 167 | ### Trivial Changes 168 | 169 | * update node versions in Actions matrix (& enable macos) ([#131](https://github.com/nodejs/changelog-maker/issues/131)) ([2a87620](https://github.com/nodejs/changelog-maker/commit/2a87620b359f0d819c9fd5f05bdd79f9402f4491)) 170 | 171 | ## [3.1.0](https://github.com/nodejs/changelog-maker/compare/v3.0.0...v3.1.0) (2022-05-05) 172 | 173 | 174 | ### Features 175 | 176 | * add collect-commit-labels to exports ([#127](https://github.com/nodejs/changelog-maker/issues/127)) ([ad41ebf](https://github.com/nodejs/changelog-maker/commit/ad41ebff35b451b15452c3acb3fd24f0bcb78e40)) 177 | 178 | 179 | ### Trivial Changes 180 | 181 | * **no-release:** bump actions/checkout from 2 to 3 ([#125](https://github.com/nodejs/changelog-maker/issues/125)) ([dfcda56](https://github.com/nodejs/changelog-maker/commit/dfcda569a90f083a65dc6076805e16889f69bc72)) 182 | * **no-release:** bump actions/setup-node from 2 to 3 ([#124](https://github.com/nodejs/changelog-maker/issues/124)) ([89a19e0](https://github.com/nodejs/changelog-maker/commit/89a19e02119266c5c39006c083bca1b5d2b54e33)) 183 | * **no-release:** bump standard from 16.0.4 to 17.0.0 ([#129](https://github.com/nodejs/changelog-maker/issues/129)) ([387fff4](https://github.com/nodejs/changelog-maker/commit/387fff4c54112f46b84911a63ec2a9d2b79ab239)) 184 | * **no-release:** bump tap from 15.2.3 to 16.0.0 ([#126](https://github.com/nodejs/changelog-maker/issues/126)) ([6665653](https://github.com/nodejs/changelog-maker/commit/666565365d85f05a9741b63cf3455a0747ff567f)) 185 | 186 | ## [3.0.0](https://github.com/nodejs/changelog-maker/compare/v2.8.0...v3.0.0) (2022-01-17) 187 | 188 | 189 | ### ⚠ BREAKING CHANGES 190 | 191 | * switch to ESM, fix output colourising, dedupe more code w/ branch-diff 192 | 193 | ### Features 194 | 195 | * switch to ESM, fix output colourising, dedupe more code w/ branch-diff ([cf51a0f](https://github.com/nodejs/changelog-maker/commit/cf51a0f0f74a6e222c8f150d699e2a210f6bb722)) 196 | 197 | 198 | ### Bug Fixes 199 | 200 | * add back --format=x arg option ([1462378](https://github.com/nodejs/changelog-maker/commit/146237837773b39196ae61399336e29365b4113d)) 201 | 202 | 203 | ### Trivial Changes 204 | 205 | * remove Node.js v12 support ([fcf0d85](https://github.com/nodejs/changelog-maker/commit/fcf0d854bf857b3b46c92f91c2f125b82813be5b)) 206 | 207 | ## [2.8.0](https://github.com/nodejs/changelog-maker/compare/v2.7.4...v2.8.0) (2022-01-17) 208 | 209 | 210 | ### Features 211 | 212 | * escape markdown characters in user provided strings ([#122](https://github.com/nodejs/changelog-maker/issues/122)) ([aa0234f](https://github.com/nodejs/changelog-maker/commit/aa0234f07cc7f9cfeb4c8a240e251943905a1518)) 213 | 214 | ## [2.7.4](https://github.com/nodejs/changelog-maker/compare/v2.7.3...v2.7.4) (2021-11-23) 215 | 216 | 217 | ### Trivial Changes 218 | 219 | * **deps:** remove package-lock.json ([#118](https://github.com/nodejs/changelog-maker/issues/118)) ([a059bc7](https://github.com/nodejs/changelog-maker/commit/a059bc7ca9b5e16b6678f4f419454aff76fd4b5b)) 220 | * **no-release:** bump tap from 15.0.10 to 15.1.1 ([#113](https://github.com/nodejs/changelog-maker/issues/113)) ([fcfc5c8](https://github.com/nodejs/changelog-maker/commit/fcfc5c80ded8f64ae4bf672828272287d34a2b27)) 221 | 222 | ## [2.7.3](https://github.com/nodejs/changelog-maker/compare/v2.7.2...v2.7.3) (2021-10-28) 223 | 224 | 225 | ### Trivial Changes 226 | 227 | * **deps:** bump async from 3.2.1 to 3.2.2 ([790bb0a](https://github.com/nodejs/changelog-maker/commit/790bb0abb04f7d2e5730407fa1795570169701ce)) 228 | 229 | ## [2.7.2](https://github.com/nodejs/changelog-maker/compare/v2.7.1...v2.7.2) (2021-10-25) 230 | 231 | 232 | ### Trivial Changes 233 | 234 | * **deps:** bump split2 from 4.0.0 to 4.1.0 ([f9676c0](https://github.com/nodejs/changelog-maker/commit/f9676c0edbfc887b03c95de7d98a6806c0a47019)) 235 | 236 | ## [2.7.1](https://github.com/nodejs/changelog-maker/compare/v2.7.0...v2.7.1) (2021-10-15) 237 | 238 | 239 | ### Trivial Changes 240 | 241 | * **deps:** bump split2 from 3.2.2 to 4.0.0 ([#109](https://github.com/nodejs/changelog-maker/issues/109)) ([365916f](https://github.com/nodejs/changelog-maker/commit/365916f06e50e51c7123464b3d4331759cd6672f)) 242 | 243 | ## [2.7.0](https://github.com/nodejs/changelog-maker/compare/v2.6.0...v2.7.0) (2021-10-14) 244 | 245 | 246 | ### Features 247 | 248 | * run Node.js markdown formatter on markdown output ([#98](https://github.com/nodejs/changelog-maker/issues/98)) ([26afb81](https://github.com/nodejs/changelog-maker/commit/26afb813e74c87cdf53b9cf384fb43982ad57eaa)) 249 | 250 | 251 | ### Bug Fixes 252 | 253 | * main instead of master ([c6aac22](https://github.com/nodejs/changelog-maker/commit/c6aac227f76183b5d7bbd909c4daec60ec651a9a)) 254 | * remove build step from release process ([ec2b9c0](https://github.com/nodejs/changelog-maker/commit/ec2b9c056ca6bceb3211dade19aec82d7207fc8a)) 255 | 256 | 257 | ### Trivial Changes 258 | 259 | * add auto-release workflow ([36ca9e5](https://github.com/nodejs/changelog-maker/commit/36ca9e50c5b52594713b4cd5e4c75e964e1e7c7b)) 260 | * add dependabot config ([5f16f7e](https://github.com/nodejs/changelog-maker/commit/5f16f7eb0b44c3287b07bfde963f46b477b79464)) 261 | * **deps:** bump remark-preset-lint-node from 3.2.0 to 3.3.0 ([ed6ff3b](https://github.com/nodejs/changelog-maker/commit/ed6ff3b7a07d4c1176568ef82a20b7f225953cef)) 262 | * **deps:** bump remark-stringify from 10.0.0 to 10.0.1 ([73c2046](https://github.com/nodejs/changelog-maker/commit/73c20463cca533da6e94240185c87516124fbcf0)) 263 | * escape opening bracket explicitly ([#97](https://github.com/nodejs/changelog-maker/issues/97)) ([64c1220](https://github.com/nodejs/changelog-maker/commit/64c12201b0ba7c80f8de725c5f20bbbce3723848)), closes [/github.com/nodejs/node/pull/40388#issuecomment-939321694](https://github.com/nodejs//github.com/nodejs/node/pull/40388/issues/issuecomment-939321694) 264 | * update package-lock.json ([#102](https://github.com/nodejs/changelog-maker/issues/102)) ([24112e0](https://github.com/nodejs/changelog-maker/commit/24112e0849849bfb9a85b1cf67e1d5309c508641)) 265 | * update standard (and path-parse) ([#96](https://github.com/nodejs/changelog-maker/issues/96)) ([66b6eef](https://github.com/nodejs/changelog-maker/commit/66b6eef843a0c6f05dc04999a8e4b2cb149fbb46)) 266 | * update test workflow ([#100](https://github.com/nodejs/changelog-maker/issues/100)) ([eaeb938](https://github.com/nodejs/changelog-maker/commit/eaeb938bd816b35224998c970ec182aea6f6e7f7)) 267 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright (c) 2015 Rod Vagg 5 | --------------------------- 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # changelog-maker [![Build Status](https://github.com/nodejs/changelog-maker/workflows/Tests/badge.svg)](https://github.com/nodejs/changelog-maker/actions?workflow=Tests) 2 | 3 | **A git log to CHANGELOG.md tool** 4 | 5 | [![npm](https://nodei.co/npm/changelog-maker.png?downloads=true&downloadRank=true)](https://nodei.co/npm/changelog-maker/) 6 | [![npm](https://nodei.co/npm-dl/changelog-maker.png?months=6&height=3)](https://nodei.co/npm/changelog-maker/) 7 | 8 | ## Eh? 9 | 10 | **changelog-maker** is a formalisation of the [Node.js](https://github.com/nodejs/node) CHANGELOG.md entry process but flexible enough to be used on other repositories. 11 | 12 | **changelog-maker** will look at the git log of the current directory, pulling entries since the last tag. Commits with just a version number in the summary are removed, as are commits prior to, and including summaries that say `working on ` (this is an io.js / Node ism). 13 | 14 | After collecting the list of commits, any that have `PR-URL: ` in them are looked up on GitHub and the labels of the pull request are collected, specifically looking for labels that start with `semver` (the assumption is that `semver-minor`, `semver-major` labels are used to indicate non-patch version bumps). 15 | 16 | Finally, the list is formatted as Markdown and printed to stdout. 17 | 18 | Each commit will come out something like this (on one line): 19 | 20 | ```markdown 21 | * [[`20f8e7f17a`](https://github.com/nodejs/io.js/commit/20f8e7f17a)] - 22 | **test**: remove flaky test functionality (Rod Vagg) 23 | [#812](https://github.com/nodejs/io.js/pull/812) 24 | ``` 25 | 26 | Note: 27 | 28 | * When running `changelog-maker` on the command-line, the default GitHub repo is computed from the `package.json` that exists on `cwd`, otherwise fallback to `nodejs/node`, you can change this by supplying the user/org as the first argument and project as the second. e.g `changelog-maker joyent node`. 29 | * Commit links will go to the assumed repo (default: nodejs/node) 30 | * If a commit summary starts with a word, followed by a `:`, this is treated as a special label and rendered in bold 31 | * Commits that have `semver*` labels on the pull request referred to in their `PR-URL` have those labels printed out at the start of the summary, in bold, upper cased. 32 | * Pull request URLs come from the `PR-URL` data, if it matches the assumed repo (default: nodejs/node) then just a `#` followed by the number, if another repo then a full `user/project#number`. 33 | 34 | When printing to a console some special behaviours are invoked: 35 | 36 | * Commits with a summary that starts with `doc:` are rendered in grey 37 | * Commits that have a `semver*` label on the pull request referred to in their `PR-URL` are rendered in bold green 38 | 39 | ## Install 40 | 41 | ```shell 42 | npm i changelog-maker -g 43 | ``` 44 | 45 | ## Usage 46 | 47 | **`changelog-maker [--plaintext|p] [--markdown|md] [--sha] [--group|-g] [--reverse] [--find-matching-prs] [--commit-url=] [--start-ref=] [--end-ref=] [github-user[, github-project]]`** 48 | 49 | `github-user` and `github-project` should point to the GitHub repository that can be used to find the `PR-URL` data if just an issue number is provided and will also impact how the PR-URL issue numbers are displayed 50 | 51 | * `--format`: dictates what formatting the output will have. Possible options are: `simple`, `markdown`, `plaintext`, `messageonly` and `sha`. The default is to print a `simple` output suitable for stdout. 52 | - `simple`: don't print full markdown output, good for console printing without the additional fluff. 53 | - `sha`: print only the 10-character truncated commit hashes. 54 | - `plaintext`: a very simple form, without commit details, implies `--group`. 55 | - `markdown`: a Markdown formatted from, with links and proper escaping. 56 | - `messageonly`: displays the commit message only, implies `--group` 57 | * `--sha`: same as `--format=sha`. 58 | * `--plaintext`: same as `--format=plaintext`. 59 | * `--markdown`: same as `--format=markdown`. 60 | * `--messageonly`: same as `--format=messageonly`. 61 | * `--group`: reorder commits so that they are listed in groups where the `xyz:` prefix of the commit message defines the group. Commits are listed in original order _within_ group. 62 | * `--reverse`: reverse the order of commits when printed, does not work with `--reverse` 63 | * `--commit-url`: pass in a url template which will be used to generate commit URLs for a repository not hosted in Github. `{ref}` is the placeholder that will be replaced with the commit, i.e. `--commit-url=https://gitlab.com/myUser/myRepo/commit/{ref}` 64 | * `--start-ref=`: use the given git `` as a starting point rather than the _last tag_. The `` can be anything commit-ish including a commit sha, tag, branch name. If you specify a `--start-ref` argument the commit log will not be pruned so that version commits and `working on ` commits are left in the list. 65 | * `--end-ref=`: use the given git `` as a end-point rather than the _now_. The `` can be anything commit-ish including a commit sha, tag, branch name. 66 | * `--filter-release`: exclude Node-style release commits from the list. e.g. "Working on v1.0.0" or "2015-10-21 Version 2.0.0" and also "npm version X" style commits containing _only_ an `x.y.z` semver designator. 67 | * `--find-matching-prs`: use the GitHub API to find the pull requests that match commits that don't have the `PR-URL` metadata in their message text. Without metadata, it may be necessary to also pass the org/user and repo name on the commandline (as the `github-user` and `github-project` arguments as demonstrated above, it may also be necessary to use `--find-matching-prs=true` in this case). 68 | * `--quiet` or `-q`: do not print to `process.stdout` 69 | * `--all` or `-a`: process all commits since beginning, instead of last tag. 70 | * `--help` or `-h`: show usage and help. 71 | 72 | ## Development 73 | 74 | Tests require GitHub authentication in order to fetch pull request metadata. [ghauth](https://github.com/rvagg/ghauth) will generate, store and load a [personal access token](https://github.com/settings/tokens) in your local user configuration when changelog-maker is run during normal operation. To run the tests, you will need to ensure that you have a token in place. There are two ways to do this: 75 | 76 | 1. Run `node ./changelog-maker.js -a` to cause changelog-maker to fetch metadata on a commit with a `PR-URL`. 77 | 78 | 2. Manually generate a personal access token with `public_repo` scope. Then create a config.json file: 79 | 80 | ```json 81 | { 82 | "user": "MY_GITHUB_USERNAME", 83 | "token": "MY_SECRET_TOKEN" 84 | } 85 | ``` 86 | 87 | `user` is your username, and `token` is the token you generated above. The location of `config.json` depends on the OS, please see 88 | 89 | ## License 90 | 91 | **changelog-maker** is Copyright (c) 2015 Rod Vagg [@rvagg](https://twitter.com/rvagg) and licenced under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details. 92 | -------------------------------------------------------------------------------- /auth.js: -------------------------------------------------------------------------------- 1 | import { promisify } from 'util' 2 | import ghauth from 'ghauth' 3 | 4 | const authOptions = { 5 | configName: 'changelog-maker', 6 | scopes: ['repo'], 7 | noDeviceFlow: true 8 | } 9 | 10 | export async function auth () { 11 | return await promisify(ghauth)(authOptions) 12 | } 13 | -------------------------------------------------------------------------------- /changelog-maker.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import _debug from 'debug' 4 | import process from 'process' 5 | import minimist from 'minimist' 6 | import pkgtoId from 'pkg-to-id' 7 | import { existsSync, readFileSync } from 'fs' 8 | import { join } from 'path' 9 | import { processCommits } from './process-commits.js' 10 | import { commitToList } from './commit-to-list.js' 11 | 12 | const debug = _debug('changelog-maker') 13 | 14 | const argv = minimist(process.argv.slice(2)) 15 | const help = argv.h || argv.help 16 | 17 | const pkgFile = join(process.cwd(), 'package.json') 18 | const pkgData = existsSync(pkgFile) ? JSON.parse(readFileSync(pkgFile)) : {} 19 | const pkgId = pkgtoId(pkgData) 20 | 21 | const ghId = { 22 | user: argv._[0] || pkgId.user || 'nodejs', 23 | repo: argv._[1] || (pkgId.name && stripScope(pkgId.name)) || 'node' 24 | } 25 | debug(ghId) 26 | 27 | if (help) { 28 | showUsage() 29 | process.exit(0) 30 | } 31 | 32 | function stripScope (name) { 33 | return name[0] === '@' && name.indexOf('/') > 0 ? name.split('/')[1] : name 34 | } 35 | 36 | function showUsage () { 37 | const usage = readFileSync(new URL('README.md', import.meta.url), 'utf8') 38 | .replace(/[\s\S]+(## Usage\n[\s\S]*)\n## [\s\S]+/m, '$1') 39 | .replace(/## Usage\n[\s]*/m, 'Usage: ') 40 | .replace(/\*\*/g, '') 41 | .replace(/`/g, '') 42 | 43 | process.stdout.write(usage) 44 | } 45 | 46 | async function run () { 47 | const commitList = await commitToList(ghId, argv) 48 | await processCommits(argv, ghId, commitList) 49 | } 50 | 51 | run().catch((err) => { 52 | console.error(err) 53 | process.exit(1) 54 | }) 55 | -------------------------------------------------------------------------------- /collect-commit-labels.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { auth } from './auth.js' 4 | import ghissues from 'ghissues' 5 | import async from 'async' 6 | 7 | export async function collectCommitLabels (list) { 8 | const sublist = list.filter((commit) => { 9 | return typeof commit.ghIssue === 'number' && commit.ghUser && commit.ghProject 10 | }) 11 | 12 | if (!sublist.length) { 13 | return 14 | } 15 | 16 | const authData = await auth() 17 | 18 | const cache = {} 19 | 20 | const q = async.queue((commit, next) => { 21 | function onFetch (err, issue) { 22 | if (err) { 23 | console.error('Error fetching issue #%s: %s', commit.ghIssue, err.message) 24 | return next() 25 | } 26 | 27 | if (issue.labels) { 28 | commit.labels = issue.labels.map((label) => label.name) 29 | } 30 | 31 | next() 32 | } 33 | 34 | if (commit.ghUser === 'iojs') { 35 | commit.ghUser = 'nodejs' // forcibly rewrite as the GH API doesn't do it for us 36 | } 37 | 38 | // To prevent multiple simultaneous requests for the same issue 39 | // from hitting the network at the same time, immediately assign a Promise 40 | // to the cache that all commits with the same ghIssue value will use. 41 | const key = `${commit.ghUser}/${commit.ghProject}#${commit.ghIssue}` 42 | cache[key] = cache[key] || new Promise((resolve, reject) => { 43 | ghissues.get(authData, commit.ghUser, commit.ghProject, commit.ghIssue, (err, issue) => { 44 | if (err) { 45 | return reject(err) 46 | } 47 | 48 | resolve(issue) 49 | }) 50 | }) 51 | cache[key].then((val) => onFetch(null, val), (err) => onFetch(err)) 52 | }, 15) 53 | 54 | q.push(sublist) 55 | await q.drain() 56 | } 57 | -------------------------------------------------------------------------------- /commit-to-list.js: -------------------------------------------------------------------------------- 1 | import _debug from 'debug' 2 | import process from 'process' 3 | import { pipeline as _pipeline } from 'stream' 4 | import { promisify } from 'util' 5 | import commitStream from 'commit-stream' 6 | import gitexec from 'gitexec' 7 | import split2 from 'split2' 8 | import { isReleaseCommit } from './groups.js' 9 | 10 | const debug = _debug('changelog-maker') 11 | 12 | const pipeline = promisify(_pipeline) 13 | const gitcmd = 'git log --pretty=full --since="{{sincecmd}}" --until="{{untilcmd}}"' 14 | const commitdatecmd = '$(git show -s --format=%cd `{{refcmd}}`)' 15 | const untilcmd = '' 16 | const defaultRef = '--tags=v*.*.* 2> /dev/null ' + 17 | '|| git rev-list --max-count=1 --tags=*.*.* 2> /dev/null ' + 18 | '|| git rev-list --max-count=1 HEAD' 19 | 20 | function replace (s, m) { 21 | Object.keys(m).forEach((k) => { 22 | s = s.replace(new RegExp(`\\{\\{${k}\\}\\}`, 'g'), m[k]) 23 | }) 24 | return s 25 | } 26 | 27 | function organiseCommits (argv, list) { 28 | if (argv['start-ref'] || argv.a || argv.all) { 29 | if (argv['filter-release']) { 30 | list = list.filter((commit) => !isReleaseCommit(commit.summary)) 31 | } 32 | 33 | return list 34 | } 35 | 36 | // filter commits to those _before_ 'working on ...' 37 | let started = false 38 | return list.filter((commit) => { 39 | if (started) { 40 | return false 41 | } 42 | 43 | if (isReleaseCommit(commit.summary)) { 44 | started = true 45 | } 46 | 47 | return !started 48 | }) 49 | } 50 | 51 | export async function commitToList (ghId, argv) { 52 | const refcmd = argv.a || argv.all ? 'git rev-list --max-parents=0 HEAD' : 'git rev-list --max-count=1 {{ref}}' 53 | const _startrefcmd = replace(refcmd, { ref: argv['start-ref'] || defaultRef }) 54 | const _endrefcmd = argv['end-ref'] && replace(refcmd, { ref: argv['end-ref'] }) 55 | const _sincecmd = replace(commitdatecmd, { refcmd: _startrefcmd }) 56 | const _untilcmd = argv['end-ref'] ? replace(commitdatecmd, { refcmd: _endrefcmd }) : untilcmd 57 | const _gitcmd = replace(gitcmd, { sincecmd: _sincecmd, untilcmd: _untilcmd }) 58 | debug('%s', _startrefcmd) 59 | debug('%s', _endrefcmd) 60 | debug('%s', _sincecmd) 61 | debug('%s', _untilcmd) 62 | debug('%s', _gitcmd) 63 | 64 | let commitList = [] 65 | await pipeline( 66 | gitexec.exec(process.cwd(), _gitcmd), 67 | split2(), 68 | commitStream(ghId.user, ghId.repo), 69 | async function * (source) { 70 | for await (const commit of source) { 71 | commitList.push(commit) 72 | } 73 | }) 74 | commitList = organiseCommits(argv, commitList) 75 | return commitList 76 | } 77 | -------------------------------------------------------------------------------- /commit-to-output.js: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | import { isRevert, cleanSummary as cleanRevertSummary } from './reverts.js' 3 | import { toGroups, cleanSummary as cleanGroupSummary } from './groups.js' 4 | 5 | function cleanUnsupportedMarkdown (txt) { 6 | // escape _~*\[]<>` 7 | return txt.replace(/([_~*\\[\]<>`])/g, '\\$1') 8 | } 9 | 10 | function cleanMarkdown (txt) { 11 | // Escape backticks for edge case scenarii (no code span support). 12 | if (txt.includes('``') || txt.includes('\\`')) { 13 | return cleanUnsupportedMarkdown(txt) 14 | } 15 | 16 | const backtickSplit = txt.split('`') 17 | // If there's an odd number of backticks, give up and escape them all. 18 | if (backtickSplit.length % 2 === 0) return cleanUnsupportedMarkdown(txt) 19 | 20 | let cleanMdString = '' 21 | for (let i = 0; i < backtickSplit.length; i++) { 22 | const isInsideBacktickString = i % 2 23 | cleanMdString += isInsideBacktickString 24 | // No escaping inside a code span. 25 | ? `\`${backtickSplit[i]}\`` 26 | // otherwise escape _~*\[]<> 27 | : backtickSplit[i].replace(/([_~*\\[\]<>])/g, '\\$1') 28 | } 29 | return cleanMdString 30 | } 31 | 32 | export const formatType = { 33 | SHA: 'sha', 34 | PLAINTEXT: 'plaintext', 35 | MARKDOWN: 'markdown', 36 | SIMPLE: 'simple', 37 | MESSAGEONLY: 'messageonly' 38 | } 39 | 40 | function toStringPlaintext (data) { 41 | let s = '' 42 | 43 | if (data.cveId) { 44 | s += `(${data.cveId}) ` 45 | } 46 | 47 | s += (data.semver || []).length ? `(${data.semver.join(', ').toUpperCase()}) ` : '' 48 | 49 | if (data.revert) { 50 | s += `Revert "${data.group}: ${data.summary} ` 51 | } else { 52 | s += `${data.summary} ` 53 | } 54 | 55 | s += data.author ? `(${data.author}) ` : '' 56 | s += data.pr ? data.prUrl : '' 57 | 58 | return ` * ${s.trim()}` 59 | } 60 | 61 | function toStringSimple (data) { 62 | let s = '' 63 | s += `* [${data.sha.substr(0, 10)}] - ` 64 | s += (data.semver || []).length ? `(${data.semver.join(', ').toUpperCase()}) ` : '' 65 | s += data.revert ? 'Revert "' : '' 66 | s += data.group ? `${data.group}: ` : '' 67 | s += data.summary 68 | s += data.revert ? '" ' : ' ' 69 | s += data.author ? `(${data.author}) ` : '' 70 | s += data.pr ? data.prUrl : '' 71 | s = s.trim() 72 | 73 | return (data.semver && data.semver.length) 74 | ? chalk.green.bold(s) 75 | : (data.group === 'doc' 76 | ? chalk.grey(s) 77 | : s) 78 | } 79 | 80 | function toStringMarkdown (data) { 81 | let s = '' 82 | s += `* \\[[\`${data.sha.substr(0, 10)}\`](${data.shaUrl})] - ` 83 | s += (data.semver || []).length ? `**(${data.semver.join(', ').toUpperCase()})** ` : '' 84 | s += data.cveId ? `**(${data.cveId})** ` : '' 85 | s += data.revert ? '***Revert*** "' : '' 86 | s += data.group ? `**${cleanMarkdown(data.group)}**: ` : '' 87 | s += cleanMarkdown(data.summary) 88 | s += data.revert ? '" ' : ' ' 89 | s += data.author ? `(${cleanMarkdown(data.author)}) ` : '' 90 | s += data.pr ? `[${data.pr}](${data.prUrl})` : '' 91 | s = s.trim() 92 | 93 | return (data.semver && data.semver.length) 94 | ? chalk.green.bold(s) 95 | : (data.group === 'doc' 96 | ? chalk.grey(s) 97 | : s) 98 | } 99 | 100 | function toStringMessageOnly (data) { 101 | return ` * ${data.cveId ? '(' + data.cveId + ') ' : ''}${data.summary.trim()}` 102 | } 103 | 104 | export function commitToOutput (commit, format, ghId, commitUrl) { 105 | const data = {} 106 | const prUrlMatch = commit.prUrl && commit.prUrl.match(/^https?:\/\/.+\/([^/]+\/[^/]+)\/\w+\/\d+$/i) 107 | const urlHash = `#${commit.ghIssue}` || commit.prUrl 108 | const ref = commit.sha.substr(0, 10) 109 | 110 | data.sha = commit.sha 111 | data.shaUrl = commitUrl.replace(/\{ghUser\}/g, ghId.user).replace(/\{ghRepo\}/g, ghId.repo).replace(/\{ref\}/g, ref) 112 | data.semver = commit.labels && commit.labels.filter((l) => l.includes('semver')) 113 | data.revert = isRevert(commit.summary) 114 | data.group = toGroups(commit.summary) 115 | data.summary = cleanGroupSummary(cleanRevertSummary(commit.summary)) 116 | data.author = (commit.author && commit.author.name) || '' 117 | data.pr = prUrlMatch && ((prUrlMatch[1] !== `${ghId.user}/${ghId.repo}` ? prUrlMatch[1] : '') + urlHash) 118 | data.prUrl = prUrlMatch && commit.prUrl 119 | data.cveId = commit.cveId 120 | 121 | if (format === formatType.SIMPLE) { 122 | return toStringSimple(data) 123 | } else if (format === formatType.PLAINTEXT) { 124 | return toStringPlaintext(data) 125 | } else if (format === formatType.MESSAGEONLY) { 126 | return toStringMessageOnly(data) 127 | } 128 | 129 | return toStringMarkdown(data) 130 | } 131 | -------------------------------------------------------------------------------- /find-matching-prs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { auth } from './auth.js' 4 | import { graphql } from '@octokit/graphql' 5 | import async from 'async' 6 | 7 | // Query to find the first 4 pull requests that include the commit that we're 8 | // concerned about. We'll filter them and take the first one that was MERGED 9 | // as our prUrl. 10 | const query = ` 11 | query ($owner: String!, $name: String!, $commit: GitObjectID!) { 12 | repository(owner: $owner, name: $name) { 13 | object(oid: $commit) { 14 | ... on Commit { 15 | associatedPullRequests(first: 4) { 16 | ... on PullRequestConnection { 17 | edges { 18 | node { 19 | ... on PullRequest { 20 | number 21 | url 22 | title 23 | state 24 | } 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | ` 34 | 35 | export async function findMatchingPrs (ghId, list) { 36 | // only look up commits that don't have a prUrl from metadata 37 | const sublist = list.filter((commit) => typeof commit.prUrl !== 'string') 38 | if (!sublist.length) { 39 | return 40 | } 41 | 42 | const authData = await auth() 43 | const headers = { authorization: `token ${authData.token}` } 44 | const cache = {} 45 | 46 | const q = async.queue(async (commit, next) => { 47 | if (commit.ghUser === 'iojs') { 48 | commit.ghUser = 'nodejs' // forcibly rewrite as the GH API doesn't do it for us 49 | } 50 | 51 | // cache on commit, so we don't run the same commit twice (is this possible?) 52 | cache[commit.sha] = cache[commit.sha] || (async () => { 53 | try { 54 | const res = await graphql(query, { owner: ghId.user, name: ghId.repo, commit: commit.sha, headers }) 55 | if (res.repository?.object?.associatedPullRequests?.edges?.length) { 56 | const pr = res.repository.object.associatedPullRequests.edges.filter((e) => e.node?.state === 'MERGED')[0] 57 | if (pr) { 58 | commit.ghIssue = pr.node.number 59 | commit.prUrl = pr.node.url 60 | } 61 | } 62 | } catch (err) { 63 | console.error(`Error querying GitHub to find pull request for commit: ${err}`) 64 | } 65 | })() 66 | await cache[commit.sha] 67 | next() 68 | }, 15) 69 | 70 | q.push(sublist) 71 | await q.drain() 72 | } 73 | -------------------------------------------------------------------------------- /format.js: -------------------------------------------------------------------------------- 1 | import { unified } from 'unified' 2 | import remarkParse from 'remark-parse' 3 | import remarkStringify from 'remark-stringify' 4 | import presetLintNode from 'remark-preset-lint-node' 5 | 6 | const formatter = unified() 7 | .use(remarkParse) 8 | .use(presetLintNode) 9 | .use(remarkStringify) 10 | 11 | export async function formatMarkdown (markdown) { 12 | const result = await formatter.process(markdown) 13 | return result.toString() 14 | } 15 | -------------------------------------------------------------------------------- /group-commits.js: -------------------------------------------------------------------------------- 1 | import { toGroups } from './groups.js' 2 | 3 | export function groupCommits (list) { 4 | const groupList = list.reduce((groupList, commit) => { 5 | const group = toGroups(commit.summary) || '*' 6 | 7 | if (!groupList[group]) { 8 | groupList[group] = [] 9 | } 10 | 11 | groupList[group].push(commit) 12 | return groupList 13 | }, {}) 14 | 15 | return Object.keys(groupList).sort().reduce((p, group) => { 16 | return p.concat(groupList[group]) 17 | }, []) 18 | } 19 | -------------------------------------------------------------------------------- /groups.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'url' 2 | import process from 'process' 3 | import { readFileSync } from 'fs' 4 | import { cleanSummary as cleanRevertsSummary } from './reverts.js' 5 | 6 | const groupRe = /^((:?\w|-|,|, )+(\([\w.,-]+\))?)!?:\s*/i 7 | 8 | export function toGroups (summary) { 9 | summary = cleanRevertsSummary(summary) 10 | const m = summary.match(groupRe) 11 | return (m && m[1]) || '' 12 | } 13 | 14 | export function cleanSummary (summary) { 15 | return (summary || '').replace(groupRe, '') 16 | } 17 | 18 | /* 19 | To test this, run on the command line in a nodejs/node clone: 20 | 21 | for br in v4.x v5.x v6.x v7.x; do git log $br --format='%s' | grep -E '^\d+.*elease$' >> /tmp/release-commits-all.txt; done 22 | sort /tmp/release-commits-all.txt | uniq > /tmp/release-commits.txt 23 | rm /tmp/release-commits-all.txt 24 | 25 | Then in this directory: 26 | 27 | node groups.js /tmp/release-commits.txt 28 | 29 | It doesn't cover false positives though. 30 | */ 31 | 32 | export function isReleaseCommit (summary) { 33 | return /^Working on v?\d{1,2}\.\d{1,3}\.\d{1,3}$/.test(summary) || 34 | /^\d{4}-\d{2}-\d{2},? (Node\.js|Version) v?\d{1,2}\.\d{1,3}\.\d{1,3} (["'][A-Za-z ]+["'] )?\((Current|Stable|LTS|Maintenance)\)/.test(summary) || 35 | /^\d{4}-\d{2}-\d{2},? io.js v\d{1,2}\.\d{1,3}\.\d{1,3} Release/.test(summary) || 36 | /^\d+\.\d+\.\d+$/.test(summary) // `npm version X` style commit 37 | } 38 | 39 | if (process.argv[1] === fileURLToPath(import.meta.url)) { 40 | console.log(`Running tests on lines in ${process.argv[2]}...`) 41 | const failures = readFileSync(process.argv[2], 'utf8').split('\n').filter(Boolean).filter((summary) => { 42 | return !isReleaseCommit(summary) 43 | }) 44 | if (!failures.length) { 45 | console.log('All good, no failures!') 46 | } else { 47 | console.log('Failed on the following commit summaries:') 48 | console.log(failures.join('\n')) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "changelog-maker", 3 | "version": "4.4.2", 4 | "description": "A git log to CHANGELOG.md tool", 5 | "main": "changelog-maker.js", 6 | "type": "module", 7 | "bin": { 8 | "changelog-maker": "./changelog-maker.js" 9 | }, 10 | "exports": { 11 | "./collect-commit-labels": "./collect-commit-labels.js", 12 | "./groups": "./groups.js", 13 | "./process-commits": "./process-commits.js" 14 | }, 15 | "author": "Rod (https://r.va.gg/)", 16 | "license": "MIT", 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/nodejs/changelog-maker.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/nodejs/changelog-maker/issues" 23 | }, 24 | "homepage": "https://github.com/nodejs/changelog-maker#readme", 25 | "keywords": [], 26 | "preferGlobal": true, 27 | "dependencies": { 28 | "@octokit/graphql": "^7.0.1", 29 | "async": "^3.2.4", 30 | "chalk": "^5.3.0", 31 | "commit-stream": "^2.2.0", 32 | "debug": "^4.3.4", 33 | "ghauth": "^6.0.0", 34 | "ghissues": "^1.1.4", 35 | "gitexec": "^2.0.1", 36 | "minimist": "^1.2.8", 37 | "pkg-to-id": "^0.0.3", 38 | "remark-parse": "^11.0.0", 39 | "remark-preset-lint-node": "^5.0.0", 40 | "remark-stringify": "^11.0.0", 41 | "split2": "^4.2.0", 42 | "strip-ansi": "^7.1.0", 43 | "unified": "^11.0.3" 44 | }, 45 | "devDependencies": { 46 | "standard": "^17.1.0", 47 | "tap": "^21.0.1" 48 | }, 49 | "scripts": { 50 | "lint": "standard", 51 | "unit": "tap --allow-incomplete-coverage", 52 | "build": "true", 53 | "test:ci": "npm run test", 54 | "test": "npm run lint && npm run unit" 55 | }, 56 | "release": { 57 | "branches": [ 58 | "main" 59 | ], 60 | "plugins": [ 61 | [ 62 | "@semantic-release/commit-analyzer", 63 | { 64 | "preset": "conventionalcommits", 65 | "releaseRules": [ 66 | { 67 | "breaking": true, 68 | "release": "major" 69 | }, 70 | { 71 | "revert": true, 72 | "release": "patch" 73 | }, 74 | { 75 | "type": "feat", 76 | "release": "minor" 77 | }, 78 | { 79 | "type": "fix", 80 | "release": "patch" 81 | }, 82 | { 83 | "type": "chore", 84 | "release": "patch" 85 | }, 86 | { 87 | "type": "docs", 88 | "release": "patch" 89 | }, 90 | { 91 | "type": "test", 92 | "release": "patch" 93 | }, 94 | { 95 | "scope": "no-release", 96 | "release": false 97 | } 98 | ] 99 | } 100 | ], 101 | [ 102 | "@semantic-release/release-notes-generator", 103 | { 104 | "preset": "conventionalcommits", 105 | "presetConfig": { 106 | "types": [ 107 | { 108 | "type": "feat", 109 | "section": "Features" 110 | }, 111 | { 112 | "type": "fix", 113 | "section": "Bug Fixes" 114 | }, 115 | { 116 | "type": "chore", 117 | "section": "Trivial Changes" 118 | }, 119 | { 120 | "type": "docs", 121 | "section": "Trivial Changes" 122 | }, 123 | { 124 | "type": "test", 125 | "section": "Tests" 126 | } 127 | ] 128 | } 129 | } 130 | ], 131 | "@semantic-release/changelog", 132 | "@semantic-release/npm", 133 | "@semantic-release/github", 134 | "@semantic-release/git" 135 | ] 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /process-commits.js: -------------------------------------------------------------------------------- 1 | import stripAnsi from 'strip-ansi' 2 | import { commitToOutput, formatType } from './commit-to-output.js' 3 | import { groupCommits } from './group-commits.js' 4 | import { toGroups } from './groups.js' 5 | import { formatMarkdown } from './format.js' 6 | import { supportsColor } from 'chalk' 7 | import { collectCommitLabels } from './collect-commit-labels.js' 8 | import { findMatchingPrs } from './find-matching-prs.js' 9 | 10 | function getFormat (argv) { 11 | if (argv.format && Object.values(formatType).includes(argv.format)) { 12 | return argv.format 13 | } else if (argv.sha) { 14 | return formatType.SHA 15 | } else if (argv.plaintext || argv.p) { 16 | return formatType.PLAINTEXT 17 | } else if (argv.markdown || argv.md) { 18 | return formatType.MARKDOWN 19 | } else if (argv.messageonly || argv.mo) { 20 | return formatType.MESSAGEONLY 21 | } 22 | return formatType.SIMPLE 23 | } 24 | 25 | async function printCommits (list) { 26 | for (let commit of list) { 27 | if (!supportsColor) { 28 | commit = stripAnsi(commit) 29 | } 30 | process.stdout.write(`${commit}\n`) 31 | } 32 | } 33 | 34 | export async function processCommits (argv, ghId, list) { 35 | const quiet = argv.quiet || argv.q 36 | const reverse = argv.reverse 37 | const commitUrl = argv['commit-url'] || 'https://github.com/{ghUser}/{ghRepo}/commit/{ref}' 38 | 39 | if (argv['find-matching-prs']) { 40 | await findMatchingPrs(ghId, list) 41 | } 42 | 43 | await collectCommitLabels(list) 44 | 45 | const format = getFormat(argv) 46 | 47 | if (argv.group || argv.g || format === formatType.PLAINTEXT || format === formatType.MESSAGEONLY) { 48 | list = groupCommits(list) 49 | } 50 | 51 | if (format === formatType.SHA) { 52 | list = list.map((commit) => `${commit.sha.substr(0, 10)}`) 53 | } else if ( 54 | format === formatType.PLAINTEXT || 55 | format === formatType.MESSAGEONLY 56 | ) { 57 | const formatted = [] 58 | 59 | let currentGroup 60 | for (const commit of list) { 61 | const commitGroup = toGroups(commit.summary) 62 | if (currentGroup !== commitGroup) { 63 | formatted.push(commitGroup ? `${commitGroup}:` : '') 64 | currentGroup = commitGroup 65 | } 66 | formatted.push(commitToOutput(commit, format, ghId, commitUrl)) 67 | } 68 | list = formatted 69 | } else { 70 | list = await Promise.all(list.map(async (commit) => { 71 | let output = commitToOutput(commit, format, ghId, commitUrl) 72 | if (format === formatType.MARKDOWN) { 73 | output = stripAnsi(output) 74 | return (await formatMarkdown(output)).replace(/\n$/, '') 75 | } 76 | return output 77 | })) 78 | } 79 | 80 | if (format !== formatType.PLAINTEXT && reverse) { 81 | list = list.reverse() 82 | } 83 | 84 | if (!quiet) { 85 | printCommits(list) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /reverts.js: -------------------------------------------------------------------------------- 1 | const revertRe = /^revert\s+"?/i 2 | 3 | export function isRevert (summary) { 4 | return summary && revertRe.test(summary) 5 | } 6 | 7 | export function cleanSummary (summary = '') { 8 | if (!isRevert(summary)) { 9 | return summary 10 | } 11 | 12 | return summary.replace(revertRe, '').replace(/"$/, '') 13 | } 14 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | // NOTE: to run this you will probably need to be authorized with GitHub. 2 | // Run `./changelog-maker.js` by itself to set this up. 3 | 4 | import { dirname, join } from 'path' 5 | import { execSync } from 'child_process' 6 | import { test } from 'tap' 7 | import chalk from 'chalk' 8 | 9 | const __dirname = dirname(new URL(import.meta.url).pathname) 10 | 11 | function exec (args) { 12 | const stdout = execSync(`"${process.execPath}" ${join(__dirname, 'changelog-maker.js')} ${args}`).toString() 13 | 14 | return stdout 15 | } 16 | 17 | test('test basic commit block', (t) => { 18 | t.equal(exec('--md --start-ref=v1.3.9 --end-ref=v1.3.10'), 19 | `* \\[[\`e28b3f2813\`](https://github.com/nodejs/changelog-maker/commit/e28b3f2813)] - 1.3.10 (Rod Vagg) 20 | * \\[[\`ace3af943e\`](https://github.com/nodejs/changelog-maker/commit/ace3af943e)] - Merge pull request #13 from jamsyoung/private-repo-support (Rod Vagg) 21 | * \\[[\`25ec5428bc\`](https://github.com/nodejs/changelog-maker/commit/25ec5428bc)] - default to repo scope always - revert previous changes (James Young) 22 | * \\[[\`424d6c22c1\`](https://github.com/nodejs/changelog-maker/commit/424d6c22c1)] - add --private arg to set repo scope, update readme (James Young) 23 | * \\[[\`7c8c5e6215\`](https://github.com/nodejs/changelog-maker/commit/7c8c5e6215)] - 1.3.9 (Rod Vagg) 24 | `) 25 | t.end() 26 | }) 27 | 28 | test('test filter-release', (t) => { 29 | t.equal(exec('--md --start-ref=v1.3.9 --end-ref=v1.3.10 --filter-release'), 30 | `* \\[[\`ace3af943e\`](https://github.com/nodejs/changelog-maker/commit/ace3af943e)] - Merge pull request #13 from jamsyoung/private-repo-support (Rod Vagg) 31 | * \\[[\`25ec5428bc\`](https://github.com/nodejs/changelog-maker/commit/25ec5428bc)] - default to repo scope always - revert previous changes (James Young) 32 | * \\[[\`424d6c22c1\`](https://github.com/nodejs/changelog-maker/commit/424d6c22c1)] - add --private arg to set repo scope, update readme (James Young) 33 | `) 34 | t.end() 35 | }) 36 | 37 | test('test simple', (t) => { 38 | t.equal(exec('--start-ref=v1.3.9 --end-ref=v1.3.10'), 39 | `* [e28b3f2813] - 1.3.10 (Rod Vagg) 40 | * [ace3af943e] - Merge pull request #13 from jamsyoung/private-repo-support (Rod Vagg) 41 | * [25ec5428bc] - default to repo scope always - revert previous changes (James Young) 42 | * [424d6c22c1] - add --private arg to set repo scope, update readme (James Young) 43 | * [7c8c5e6215] - 1.3.9 (Rod Vagg) 44 | `) 45 | t.end() 46 | }) 47 | 48 | test('test plaintext', (t) => { 49 | t.equal(exec('--start-ref=9c700d2 --end-ref=dd937e9 --group --filter-release --plaintext'), 50 | `feature: 51 | * refactor and improve --commit-url (Rod Vagg) 52 | test: 53 | * update refs for testing (Rod Vagg) 54 | `) 55 | t.end() 56 | }) 57 | 58 | test('test messageonly', (t) => { 59 | t.equal(exec('--start-ref=9c700d2 --end-ref=dd937e9 --group --filter-release --messageonly'), 60 | `feature: 61 | * refactor and improve --commit-url 62 | test: 63 | * update refs for testing 64 | `) 65 | t.end() 66 | }) 67 | 68 | test('test group, semver labels, PR-URL', (t) => { 69 | t.equal(exec('--start-ref=v2.2.7 --end-ref=9c700d29 --group --filter-release'), 70 | `${chalk.green.bold('* [cc442b6534] - (SEMVER-MINOR) minor nit (Rod Vagg) https://github.com/nodejs/node/pull/23715')} 71 | * [4f2b7f8136] - deps: use strip-ansi instead of chalk.stripColor (Rod Vagg) 72 | * [6898501e18] - deps: update deps, introduce test & lint deps (Rod Vagg) 73 | * [9c700d2910] - feature: refactor and improve --commit-url (Rod Vagg) 74 | * [5094524655] - feature: make the commit url configurable via an additional argument (Jim Nielsen) https://github.com/nodejs/changelog-maker/pull/55 75 | * [42f248cf89] - src: use \`standard\` for linting (Rod Vagg) 76 | * [64a8fdef3c] - test: basic test infrastructure (Rod Vagg) 77 | `) 78 | t.end() 79 | }) 80 | 81 | test('test simple group, semver labels, PR-URL', (t) => { 82 | t.equal(exec('--md --start-ref=v2.2.7 --end-ref=9c700d29 --group --filter-release'), 83 | `* \\[[\`cc442b6534\`](https://github.com/nodejs/changelog-maker/commit/cc442b6534)] - **(SEMVER-MINOR)** minor nit (Rod Vagg) [nodejs/node#23715](https://github.com/nodejs/node/pull/23715) 84 | * \\[[\`4f2b7f8136\`](https://github.com/nodejs/changelog-maker/commit/4f2b7f8136)] - **deps**: use strip-ansi instead of chalk.stripColor (Rod Vagg) 85 | * \\[[\`6898501e18\`](https://github.com/nodejs/changelog-maker/commit/6898501e18)] - **deps**: update deps, introduce test & lint deps (Rod Vagg) 86 | * \\[[\`9c700d2910\`](https://github.com/nodejs/changelog-maker/commit/9c700d2910)] - **feature**: refactor and improve --commit-url (Rod Vagg) 87 | * \\[[\`5094524655\`](https://github.com/nodejs/changelog-maker/commit/5094524655)] - **feature**: make the commit url configurable via an additional argument (Jim Nielsen) [#55](https://github.com/nodejs/changelog-maker/pull/55) 88 | * \\[[\`42f248cf89\`](https://github.com/nodejs/changelog-maker/commit/42f248cf89)] - **src**: use \`standard\` for linting (Rod Vagg) 89 | * \\[[\`64a8fdef3c\`](https://github.com/nodejs/changelog-maker/commit/64a8fdef3c)] - **test**: basic test infrastructure (Rod Vagg) 90 | `) 91 | t.end() 92 | }) 93 | 94 | test('test blank commit-url', (t) => { 95 | let actual = exec('--md --start-ref=v2.2.7 --end-ref=9c700d29 --filter-release --commit-url=http://foo.bar/').split('\n') 96 | actual.splice(0, actual.length - 3) 97 | actual = actual.join('\n') 98 | t.equal(actual, 99 | `* \\[[\`cc442b6534\`](http://foo.bar/)] - **(SEMVER-MINOR)** minor nit (Rod Vagg) [nodejs/node#23715](https://github.com/nodejs/node/pull/23715) 100 | * \\[[\`5094524655\`](http://foo.bar/)] - **feature**: make the commit url configurable via an additional argument (Jim Nielsen) [#55](https://github.com/nodejs/changelog-maker/pull/55) 101 | `) 102 | t.end() 103 | }) 104 | 105 | test('test blank commit-url', (t) => { 106 | let actual = exec('--md --start-ref=v2.2.7 --end-ref=9c700d29 --filter-release --commit-url=https://yeehaw.com/{ref}/{ref}/{ghUser}/{ghRepo}/').split('\n') 107 | actual.splice(0, actual.length - 3) 108 | actual = actual.join('\n') 109 | t.equal(actual, 110 | `* \\[[\`cc442b6534\`](https://yeehaw.com/cc442b6534/cc442b6534/nodejs/changelog-maker/)] - **(SEMVER-MINOR)** minor nit (Rod Vagg) [nodejs/node#23715](https://github.com/nodejs/node/pull/23715) 111 | * \\[[\`5094524655\`](https://yeehaw.com/5094524655/5094524655/nodejs/changelog-maker/)] - **feature**: make the commit url configurable via an additional argument (Jim Nielsen) [#55](https://github.com/nodejs/changelog-maker/pull/55) 112 | `) 113 | t.end() 114 | }) 115 | 116 | test('test backtick strings in commit messages', (t) => { 117 | t.equal( 118 | exec('--md --start-ref=ce886b5130 --end-ref=0717fdc946 --filter-release --commit-url=https://yeehaw.com/{ref}/{ref}/{ghUser}/{ghRepo}/'), 119 | `* \\[[\`0717fdc946\`](https://yeehaw.com/0717fdc946/0717fdc946/nodejs/changelog-maker/)] - **test**: \\\`commit\\_msg\\\` with an unescaped \\\` backtick char (Antoine du Hamel) 120 | * \\[[\`9f1d897c88\`](https://yeehaw.com/9f1d897c88/9f1d897c88/nodejs/changelog-maker/)] - **test**: \\\`commit\\_msg\\\` with an escaped \\\\\\\` backtick char (Antoine du Hamel) 121 | * \\[[\`4a3154bde0\`](https://yeehaw.com/4a3154bde0/4a3154bde0/nodejs/changelog-maker/)] - **test**: \`commit_msg\` starting with a backtick string (Antoine du Hamel) 122 | * \\[[\`46384bb241\`](https://yeehaw.com/46384bb241/46384bb241/nodejs/changelog-maker/)] - **test**: commit\\_msg with \\\`backtick\\\\\\\` string (Antoine du Hamel) 123 | * \\[[\`3b13c6804d\`](https://yeehaw.com/3b13c6804d/3b13c6804d/nodejs/changelog-maker/)] - **test**: commit\\_msg with \\\`\\\`backtick \\\` string\\\`\\\` (Antoine du Hamel) 124 | * \\[[\`699ce8c377\`](https://yeehaw.com/699ce8c377/699ce8c377/nodejs/changelog-maker/)] - **test**: commit\\_msg with \`back_tick\` string (Antoine du Hamel) 125 | * \\[[\`ce886b5130\`](https://yeehaw.com/ce886b5130/ce886b5130/nodejs/changelog-maker/)] - **test**: commit\\_msg with \`backtick\` string (Antoine du Hamel) 126 | `) 127 | t.end() 128 | }) 129 | 130 | test('test markdown punctuation chars in commit message and author name', (t) => { 131 | t.equal( 132 | exec('--md --start-ref=f12fe589c4 --end-ref=f12fe589c4 --filter-release --commit-url=https://yeehaw.com/{ref}/{ref}/{ghUser}/{ghRepo}/'), 133 | `* \\[[\`f12fe589c4\`](https://yeehaw.com/f12fe589c4/f12fe589c4/nodejs/changelog-maker/)] - **group\\_with\\_underscore**: test commit message (Author\\_name\\_with\\_underscore) 134 | `) 135 | t.end() 136 | }) 137 | 138 | test('test find-matching-prs', (t) => { 139 | t.equal( 140 | exec('--start-ref=a059bc7ca9 --end-ref=a059bc7ca9 --find-matching-prs=true nodejs changelog-maker'), 141 | `* [a059bc7ca9] - chore(deps): remove package-lock.json (Rod Vagg) https://github.com/nodejs/changelog-maker/pull/118 142 | `) 143 | t.end() 144 | }) 145 | 146 | test('test group, CVE-ID', (t) => { 147 | const out = exec('--md --start-ref=43d428b3d2 --end-ref=43d428b3d2 --group --filter-release') 148 | t.equal( 149 | out, 150 | `* \\[[\`43d428b3d2\`](https://github.com/nodejs/changelog-maker/commit/43d428b3d2)] - **(CVE-2024-22020)** **feat**: add cveId support to commmit output (RafaelGSS) [nodejs/node#55819](https://github.com/nodejs/node/pull/55819) 151 | `) 152 | t.end() 153 | }) 154 | 155 | test('test conventionalcommit style', (t) => { 156 | // testing that we capture `foo(bar)` as a group in `foo(bar): message`, not just `foo` in `foo: message` 157 | const out = exec('--md --start-ref=35b762c7 --end-ref=375e0b7d') 158 | t.equal( 159 | out, 160 | `* \\[[\`375e0b7d48\`](https://github.com/nodejs/changelog-maker/commit/375e0b7d48)] - **test(cc,yay)**: add test case for conventional commits (Rod Vagg) 161 | * \\[[\`6d6ef98c14\`](https://github.com/nodejs/changelog-maker/commit/6d6ef98c14)] - **feat(conventionalcommits)**: make group matching regexp cover CC style (Rod Vagg) 162 | * \\[[\`b3c44809b7\`](https://github.com/nodejs/changelog-maker/commit/b3c44809b7)] - **chore(release)**: 4.2.1 \\[skip ci] (semantic-release-bot) 163 | * \\[[\`fce438030c\`](https://github.com/nodejs/changelog-maker/commit/fce438030c)] - **chore(deps-dev)**: bump tap from 18.8.0 to 19.0.0 (dependabot\\[bot]) [#165](https://github.com/nodejs/changelog-maker/pull/165) 164 | * \\[[\`ddb59f6a17\`](https://github.com/nodejs/changelog-maker/commit/ddb59f6a17)] - **chore(release)**: 4.2.0 \\[skip ci] (semantic-release-bot) 165 | * \\[[\`f8391430af\`](https://github.com/nodejs/changelog-maker/commit/f8391430af)] - Merge pull request #167 from nodejs/add-cve-id-to-commitoutput (Rafael Gonzaga) 166 | * \\[[\`f2f235bb05\`](https://github.com/nodejs/changelog-maker/commit/f2f235bb05)] - **test**: add cveId tests (RafaelGSS) 167 | * \\[[\`43d428b3d2\`](https://github.com/nodejs/changelog-maker/commit/43d428b3d2)] - **(CVE-2024-22020)** **feat**: add cveId support to commmit output (RafaelGSS) [nodejs/node#55819](https://github.com/nodejs/node/pull/55819) 168 | * \\[[\`35b762c78a\`](https://github.com/nodejs/changelog-maker/commit/35b762c78a)] - **chore(release)**: 4.1.1 \\[skip ci] (semantic-release-bot) 169 | `) 170 | t.end() 171 | }) 172 | 173 | test('test plaintext, CVE-ID', (t) => { 174 | const out = exec('--start-ref=43d428b3d2 --end-ref=43d428b3d2 --group --filter-release --plaintext') 175 | t.equal( 176 | out, 177 | `feat: 178 | * (CVE-2024-22020) add cveId support to commmit output (RafaelGSS) https://github.com/nodejs/node/pull/55819 179 | `) 180 | t.end() 181 | }) 182 | --------------------------------------------------------------------------------