@furystack/yarn-plugin-changelog 1.0.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/README.md +181 -0
- package/bundles/@yarnpkg/plugin-changelog.js +124 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @furystack/yarn-plugin-changelog
|
|
2
|
+
|
|
3
|
+
A Yarn plugin for automated changelog generation and management in monorepos. It integrates with Yarn's version plugin to generate, validate, and apply changelog entries from version manifests.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### As a Yarn Plugin (Recommended)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
yarn plugin import https://raw.githubusercontent.com/furystack/furystack/main/packages/yarn-plugin-changelog/bundles/@yarnpkg/plugin-changelog.js
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### From NPM
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn plugin import @furystack/yarn-plugin-changelog
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
This plugin provides three commands that work together with Yarn's version workflow:
|
|
22
|
+
|
|
23
|
+
### 1. Create Changelog Drafts
|
|
24
|
+
|
|
25
|
+
Generate changelog draft files from version manifests (`.yarn/versions/*.yml`):
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
yarn changelog create
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
This creates draft files in `.yarn/changelogs/` with template sections based on the version bump type (patch/minor/major).
|
|
32
|
+
|
|
33
|
+
**Options:**
|
|
34
|
+
|
|
35
|
+
| Flag | Description |
|
|
36
|
+
| --------------------- | ---------------------------------------------------------------------- |
|
|
37
|
+
| `-v, --verbose` | Show verbose output |
|
|
38
|
+
| `-f, --force` | Regenerate changelogs with mismatched version types or invalid entries |
|
|
39
|
+
| `--dependabot` | Auto-fill changelog for dependency updates (Dependabot PRs) |
|
|
40
|
+
| `-m, --message <msg>` | Custom message for the changelog entry (used with `--dependabot`) |
|
|
41
|
+
|
|
42
|
+
**Examples:**
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Generate changelog drafts for all version manifests
|
|
46
|
+
yarn changelog create
|
|
47
|
+
|
|
48
|
+
# Regenerate mismatched or invalid changelogs
|
|
49
|
+
yarn changelog create --force
|
|
50
|
+
|
|
51
|
+
# Auto-fill for Dependabot dependency updates
|
|
52
|
+
yarn changelog create --dependabot
|
|
53
|
+
|
|
54
|
+
# With custom message (e.g., from PR title)
|
|
55
|
+
yarn changelog create --dependabot -m "Bump lodash from 4.17.20 to 4.17.21"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. Validate Changelogs
|
|
59
|
+
|
|
60
|
+
Validate that all changelog entries are complete and match their version manifests:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
yarn changelog check
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Validates:**
|
|
67
|
+
|
|
68
|
+
- Every release in `.yarn/versions/*.yml` has a corresponding changelog file
|
|
69
|
+
- Major releases have filled "Breaking Changes" sections
|
|
70
|
+
- At least one section has content (not just placeholders)
|
|
71
|
+
- Version type in changelog matches the manifest
|
|
72
|
+
|
|
73
|
+
**Options:**
|
|
74
|
+
|
|
75
|
+
| Flag | Description |
|
|
76
|
+
| --------------- | -------------------------------------------- |
|
|
77
|
+
| `-v, --verbose` | Show verbose output including passing checks |
|
|
78
|
+
|
|
79
|
+
### 3. Apply Changelogs
|
|
80
|
+
|
|
81
|
+
Apply changelog drafts to each package's `CHANGELOG.md` file:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
yarn changelog apply
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This command:
|
|
88
|
+
|
|
89
|
+
- Reads all changelog drafts from `.yarn/changelogs/`
|
|
90
|
+
- Groups entries by package name
|
|
91
|
+
- Merges multiple entries for the same package (with deduplication)
|
|
92
|
+
- Prepends new entries to each package's `CHANGELOG.md`
|
|
93
|
+
- Deletes processed draft files
|
|
94
|
+
|
|
95
|
+
**Options:**
|
|
96
|
+
|
|
97
|
+
| Flag | Description |
|
|
98
|
+
| --------------- | ---------------------------------------------- |
|
|
99
|
+
| `-v, --verbose` | Show verbose output |
|
|
100
|
+
| `--dry-run` | Show what would be done without making changes |
|
|
101
|
+
|
|
102
|
+
## Workflow
|
|
103
|
+
|
|
104
|
+
A typical release workflow using this plugin:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# 1. Make your changes and commit them
|
|
108
|
+
|
|
109
|
+
# 2. Bump versions (creates .yarn/versions/*.yml)
|
|
110
|
+
yarn version check --interactive
|
|
111
|
+
|
|
112
|
+
# 3. Generate changelog drafts
|
|
113
|
+
yarn changelog create
|
|
114
|
+
|
|
115
|
+
# 4. Edit the generated drafts in .yarn/changelogs/
|
|
116
|
+
# Fill in the relevant sections with your changes
|
|
117
|
+
|
|
118
|
+
# 5. Validate changelogs
|
|
119
|
+
yarn changelog check
|
|
120
|
+
|
|
121
|
+
# 6. Apply version changes and changelogs
|
|
122
|
+
yarn version apply --all
|
|
123
|
+
yarn changelog apply
|
|
124
|
+
|
|
125
|
+
# 7. Commit and push
|
|
126
|
+
git add -A
|
|
127
|
+
git commit -m "Release"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Changelog Draft Format
|
|
131
|
+
|
|
132
|
+
Draft files use a markdown format with conventional changelog sections:
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
<!-- version-type: minor -->
|
|
136
|
+
|
|
137
|
+
# @scope/package-name
|
|
138
|
+
|
|
139
|
+
## 💥 Breaking Changes
|
|
140
|
+
|
|
141
|
+
(Only for major releases)
|
|
142
|
+
|
|
143
|
+
## ✨ Features
|
|
144
|
+
|
|
145
|
+
- Added new feature X
|
|
146
|
+
|
|
147
|
+
## 🐛 Bug Fixes
|
|
148
|
+
|
|
149
|
+
- Fixed issue with Y
|
|
150
|
+
|
|
151
|
+
## 📚 Documentation
|
|
152
|
+
|
|
153
|
+
## ⚡ Performance
|
|
154
|
+
|
|
155
|
+
## ♻️ Refactoring
|
|
156
|
+
|
|
157
|
+
## 🧪 Tests
|
|
158
|
+
|
|
159
|
+
## 📦 Build
|
|
160
|
+
|
|
161
|
+
## 👷 CI
|
|
162
|
+
|
|
163
|
+
## ⬆️ Dependencies
|
|
164
|
+
|
|
165
|
+
## 🔧 Chores
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Directory Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
.yarn/
|
|
172
|
+
├── versions/ # Version manifests (created by yarn version)
|
|
173
|
+
│ └── abc123.yml
|
|
174
|
+
└── changelogs/ # Changelog drafts (created by this plugin)
|
|
175
|
+
├── @scope-package-a.abc123.md
|
|
176
|
+
└── @scope-package-b.abc123.md
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
GPL-2.0
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
//prettier-ignore
|
|
3
|
+
module.exports = {
|
|
4
|
+
name: "@yarnpkg/plugin-changelog",
|
|
5
|
+
factory: function (require) {
|
|
6
|
+
"use strict";var plugin=(()=>{var z=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var pe=Object.prototype.hasOwnProperty;var N=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,o)=>(typeof require<"u"?require:t)[o]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var he=(e,t)=>{for(var o in t)z(e,o,{get:t[o],enumerable:!0})},de=(e,t,o,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of ge(t))!pe.call(e,i)&&i!==o&&z(e,i,{get:()=>t[i],enumerable:!(s=le(t,i))||s.enumerable});return e};var me=e=>de(z({},"__esModule",{value:!0}),e);var $e={};he($e,{default:()=>Oe});var re=N("@yarnpkg/cli"),M=N("@yarnpkg/core"),f=N("@yarnpkg/fslib"),$=N("clipanion");function W(e,t,o){let s=`## [${t}] - ${o}
|
|
7
|
+
|
|
8
|
+
`;for(let i of e.sections)i.isEmpty||(s+=`### ${i.name}
|
|
9
|
+
`,s+=`${i.content.trim()}
|
|
10
|
+
|
|
11
|
+
`);return s}var ne={heading:1,other:2,list:3};function oe(e){let t=e.trim();if(!t)return"other";let o=t.split(`
|
|
12
|
+
`)[0].trim();return/^#{2,}/.test(o)?"heading":/^[-*+]/.test(o)||/^\d+\./.test(o)?"list":"other"}function fe(e){let t=e.trim();return/^[-*+]/.test(t)||/^\d+\./.test(t)}function Y(e){if(e.length===0)return"";let t=e.map(l=>({content:l.trim(),type:oe(l)}));t.sort((l,h)=>ne[l.type]-ne[h.type]);let o=t.filter(l=>l.type!=="list"),s=t.filter(l=>l.type==="list"),i=[];for(let l of o)i.push(l.content);if(s.length>0){let l=[];for(let h of s){let a=h.content.split(`
|
|
13
|
+
`);for(let r of a)r.trim()&&(fe(r)||/^\s+/.test(r))&&l.push(r)}l.length>0&&i.push(l.join(`
|
|
14
|
+
`))}return i.join(`
|
|
15
|
+
|
|
16
|
+
`)}var se={major:3,minor:2,patch:1};function X(e){if(e.length===0)return{packageName:"",versionType:"patch",sections:[],hasPlaceholders:!1};if(e.length===1)return e[0];let{packageName:t}=e[0],o=e.some(a=>a.hasPlaceholders),s=e.reduce((a,r)=>{let c=se[r.versionType]??0,m=se[a]??0;return c>m?r.versionType:a},"patch"),i=new Map,l=[];for(let a of e)for(let r of a.sections){i.has(r.name)||(i.set(r.name,[]),l.push(r.name));let c=r.content.trim();if(!c)continue;let m=i.get(r.name);m.some(p=>p.trim().toLowerCase()===c.toLowerCase())||m.push(c)}let h=l.map(a=>{let r=i.get(a)??[],c=Y(r);return{name:a,content:c?`${c}
|
|
17
|
+
`:"",isEmpty:!c}});return{packageName:t,versionType:s,sections:h,hasPlaceholders:o}}var n={BREAKING_CHANGES:"\u{1F4A5} Breaking Changes",DEPRECATED:"\u{1F5D1}\uFE0F Deprecated",FEATURES:"\u2728 Features",BUG_FIXES:"\u{1F41B} Bug Fixes",DOCUMENTATION:"\u{1F4DA} Documentation",PERFORMANCE:"\u26A1 Performance",REFACTORING:"\u267B\uFE0F Refactoring",TESTS:"\u{1F9EA} Tests",BUILD:"\u{1F4E6} Build",CI:"\u{1F477} CI",DEPENDENCIES:"\u2B06\uFE0F Dependencies",CHORES:"\u{1F527} Chores"};function G(e,t={}){let o=[];return t.expectedVersionType&&e.versionType!==t.expectedVersionType&&o.push(`Version type mismatch: changelog has "${e.versionType}" but manifest expects "${t.expectedVersionType}". Run 'yarn changelog create --force' to regenerate.`),e.versionType==="major"&&!e.sections.some(i=>i.name===n.BREAKING_CHANGES&&!i.isEmpty)&&o.push(`Major release requires filled "${n.BREAKING_CHANGES}" section`),e.sections.filter(i=>!i.isEmpty).length===0&&o.push("At least one section must have content"),o}function J(e,t){let o=[];return e||o.push(`${t}: Missing package name heading. Expected a heading like "# @furystack/package-name" at the start of the file.`),{isValid:o.length===0,errors:o}}function q(e,t){let o=e.versionType!==t,i=G(e,{expectedVersionType:t}).filter(l=>!l.includes("Version type mismatch"));return{shouldRegenerate:o||i.length>0,hasVersionMismatch:o,contentErrors:i}}var ue="patch",Ee="<!-- PLACEHOLDER:",ye=/<!-- version-type: (\w+) -->/,Ce=/^# (.+)$/m,Pe=/^## (.+)$/;function A(e){let t=e.split(`
|
|
18
|
+
`),s=e.match(ye)?.[1]??ue,l=e.match(Ce)?.[1]??"",h=e.includes(Ee),a=[],r=null;for(let c of t){let m=c.match(Pe);m?(r&&a.push(r),r={name:m[1],content:"",isEmpty:!0}):r&&!c.trim().startsWith("<!--")&&(r.content+=`${c}
|
|
19
|
+
`,c.trim()&&(r.isEmpty=!1))}return r&&a.push(r),{packageName:l,versionType:s,sections:a,hasPlaceholders:h}}var I=".yarn/changelogs",F=".yarn/versions";var Q="0.0.0",Z="# Changelog",_=class extends re.BaseCommand{static paths=[["changelog","apply"]];static usage=$.Command.Usage({description:"Apply changelog entries to package CHANGELOG.md files",details:`
|
|
20
|
+
This command:
|
|
21
|
+
- Reads all changelog drafts from \`.yarn/changelogs/\`
|
|
22
|
+
- Groups entries by package name
|
|
23
|
+
- Prepends new entries to each package's CHANGELOG.md
|
|
24
|
+
- Deletes processed draft files
|
|
25
|
+
`,examples:[["Apply changelogs","yarn changelog apply"]]});verbose=$.Option.Boolean("-v,--verbose",!1,{description:"Show verbose output"});dryRun=$.Option.Boolean("--dry-run",!1,{description:"Show what would be done without making changes"});async execute(){let t=await M.Configuration.find(this.context.cwd,this.context.plugins),{project:o}=await M.Project.find(t,this.context.cwd),s=f.ppath.join(o.cwd,I);if(this.dryRun&&this.context.stdout.write(`[DRY RUN] No changes will be made.
|
|
26
|
+
|
|
27
|
+
`),!await f.xfs.existsPromise(s))return this.context.stdout.write(`No .yarn/changelogs directory found. Nothing to apply.
|
|
28
|
+
`),0;let l=(await f.xfs.readdirPromise(s)).filter(p=>p.endsWith(".md"));if(l.length===0)return this.context.stdout.write(`No changelog drafts found. Nothing to apply.
|
|
29
|
+
`),0;let h=[],a=[];for(let p of l){let d=f.ppath.join(s,p),C=await f.xfs.readFilePromise(d,"utf8"),g=A(C),E=J(g.packageName,p);if(!E.isValid){a.push(...E.errors);continue}h.push({path:d,filename:p,packageName:g.packageName,content:C})}if(a.length>0){this.context.stderr.write(`Validation errors found:
|
|
30
|
+
`);for(let p of a)this.context.stderr.write(` \u2717 ${p}
|
|
31
|
+
`);this.context.stderr.write(`
|
|
32
|
+
Invalid drafts were skipped and not deleted.
|
|
33
|
+
|
|
34
|
+
`)}let r=new Map;for(let p of h){let d=r.get(p.packageName)??[];d.push(p),r.set(p.packageName,d)}let c=new Date().toISOString().split("T")[0],m=0;for(let[p,d]of r){let C=o.workspaces.find(y=>y.manifest.raw.name===p),g,E;if(C)g=C.cwd,E=C.manifest.version??Q;else{let y=p.replace(/^@[^/]+\//,"");g=f.ppath.join(o.cwd,`packages/${y}`);let O=f.ppath.join(g,"package.json");await f.xfs.existsPromise(O)?E=JSON.parse(await f.xfs.readFilePromise(O,"utf8")).version??Q:E=Q}if(!await f.xfs.existsPromise(g))throw new Error(`Package directory not found: ${g}
|
|
35
|
+
Package '${p}' has changelog entries but no workspace directory exists.
|
|
36
|
+
This may indicate the package was deleted or uses a non-standard directory structure.`);let R=f.ppath.join(g,"CHANGELOG.md"),T="";await f.xfs.existsPromise(R)&&(T=await f.xfs.readFilePromise(R,"utf8"));let b=d.map(y=>A(y.content)),k=X(b),P=W(k,E,c),x,S=new RegExp(`^${Z}(?:\\r?\\n)+`);if(T){let y=T.match(S);if(y){let O=y[0].length;x=T.slice(0,O)+P+T.slice(O)}else x=`${Z}
|
|
37
|
+
|
|
38
|
+
${P}${T}`}else x=`${Z}
|
|
39
|
+
|
|
40
|
+
${P}`;if(this.context.stdout.write(`Applying ${d.length} entry(ies) to ${p}
|
|
41
|
+
`),this.dryRun){if(this.verbose){this.context.stdout.write(` Would write to: ${R}
|
|
42
|
+
`);for(let y of d)this.context.stdout.write(` Would delete: ${y.filename}
|
|
43
|
+
`)}}else{await f.xfs.writeFilePromise(R,x);for(let y of d)await f.xfs.unlinkPromise(y.path),this.verbose&&this.context.stdout.write(` Deleted: ${y.filename}
|
|
44
|
+
`)}m+=d.length}let D=this.dryRun?"Would apply":"Applied";return this.context.stdout.write(`
|
|
45
|
+
${D} ${m} changelog entry(ies) to ${r.size} package(s).
|
|
46
|
+
`),a.length>0?1:0}};var ae=N("@yarnpkg/cli"),H=N("@yarnpkg/core"),w=N("@yarnpkg/fslib"),U=N("clipanion");function Ne(e){return e==="patch"||e==="minor"||e==="major"}function V(e,t){let o=[],s=e.split(`
|
|
47
|
+
`),i=!1;for(let l of s){let h=l.trim();if(h==="releases:"){i=!0;continue}if(i&&h){let a=h.match(/^["']?([^"':]+)["']?\s*:\s*(patch|minor|major)\s*$/);if(a){let r=a[1],c=a[2];Ne(c)&&o.push({packageName:r,versionType:c})}}}return{id:Re(t),path:t,releases:o}}function ie(e){return e.replace(/\//g,"-")}function Re(e){return(e.split("/").pop()??"").replace(".yml","")}var Te="Updated dependencies",we=`<!--
|
|
48
|
+
FORMATTING GUIDE:
|
|
49
|
+
|
|
50
|
+
### Detailed Entry (appears first when merging)
|
|
51
|
+
|
|
52
|
+
Use h3 (###) and below for detailed entries with paragraphs, code examples, and lists.
|
|
53
|
+
|
|
54
|
+
### Simple List Items
|
|
55
|
+
|
|
56
|
+
- Simple changes can be added as list items
|
|
57
|
+
- They are collected together at the bottom of each section
|
|
58
|
+
|
|
59
|
+
TIP: When multiple changelog drafts are merged, heading-based entries
|
|
60
|
+
appear before simple list items within each section.
|
|
61
|
+
-->`,xe={[n.BREAKING_CHANGES]:"Describe breaking changes (BREAKING CHANGE:)",[n.DEPRECATED]:"Describe deprecated features. Double-check if they are annotated with a `@deprecated` jsdoc tag.",[n.FEATURES]:"Describe your shiny new features (feat:)",[n.BUG_FIXES]:"Describe the nasty little bugs that has been eradicated (fix:)",[n.DOCUMENTATION]:"Describe documentation changes (docs:)",[n.PERFORMANCE]:"Describe performance improvements (perf:)",[n.REFACTORING]:"Describe code refactoring (refactor:)",[n.TESTS]:"Describe test changes (test:)",[n.BUILD]:"Describe build system changes (build:)",[n.CI]:"Describe CI configuration changes (ci:)",[n.DEPENDENCIES]:"Describe dependency updates (deps:)",[n.CHORES]:"Describe other changes (chore:)"},De="<!-- MIGRATION REQUIRED: Explain how to migrate from the previous version -->",Se=[n.BREAKING_CHANGES,n.DEPRECATED,n.FEATURES,n.BUG_FIXES,n.DOCUMENTATION,n.PERFORMANCE,n.REFACTORING,n.TESTS,n.BUILD,n.CI,n.DEPENDENCIES,n.CHORES],Ae=[n.DEPRECATED,n.FEATURES,n.BUG_FIXES,n.DOCUMENTATION,n.PERFORMANCE,n.REFACTORING,n.TESTS,n.BUILD,n.CI,n.DEPENDENCIES,n.CHORES],ve=[n.FEATURES,n.BUG_FIXES,n.DOCUMENTATION,n.PERFORMANCE,n.REFACTORING,n.TESTS,n.BUILD,n.CI,n.DEPENDENCIES,n.CHORES];function Ie(e,t=!1){let o=xe[e],s=`## ${e}
|
|
62
|
+
<!-- PLACEHOLDER: ${o} -->`;return t&&(s+=`
|
|
63
|
+
${De}`),s}function be(e){return(e==="major"?Se:e==="minor"?Ae:ve).map(o=>{let s=o===n.BREAKING_CHANGES;return Ie(o,s)}).join(`
|
|
64
|
+
|
|
65
|
+
`)}function ee(e,t){let o=be(t);return`<!-- version-type: ${t} -->
|
|
66
|
+
# ${e}
|
|
67
|
+
|
|
68
|
+
${we}
|
|
69
|
+
|
|
70
|
+
${o}
|
|
71
|
+
`}function L(e,t){return`${ie(e)}.${t}.md`}function te(e,t,o){let s=o||Te;return t==="major"?`<!-- version-type: ${t} -->
|
|
72
|
+
# ${e}
|
|
73
|
+
|
|
74
|
+
## ${n.BREAKING_CHANGES}
|
|
75
|
+
- ${s}
|
|
76
|
+
|
|
77
|
+
## ${n.DEPENDENCIES}
|
|
78
|
+
- ${s}
|
|
79
|
+
`:`<!-- version-type: ${t} -->
|
|
80
|
+
# ${e}
|
|
81
|
+
|
|
82
|
+
## ${n.DEPENDENCIES}
|
|
83
|
+
- ${s}
|
|
84
|
+
`}var j=class extends ae.BaseCommand{static paths=[["changelog","check"]];static usage=U.Command.Usage({description:"Validate changelog entries for all version manifests",details:`
|
|
85
|
+
This command validates that:
|
|
86
|
+
- Every release in \`.yarn/versions/*.yml\` has a changelog file
|
|
87
|
+
- Major releases have filled BREAKING CHANGES sections
|
|
88
|
+
- At least one section (Added/Changed/Fixed) has content
|
|
89
|
+
`,examples:[["Validate changelogs","yarn changelog check"]]});verbose=U.Option.Boolean("-v,--verbose",!1,{description:"Show verbose output"});async execute(){let t=await H.Configuration.find(this.context.cwd,this.context.plugins),{project:o}=await H.Project.find(t,this.context.cwd),s=w.ppath.join(o.cwd,F),i=w.ppath.join(o.cwd,I);if(!await w.xfs.existsPromise(s))return this.context.stdout.write(`No .yarn/versions directory found. Nothing to check.
|
|
90
|
+
`),0;let h=(await w.xfs.readdirPromise(s)).filter(c=>c.endsWith(".yml"));if(h.length===0)return this.context.stdout.write(`No version manifests found. Nothing to check.
|
|
91
|
+
`),0;let a=[],r=0;for(let c of h){let m=w.ppath.join(s,c),D=await w.xfs.readFilePromise(m,"utf8"),p=V(D,m);this.verbose&&this.context.stdout.write(`Checking manifest: ${c}
|
|
92
|
+
`);for(let d of p.releases){let C=L(d.packageName,p.id),g=w.ppath.join(i,C);if(!await w.xfs.existsPromise(g)){a.push(`Missing changelog for ${d.packageName} (manifest: ${p.id}). Run 'yarn changelog create' to generate it.`);continue}let E=await w.xfs.readFilePromise(g,"utf8"),R=A(E),T=G(R,{expectedVersionType:d.versionType});if(T.length>0)for(let b of T)a.push(`${d.packageName} (${C}): ${b}`);else this.verbose&&this.context.stdout.write(` \u2713 ${d.packageName}
|
|
93
|
+
`);r++}}if(a.length>0){this.context.stderr.write(`
|
|
94
|
+
Changelog validation failed:
|
|
95
|
+
|
|
96
|
+
`);for(let c of a)this.context.stderr.write(` \u2717 ${c}
|
|
97
|
+
`);return this.context.stderr.write(`
|
|
98
|
+
Found ${a.length} error(s).
|
|
99
|
+
`),1}return this.context.stdout.write(`
|
|
100
|
+
\u2713 All ${r} changelog(s) are valid.
|
|
101
|
+
`),0}};var ce=N("@yarnpkg/cli"),K=N("@yarnpkg/core"),u=N("@yarnpkg/fslib"),v=N("clipanion");var B=class extends ce.BaseCommand{static paths=[["changelog","create"]];static usage=v.Command.Usage({description:"Generate changelog drafts from version manifests",details:`
|
|
102
|
+
This command reads all version manifests in \`.yarn/versions/*.yml\`
|
|
103
|
+
and generates draft changelog files in \`.yarn/changelogs/\`.
|
|
104
|
+
|
|
105
|
+
Each draft includes sections for Added, Changed, and Fixed entries.
|
|
106
|
+
For major/minor releases, additional sections are included.
|
|
107
|
+
|
|
108
|
+
Existing changelog drafts are not overwritten unless --force is used.
|
|
109
|
+
|
|
110
|
+
Use --dependabot to auto-fill changelogs for dependency updates.
|
|
111
|
+
The --message option can provide a custom message (e.g., PR title).
|
|
112
|
+
`,examples:[["Generate changelog drafts","yarn changelog create"],["Regenerate mismatched/invalid changelogs","yarn changelog create --force"],["Generate for Dependabot PR","yarn changelog create --dependabot"],["Generate with custom message",'yarn changelog create --dependabot -m "Bump lodash from 4.17.20 to 4.17.21"']]});verbose=v.Option.Boolean("-v,--verbose",!1,{description:"Show verbose output"});force=v.Option.Boolean("-f,--force",!1,{description:"Regenerate changelogs with mismatched version types or invalid entries"});dependabot=v.Option.Boolean("--dependabot",!1,{description:"Auto-fill changelog for dependency updates (Dependabot PRs)"});message=v.Option.String("-m,--message",{description:"Custom message for the changelog entry (used with --dependabot)"});async execute(){let t=await K.Configuration.find(this.context.cwd,this.context.plugins),{project:o}=await K.Project.find(t,this.context.cwd),s=u.ppath.join(o.cwd,F),i=u.ppath.join(o.cwd,I);if(await u.xfs.mkdirPromise(i,{recursive:!0}),!await u.xfs.existsPromise(s))return this.context.stdout.write(`No .yarn/versions directory found. Nothing to do.
|
|
113
|
+
`),0;let h=(await u.xfs.readdirPromise(s)).filter(D=>D.endsWith(".yml"));if(h.length===0)return this.context.stdout.write(`No version manifests found. Nothing to do.
|
|
114
|
+
`),0;let a=0,r=0,c=0;for(let D of h){let p=u.ppath.join(s,D),d=await u.xfs.readFilePromise(p,"utf8"),C=V(d,p);this.verbose&&this.context.stdout.write(`Processing manifest: ${D}
|
|
115
|
+
`);for(let g of C.releases){let E=L(g.packageName,C.id),R=u.ppath.join(i,E);if(await u.xfs.existsPromise(R)){let b=await u.xfs.readFilePromise(R,"utf8"),k=A(b),P=q(k,g.versionType);if(this.force&&P.shouldRegenerate){let x=this.dependabot?te(g.packageName,g.versionType,this.message):ee(g.packageName,g.versionType);await u.xfs.writeFilePromise(R,x);let S=[];P.hasVersionMismatch&&S.push(`${k.versionType} \u2192 ${g.versionType}`),P.contentErrors.length>0&&S.push(...P.contentErrors),this.context.stdout.write(` Regenerated: ${E} (${S.join(", ")})
|
|
116
|
+
`),r++;continue}if(this.verbose)if(P.shouldRegenerate){let x=[];P.hasVersionMismatch&&x.push(`version mismatch: ${k.versionType} vs ${g.versionType}`),P.contentErrors.length>0&&x.push(...P.contentErrors.map(S=>S.toLowerCase())),this.context.stdout.write(` Skipping ${g.packageName} (${x.join("; ")}, use --force to regenerate)
|
|
117
|
+
`)}else this.context.stdout.write(` Skipping ${g.packageName} (already exists)
|
|
118
|
+
`);c++;continue}let T=this.dependabot?te(g.packageName,g.versionType,this.message):ee(g.packageName,g.versionType);await u.xfs.writeFilePromise(R,T),this.context.stdout.write(` Created: ${E} (${g.versionType})
|
|
119
|
+
`),a++}}let m=[`Created ${a}`];return r>0&&m.push(`regenerated ${r}`),m.push(`skipped ${c}`),this.context.stdout.write(`
|
|
120
|
+
Done! ${m.join(", ")} changelog draft(s).
|
|
121
|
+
`),0}};var ke={commands:[B,j,_]},Oe=ke;return me($e);})();
|
|
122
|
+
return plugin;
|
|
123
|
+
}
|
|
124
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@furystack/yarn-plugin-changelog",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Yarn plugin for managing changelogs from version manifests",
|
|
5
|
+
"license": "GPL-2.0",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"bundles"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public",
|
|
12
|
+
"main": "./bundles/@yarnpkg/plugin-changelog.js"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/furystack/furystack.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"yarn",
|
|
20
|
+
"yarn-plugin",
|
|
21
|
+
"changelog",
|
|
22
|
+
"version-management",
|
|
23
|
+
"monorepo",
|
|
24
|
+
"furystack"
|
|
25
|
+
],
|
|
26
|
+
"author": "Gallay Lajos <gallay.lajos@gmail.com>",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/furystack/furystack/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/furystack/furystack/tree/main/packages/yarn-plugin-changelog",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "yarn build:types && yarn build:plugin",
|
|
33
|
+
"build:types": "tsc -b",
|
|
34
|
+
"build:plugin": "mv bundles/@yarnpkg/plugin-changelog.js bundles/@yarnpkg/plugin-changelog.js.bak 2>/dev/null; node ../../node_modules/@yarnpkg/builder/lib/cli.js build plugin && rm -f bundles/@yarnpkg/plugin-changelog.js.bak || (mv bundles/@yarnpkg/plugin-changelog.js.bak bundles/@yarnpkg/plugin-changelog.js 2>/dev/null; exit 1)"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@yarnpkg/cli": "^4.12.0",
|
|
38
|
+
"@yarnpkg/core": "^4.5.0",
|
|
39
|
+
"@yarnpkg/fslib": "^3.1.4",
|
|
40
|
+
"clipanion": "^4.0.0-rc.4"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^25.0.10",
|
|
44
|
+
"@yarnpkg/builder": "^4.2.3",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"vitest": "^4.0.17"
|
|
47
|
+
}
|
|
48
|
+
}
|