release-suite 2.0.0 → 3.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/.github/workflows/create-and-publish.yml +243 -0
- package/CHANGELOG.md +19 -32
- package/README.md +60 -47
- package/bin/changelog.js +88 -0
- package/bin/dry-run.js +54 -0
- package/bin/release-notes.js +79 -0
- package/bin/tag.js +73 -0
- package/bin/{compute-version.js → version.js} +40 -33
- package/docs/api.md +25 -11
- package/docs/changelog.md +266 -0
- package/docs/ci.md +154 -112
- package/docs/config.md +139 -0
- package/docs/dry-run.md +229 -0
- package/docs/release-notes.md +149 -0
- package/docs/release-process.md +167 -0
- package/docs/tag.md +193 -0
- package/docs/{compute-version.md → version.md} +37 -67
- package/eslint.config.js +1 -1
- package/lib/changelog/generate.js +145 -0
- package/lib/{changelog.js → changelog/helpers.js} +54 -154
- package/lib/changelog/rebuild.js +85 -0
- package/lib/commits.js +15 -5
- package/lib/config.js +51 -0
- package/lib/dry-run.js +79 -0
- package/lib/git.js +17 -0
- package/lib/{release-notes.js → release-notes/generate.js} +37 -28
- package/lib/tag/create.js +91 -0
- package/lib/utils.js +57 -8
- package/lib/{compute-version.js → version/compute.js} +10 -3
- package/package.json +12 -14
- package/release.config.js +10 -0
- package/.github/workflows/create-release-pr.yml +0 -101
- package/.github/workflows/publish-on-merge.yml +0 -95
- package/bin/create-tag.js +0 -110
- package/bin/generate-changelog.js +0 -66
- package/bin/generate-release-notes.js +0 -70
- package/bin/preview.js +0 -75
- package/docs/generate-changelog.md +0 -107
- package/docs/generate-release-notes.md +0 -111
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
name: Create & Publish Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
id-token: write # 🔐 REQUIRED for OIDC
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
packages: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: release-main
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
# ------------------------------------------------------------
|
|
21
|
+
# JOB 1 — CREATE RELEASE PR + TAG DECISION
|
|
22
|
+
# ------------------------------------------------------------
|
|
23
|
+
create:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
timeout-minutes: 15
|
|
26
|
+
env:
|
|
27
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
28
|
+
|
|
29
|
+
# Job-level outputs = contract with publish job
|
|
30
|
+
outputs:
|
|
31
|
+
should_publish: ${{ steps.compute.outputs.should_publish }}
|
|
32
|
+
version: ${{ steps.compute.outputs.version }}
|
|
33
|
+
tagPrefix: ${{ steps.compute.outputs.tagPrefix }}
|
|
34
|
+
tag: ${{ steps.tag.outputs.tag }}
|
|
35
|
+
|
|
36
|
+
# Only run when:
|
|
37
|
+
# - manual dispatch OR
|
|
38
|
+
# - PR merged into main AND it's NOT a release PR
|
|
39
|
+
if: >
|
|
40
|
+
github.event_name == 'workflow_dispatch' ||
|
|
41
|
+
(
|
|
42
|
+
github.event.pull_request.merged == true &&
|
|
43
|
+
!contains(join(github.event.pull_request.labels.*.name, ','), 'release')
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- name: Checkout
|
|
48
|
+
uses: actions/checkout@v4
|
|
49
|
+
with:
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
persist-credentials: true
|
|
52
|
+
|
|
53
|
+
- name: Setup Node
|
|
54
|
+
uses: actions/setup-node@v4
|
|
55
|
+
with:
|
|
56
|
+
node-version: 24
|
|
57
|
+
|
|
58
|
+
- name: Install dependencies
|
|
59
|
+
run: npm ci
|
|
60
|
+
|
|
61
|
+
- name: Install release-suite (self)
|
|
62
|
+
run: npm install .
|
|
63
|
+
|
|
64
|
+
# --------------------------------------------------------
|
|
65
|
+
# Decide if a release should happen
|
|
66
|
+
# --------------------------------------------------------
|
|
67
|
+
- name: Compute next version
|
|
68
|
+
id: compute
|
|
69
|
+
run: |
|
|
70
|
+
set +e
|
|
71
|
+
RESULT=$(node bin/version.js compute)
|
|
72
|
+
STATUS=$?
|
|
73
|
+
VERSION=$(echo "$RESULT" | jq -r '.nextVersion // empty')
|
|
74
|
+
TAG_PREFIX=$(echo "$RESULT" | jq -r '.tagPrefix // ""')
|
|
75
|
+
|
|
76
|
+
echo "$RESULT"
|
|
77
|
+
|
|
78
|
+
if [ "$STATUS" -ne 0 ] || [ -z "$VERSION" ]; then
|
|
79
|
+
echo "should_publish=false" >> $GITHUB_OUTPUT
|
|
80
|
+
else
|
|
81
|
+
echo "should_publish=true" >> $GITHUB_OUTPUT
|
|
82
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
83
|
+
echo "tagPrefix=$TAG_PREFIX" >> $GITHUB_OUTPUT
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# --------------------------------------------------------
|
|
87
|
+
# Prepare release artifacts
|
|
88
|
+
# --------------------------------------------------------
|
|
89
|
+
- name: Bump package.json
|
|
90
|
+
if: steps.compute.outputs.should_publish == 'true'
|
|
91
|
+
run: npm version ${{ steps.compute.outputs.version }} --no-git-tag-version
|
|
92
|
+
|
|
93
|
+
- name: Sync package-lock.json
|
|
94
|
+
if: steps.compute.outputs.should_publish == 'true'
|
|
95
|
+
run: npm install --package-lock-only
|
|
96
|
+
|
|
97
|
+
- name: Build
|
|
98
|
+
run: npm run build --if-present
|
|
99
|
+
|
|
100
|
+
- name: Generate changelog
|
|
101
|
+
if: steps.compute.outputs.should_publish == 'true'
|
|
102
|
+
run: node bin/changelog.js generate
|
|
103
|
+
|
|
104
|
+
# --------------------------------------------------------
|
|
105
|
+
# Stage files (dist is optional)
|
|
106
|
+
# --------------------------------------------------------
|
|
107
|
+
- name: Stage release files
|
|
108
|
+
if: steps.compute.outputs.should_publish == 'true'
|
|
109
|
+
run: |
|
|
110
|
+
git add package.json package-lock.json CHANGELOG.md
|
|
111
|
+
[ -d dist ] && git add dist || true
|
|
112
|
+
git status --short
|
|
113
|
+
|
|
114
|
+
# --------------------------------------------------------
|
|
115
|
+
# Create Release PR
|
|
116
|
+
# --------------------------------------------------------
|
|
117
|
+
- name: Create Release PR
|
|
118
|
+
id: pr
|
|
119
|
+
if: steps.compute.outputs.should_publish == 'true'
|
|
120
|
+
uses: peter-evans/create-pull-request@v6
|
|
121
|
+
with:
|
|
122
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
123
|
+
branch: release/${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }}
|
|
124
|
+
title: ":bricks: chore(release): ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }} [skip ci]"
|
|
125
|
+
commit-message: ":bricks: chore(release): prepare version ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }} [skip ci]"
|
|
126
|
+
labels: release
|
|
127
|
+
delete-branch: true
|
|
128
|
+
assignees: ${{ github.event.pull_request.merged_by.login }}
|
|
129
|
+
body: |
|
|
130
|
+
Automated release PR.
|
|
131
|
+
|
|
132
|
+
- Version bump: ${{ steps.compute.outputs.tagPrefix }}${{ steps.compute.outputs.version }}
|
|
133
|
+
- Updated changelog
|
|
134
|
+
- Build artifacts included if present
|
|
135
|
+
|
|
136
|
+
Merging this PR will trigger publish automatically.
|
|
137
|
+
|
|
138
|
+
# --------------------------------------------------------
|
|
139
|
+
# Merge Release PR (best-effort)
|
|
140
|
+
# --------------------------------------------------------
|
|
141
|
+
- name: Merge Release PR
|
|
142
|
+
if: steps.pr.outputs.pull-request-number != ''
|
|
143
|
+
run: |
|
|
144
|
+
PR=${{ steps.pr.outputs.pull-request-number }}
|
|
145
|
+
echo "Merging PR #$PR"
|
|
146
|
+
if ! gh pr merge "$PR" --squash --admin; then
|
|
147
|
+
echo "Immediate merge failed, attempting to enable auto-merge"
|
|
148
|
+
gh pr merge "$PR" --auto --squash
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
# --------------------------------------------------------
|
|
152
|
+
# Validate merge (soft)
|
|
153
|
+
# --------------------------------------------------------
|
|
154
|
+
- name: Validate release commit
|
|
155
|
+
id: validate
|
|
156
|
+
run: |
|
|
157
|
+
git fetch origin main --depth=1
|
|
158
|
+
git checkout main
|
|
159
|
+
git reset --hard origin/main
|
|
160
|
+
|
|
161
|
+
if git log -1 --pretty=%s | grep -q "^:bricks: chore(release):"; then
|
|
162
|
+
echo "ok=true" >> $GITHUB_OUTPUT
|
|
163
|
+
else
|
|
164
|
+
echo "ok=false" >> $GITHUB_OUTPUT
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# --------------------------------------------------------
|
|
168
|
+
# Create Git tag (soft)
|
|
169
|
+
# --------------------------------------------------------
|
|
170
|
+
- name: Create tag
|
|
171
|
+
id: tag
|
|
172
|
+
if: steps.validate.outputs.ok == 'true'
|
|
173
|
+
run: |
|
|
174
|
+
set +e
|
|
175
|
+
RESULT=$(node bin/tag.js create)
|
|
176
|
+
TAG=$(echo "$RESULT" | jq -r '.tag // empty')
|
|
177
|
+
|
|
178
|
+
echo "$RESULT"
|
|
179
|
+
|
|
180
|
+
if [ -n "$TAG" ]; then
|
|
181
|
+
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
# ------------------------------------------------------------
|
|
185
|
+
# JOB 2 — PUBLISH (only if tag exists)
|
|
186
|
+
# ------------------------------------------------------------
|
|
187
|
+
publish:
|
|
188
|
+
needs: create
|
|
189
|
+
runs-on: ubuntu-latest
|
|
190
|
+
timeout-minutes: 15
|
|
191
|
+
env:
|
|
192
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
193
|
+
|
|
194
|
+
if: needs.create.outputs.tag != ''
|
|
195
|
+
|
|
196
|
+
steps:
|
|
197
|
+
- name: Checkout
|
|
198
|
+
uses: actions/checkout@v4
|
|
199
|
+
with:
|
|
200
|
+
fetch-depth: 0
|
|
201
|
+
persist-credentials: true
|
|
202
|
+
|
|
203
|
+
- name: Setup Node (npm OIDC)
|
|
204
|
+
uses: actions/setup-node@v4
|
|
205
|
+
with:
|
|
206
|
+
node-version: 24
|
|
207
|
+
registry-url: https://registry.npmjs.org
|
|
208
|
+
always-auth: true
|
|
209
|
+
|
|
210
|
+
- name: Install dependencies
|
|
211
|
+
run: npm ci
|
|
212
|
+
|
|
213
|
+
- name: Install release-suite (self)
|
|
214
|
+
run: npm install .
|
|
215
|
+
|
|
216
|
+
# --------------------------------------------------------
|
|
217
|
+
# Publish to npm using Trusted Publishing (OIDC)
|
|
218
|
+
# --------------------------------------------------------
|
|
219
|
+
- name: Publish to npm (Trusted Publishing)
|
|
220
|
+
run: npm publish
|
|
221
|
+
|
|
222
|
+
# --------------------------------------------------------
|
|
223
|
+
# Generate release notes for GitHub Release
|
|
224
|
+
# --------------------------------------------------------
|
|
225
|
+
- name: Generate GitHub Release Notes
|
|
226
|
+
run: node bin/release-notes.js generate
|
|
227
|
+
|
|
228
|
+
# --------------------------------------------------------
|
|
229
|
+
# Create GitHub Release with notes and attach built assets
|
|
230
|
+
# --------------------------------------------------------
|
|
231
|
+
- name: Create GitHub Release
|
|
232
|
+
env:
|
|
233
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
234
|
+
run: |
|
|
235
|
+
ASSETS=()
|
|
236
|
+
if [ -d dist ]; then
|
|
237
|
+
ASSETS=(dist/**)
|
|
238
|
+
fi
|
|
239
|
+
|
|
240
|
+
gh release create "${{ needs.create.outputs.tag }}" \
|
|
241
|
+
--title "${{ needs.create.outputs.tag }}" \
|
|
242
|
+
--notes-file RELEASE_NOTES.md \
|
|
243
|
+
"${ASSETS[@]}"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
The format follows the conventions of [Conventional Commits](https://www.conventionalcommits.org) and semantic versioning (SemVer).
|
|
5
|
+
|
|
6
|
+
## 3.0.0
|
|
7
|
+
|
|
8
|
+
### 💥 Breaking Changes
|
|
9
|
+
- Introduce action-based CLI and reorganize release architecture (#21)
|
|
10
|
+
|
|
11
|
+
### 📚 Documentation
|
|
12
|
+
- Update README.md and ci.md with GitHub CLI usage (#20)
|
|
13
|
+
|
|
14
|
+
### 🤖 CI
|
|
15
|
+
- Simplify GitHub CLI usage and make authentication explicit (#19)
|
|
16
|
+
- Unify release automation into a single resilient workflow (#18)
|
|
17
|
+
|
|
3
18
|
## 2.0.0
|
|
4
19
|
|
|
5
20
|
### 💥 Breaking Changes
|
|
@@ -11,52 +26,24 @@
|
|
|
11
26
|
## 1.0.1
|
|
12
27
|
|
|
13
28
|
### 🐛 Fixes
|
|
14
|
-
|
|
15
29
|
- Harden changelog generation for squash commits (#13)
|
|
16
30
|
|
|
17
|
-
###
|
|
18
|
-
|
|
19
|
-
- Update ci/cd examples with trigger adjustment
|
|
20
|
-
|
|
21
|
-
### 🔁 CI
|
|
22
|
-
|
|
23
|
-
- Adjust trigger in workflow
|
|
31
|
+
### 🤖 CI
|
|
32
|
+
- Harden workflow triggers and merge conditions (#12)
|
|
24
33
|
|
|
25
34
|
## 1.0.0
|
|
26
35
|
|
|
27
36
|
### 💥 Breaking Changes
|
|
28
|
-
|
|
29
37
|
- Harden computeVersion contract and CLI behavior (#4)
|
|
30
38
|
|
|
31
39
|
### 🐛 Fixes
|
|
32
|
-
|
|
33
40
|
- Normalize path resolution for all CLI scripts (#2)
|
|
34
41
|
|
|
35
|
-
###
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
### 🔁 CI
|
|
54
|
-
|
|
42
|
+
### 🤖 CI
|
|
55
43
|
- Harden local script execution and dist resolution (#10)
|
|
56
44
|
- Binary resolution (#3)
|
|
57
45
|
|
|
58
46
|
## 0.1.0
|
|
59
47
|
|
|
60
|
-
###
|
|
61
|
-
|
|
48
|
+
### 📦 Other
|
|
62
49
|
- Init: project created
|
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@ Semantic versioning tools for Git-based projects, providing automated version co
|
|
|
9
9
|
- Automatic version bump based on commit messages
|
|
10
10
|
- Conventional commit parsing (custom prefixes supported)
|
|
11
11
|
- Auto-generated `CHANGELOG.md`
|
|
12
|
-
- Auto-generated `RELEASE_NOTES.md` using GitHub
|
|
13
|
-
- Local
|
|
12
|
+
- Auto-generated `RELEASE_NOTES.md` using GitHub API
|
|
13
|
+
- Local dry-run mode (`CHANGELOG.dry-run.md`, `RELEASE_NOTES.dry-run.md`)
|
|
14
14
|
- CI/CD ready for GitHub Actions
|
|
15
15
|
- No commit rules enforced on the main project
|
|
16
16
|
- Trusted Publishing (OIDC) — no npm tokens required
|
|
@@ -26,78 +26,94 @@ Add to your project's `package.json`:
|
|
|
26
26
|
```json
|
|
27
27
|
{
|
|
28
28
|
"scripts": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"compute
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"release-notes": "rs-generate-release-notes"
|
|
29
|
+
"dry-run": "rs-dry-run create",
|
|
30
|
+
"dry-run:clean": "rs-dry-run remove",
|
|
31
|
+
"version:compute": "rs-version compute",
|
|
32
|
+
"changelog": "rs-changelog generate",
|
|
33
|
+
"release-notes": "rs-release-notes generate",
|
|
34
|
+
"tag:create": "rs-tag create"
|
|
36
35
|
}
|
|
37
36
|
}
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
Generate
|
|
39
|
+
Generate dry-run files without touching your real changelog:
|
|
41
40
|
|
|
42
41
|
```bash
|
|
43
|
-
npm run
|
|
42
|
+
npm run dry-run
|
|
44
43
|
```
|
|
45
44
|
|
|
46
|
-
Remove
|
|
45
|
+
Remove dry-run files:
|
|
47
46
|
|
|
48
47
|
```bash
|
|
49
|
-
npm run
|
|
48
|
+
npm run dry-run:clear
|
|
50
49
|
```
|
|
51
50
|
|
|
51
|
+
## ⚙️ Configuration
|
|
52
|
+
|
|
53
|
+
Release Suite can be configured using a `release.config.js` file.
|
|
54
|
+
|
|
55
|
+
This file controls:
|
|
56
|
+
|
|
57
|
+
- Git tag prefix (`v1.2.3` vs `1.2.3`)
|
|
58
|
+
- Emoji usage in changelog rendering
|
|
59
|
+
|
|
60
|
+
See [`docs/config.md`](docs/config.md) for full documentation.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
52
64
|
## 🖥️ CLI Commands
|
|
53
65
|
|
|
54
|
-
| Command | Description
|
|
55
|
-
| --------------------------- |
|
|
56
|
-
| `rs-compute
|
|
57
|
-
| `rs-generate
|
|
58
|
-
| `rs-
|
|
59
|
-
| `rs-
|
|
60
|
-
| `rs-
|
|
66
|
+
| Command | Description |
|
|
67
|
+
| --------------------------- | ---------------------------------------------------------- |
|
|
68
|
+
| `rs-version compute` | Computes next semantic version based on git commits |
|
|
69
|
+
| `rs-changelog generate` | Generates a new changelog entry for the next release |
|
|
70
|
+
| `rs-changelog rebuild` ⚠️ | Fully rebuilds CHANGELOG.md from git history (Danger Zone) |
|
|
71
|
+
| `rs-release-notes generate` | Generates RELEASE_NOTES.md |
|
|
72
|
+
| `rs-dry-run` | Generates dry-run changelog & release notes |
|
|
73
|
+
| `rs-tag create` | Creates and pushes a git tag |
|
|
61
74
|
|
|
62
75
|
Each command follows a strict and predictable CLI contract (exit codes, stdout, JSON mode).
|
|
63
76
|
|
|
64
77
|
> 💡 **Note about execution**
|
|
65
78
|
>
|
|
79
|
+
> ⚠️ `rs-changelog rebuild` is a destructive operation.
|
|
80
|
+
> Always use `--dry-run` first.
|
|
81
|
+
>
|
|
66
82
|
> - When using these commands via `npm run`, they can be referenced directly (`rs-*`).
|
|
67
83
|
> - In CI/CD environments (e.g. GitHub Actions), always invoke them using `npx`
|
|
68
|
-
> (e.g. `npx rs-generate
|
|
84
|
+
> (e.g. `npx rs-changelog generate`) to ensure proper binary resolution.
|
|
69
85
|
|
|
70
86
|
## 🔁 Release Flow
|
|
71
87
|
|
|
72
|
-
This project
|
|
73
|
-
|
|
88
|
+
This project uses an automated, PR-based release strategy
|
|
89
|
+
designed for safety and traceability.
|
|
74
90
|
|
|
75
|
-
|
|
91
|
+
See [Release Process](docs/release-process.md) for details.
|
|
92
|
+
|
|
93
|
+
### 1️⃣ Create Release PR
|
|
76
94
|
|
|
77
95
|
Triggered when:
|
|
78
96
|
|
|
79
97
|
- A PR is merged into `main`
|
|
98
|
+
- Or the workflow is manually dispatched
|
|
80
99
|
|
|
81
100
|
Actions:
|
|
82
101
|
|
|
83
102
|
- Computes next semantic version
|
|
84
|
-
- Updates `package.json`
|
|
85
|
-
- Generates `CHANGELOG.md`
|
|
103
|
+
- Updates `package.json` and `CHANGELOG.md`
|
|
86
104
|
- Builds the project (if applicable)
|
|
87
|
-
- Opens a
|
|
105
|
+
- Opens and auto-merges a Release PR
|
|
106
|
+
- Creates a Git tag
|
|
88
107
|
|
|
89
108
|
### 2️⃣ Publish Release
|
|
90
109
|
|
|
91
|
-
Triggered
|
|
92
|
-
|
|
93
|
-
- A Release PR (`release/x.y.z`) with `release` label is merged into `main`
|
|
110
|
+
Triggered automatically after the Release PR merge (only if tag exists).
|
|
94
111
|
|
|
95
112
|
Actions:
|
|
96
113
|
|
|
97
|
-
- Creates a Git tag
|
|
98
114
|
- Publishes to npm using **Trusted Publishing (OIDC)**
|
|
99
115
|
- Generates GitHub Release Notes
|
|
100
|
-
- Uploads build artifacts (`dist/**`)
|
|
116
|
+
- Uploads build artifacts (`dist/**` if present)
|
|
101
117
|
|
|
102
118
|
---
|
|
103
119
|
|
|
@@ -105,10 +121,13 @@ Actions:
|
|
|
105
121
|
|
|
106
122
|
```mermaid
|
|
107
123
|
flowchart TD
|
|
108
|
-
A[Feature / Fix PR] -->|
|
|
109
|
-
B -->|
|
|
110
|
-
C -->|
|
|
111
|
-
D -->|
|
|
124
|
+
A[Feature / Fix PR] -->|merge| B[main]
|
|
125
|
+
B -->|workflow trigger| C[Compute Version]
|
|
126
|
+
C -->|release needed| D[Create Release PR]
|
|
127
|
+
D -->|auto-merge| E[main updated]
|
|
128
|
+
E -->|create tag| F[Git Tag]
|
|
129
|
+
F -->|publish| G[npm Registry]
|
|
130
|
+
F -->|release| H[GitHub Release]
|
|
112
131
|
```
|
|
113
132
|
|
|
114
133
|
✔️ Fully automated releases
|
|
@@ -123,12 +142,6 @@ flowchart TD
|
|
|
123
142
|
|
|
124
143
|
This project is designed to be used in automated pipelines.
|
|
125
144
|
|
|
126
|
-
Typical flow:
|
|
127
|
-
|
|
128
|
-
1. Create a Release PR (compute version, changelog, build)
|
|
129
|
-
2. Review and merge the Release PR into `main`
|
|
130
|
-
3. Publish the release (tag, npm, GitHub Release)
|
|
131
|
-
|
|
132
145
|
👉 See full examples in [`docs/ci.md`](./docs/ci.md)
|
|
133
146
|
|
|
134
147
|
## 📦 Publishing to npm (Trusted Publishing)
|
|
@@ -154,11 +167,11 @@ are **not available via npm or npx**, since they are not installed as a dependen
|
|
|
154
167
|
In this case, run the scripts directly with Node.js:
|
|
155
168
|
|
|
156
169
|
```bash
|
|
157
|
-
node bin/
|
|
158
|
-
node bin/
|
|
159
|
-
node bin/
|
|
160
|
-
node bin/
|
|
161
|
-
node bin/
|
|
170
|
+
node bin/version.js compute
|
|
171
|
+
node bin/changelog.js generate
|
|
172
|
+
node bin/release-notes.js generate
|
|
173
|
+
node bin/dry-run.js create
|
|
174
|
+
node bin/tag.js create
|
|
162
175
|
```
|
|
163
176
|
|
|
164
177
|
To test the CLI as a real consumer, you can use:
|
package/bin/changelog.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { generateChangelog } from "../lib/changelog/generate.js";
|
|
4
|
+
import { rebuildChangelog } from "../lib/changelog/rebuild.js";
|
|
5
|
+
import { parseFlags } from "../lib/utils.js";
|
|
6
|
+
|
|
7
|
+
/* ===========================
|
|
8
|
+
* CLI
|
|
9
|
+
* =========================== */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Main CLI entrypoint for changelog (generate/rebuild).
|
|
13
|
+
*
|
|
14
|
+
* Behavior:
|
|
15
|
+
* - Calls generateChangelog({ dryRun: flags.dryRun }).
|
|
16
|
+
* - Calls rebuildChangelog({ dryRun: flags.dryRun }).
|
|
17
|
+
*
|
|
18
|
+
* Exit codes (contract):
|
|
19
|
+
* - 0 -> changelog generated/rebuilt
|
|
20
|
+
* - 10 -> no release/no-tags
|
|
21
|
+
* - 2 -> no-commits/no-valid-commits
|
|
22
|
+
* - 11 -> already exists
|
|
23
|
+
* - 1 -> unexpected error
|
|
24
|
+
*/
|
|
25
|
+
function main() {
|
|
26
|
+
const action = process.argv[2];
|
|
27
|
+
const flags = parseFlags(process.argv.slice(3), {
|
|
28
|
+
dryRun: "--dry-run",
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!["generate", "rebuild"].includes(action)) {
|
|
32
|
+
console.error(
|
|
33
|
+
JSON.stringify(
|
|
34
|
+
{
|
|
35
|
+
"error": "invalid-usage",
|
|
36
|
+
"message": "Invalid action. Usage: npx rs-changelog [generate|rebuild]"
|
|
37
|
+
},
|
|
38
|
+
null,
|
|
39
|
+
2
|
|
40
|
+
)
|
|
41
|
+
);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
|
|
47
|
+
let result = null;
|
|
48
|
+
if (action === "generate") {
|
|
49
|
+
result = generateChangelog({ dryRun: flags.dryRun });
|
|
50
|
+
}
|
|
51
|
+
else if (action === "rebuild") {
|
|
52
|
+
result = rebuildChangelog({ dryRun: flags.dryRun });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(JSON.stringify(result, null, 2));
|
|
56
|
+
|
|
57
|
+
if (action === "generate") {
|
|
58
|
+
if (result.generated) process.exit(0);
|
|
59
|
+
if (result.reason === "no-release") process.exit(10);
|
|
60
|
+
if (result.reason === "no-commits") process.exit(2);
|
|
61
|
+
if (result.reason === "already-exists") process.exit(11);
|
|
62
|
+
}
|
|
63
|
+
else if (action === "rebuild") {
|
|
64
|
+
if (result.rebuilt) process.exit(0);
|
|
65
|
+
if (result.reason === "no-tags") process.exit(10);
|
|
66
|
+
if (result.reason === "no-valid-commits") process.exit(2);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
process.exit(1);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
console.error(
|
|
72
|
+
JSON.stringify(
|
|
73
|
+
{
|
|
74
|
+
error: "unexpected-error",
|
|
75
|
+
message: err.message || String(err),
|
|
76
|
+
},
|
|
77
|
+
null,
|
|
78
|
+
2
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
86
|
+
if (process.argv[1] === __filename) {
|
|
87
|
+
main();
|
|
88
|
+
}
|
package/bin/dry-run.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dryRun } from "../lib/dry-run.js";
|
|
4
|
+
|
|
5
|
+
/* ===========================
|
|
6
|
+
* CLI
|
|
7
|
+
* =========================== */
|
|
8
|
+
|
|
9
|
+
function main() {
|
|
10
|
+
const action = process.argv[2];
|
|
11
|
+
|
|
12
|
+
if (!["create", "remove"].includes(action)) {
|
|
13
|
+
console.error(
|
|
14
|
+
JSON.stringify(
|
|
15
|
+
{
|
|
16
|
+
"error": "invalid-usage",
|
|
17
|
+
"message": "Invalid action. Usage: npx rs-dry-run [create|remove]"
|
|
18
|
+
},
|
|
19
|
+
null,
|
|
20
|
+
2
|
|
21
|
+
)
|
|
22
|
+
);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const result = dryRun({ action });
|
|
28
|
+
|
|
29
|
+
console.log(JSON.stringify(result, null, 2));
|
|
30
|
+
|
|
31
|
+
if (action === "create" && result.generated === false) {
|
|
32
|
+
process.exit(10);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
process.exit(0);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(
|
|
38
|
+
JSON.stringify(
|
|
39
|
+
{
|
|
40
|
+
error: "unexpected-error",
|
|
41
|
+
message: err.message || String(err),
|
|
42
|
+
},
|
|
43
|
+
null,
|
|
44
|
+
2
|
|
45
|
+
)
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
52
|
+
if (process.argv[1] === __filename) {
|
|
53
|
+
main();
|
|
54
|
+
}
|