release-suite 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/create-release-pr.yml +94 -0
- package/.github/workflows/publish-on-merge.yml +73 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/bin/compute-version.js +132 -0
- package/bin/create-tag.js +65 -0
- package/bin/generate-changelog.js +192 -0
- package/bin/generate-release-notes.js +123 -0
- package/bin/preview.js +47 -0
- package/eslint.config.js +47 -0
- package/package.json +62 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
name: Create Release PR
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
pull-requests: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: release-pr-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
release-pr:
|
|
19
|
+
# Only runs when:
|
|
20
|
+
# - The PR has been merged
|
|
21
|
+
# - And the branch is NOT release/v/*
|
|
22
|
+
if: >
|
|
23
|
+
github.event.pull_request.merged == true &&
|
|
24
|
+
!startsWith(github.event.pull_request.head.ref, 'release/v/')
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
with:
|
|
30
|
+
fetch-depth: 0
|
|
31
|
+
persist-credentials: false
|
|
32
|
+
|
|
33
|
+
# Avoids loops: ignores commits created by bots
|
|
34
|
+
- name: Skip if last commit is from bot
|
|
35
|
+
run: |
|
|
36
|
+
last_author="$(git log -1 --pretty=format:'%an')"
|
|
37
|
+
echo "Last author: $last_author"
|
|
38
|
+
if [[ "$last_author" == *"github-actions"* ]]; then
|
|
39
|
+
echo "Commit created by bot. Canceling workflow."
|
|
40
|
+
exit 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
- uses: actions/setup-node@v4
|
|
44
|
+
with:
|
|
45
|
+
node-version: 24
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: npm ci
|
|
49
|
+
|
|
50
|
+
- name: Install release-suite locally (self usage)
|
|
51
|
+
run: npm install .
|
|
52
|
+
|
|
53
|
+
# Compute next version to release
|
|
54
|
+
- name: Compute next version
|
|
55
|
+
id: semver
|
|
56
|
+
run: |
|
|
57
|
+
VERSION=$(rs-compute-version || echo "")
|
|
58
|
+
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
59
|
+
|
|
60
|
+
- name: Stop if no release needed
|
|
61
|
+
if: steps.semver.outputs.version == ''
|
|
62
|
+
run: exit 0
|
|
63
|
+
|
|
64
|
+
- name: Bump package.json
|
|
65
|
+
run: npm version ${{ steps.semver.outputs.version }} --no-git-tag-version
|
|
66
|
+
|
|
67
|
+
- name: Build
|
|
68
|
+
run: npm run build --if-present
|
|
69
|
+
|
|
70
|
+
- name: Generate changelog
|
|
71
|
+
run: rs-generate-changelog
|
|
72
|
+
|
|
73
|
+
# Automatically create branches, commits, and PRs with peter-evans
|
|
74
|
+
- name: Create Release PR
|
|
75
|
+
uses: peter-evans/create-pull-request@v6
|
|
76
|
+
with:
|
|
77
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
78
|
+
commit-message: ":bricks: chore(release): prepare version ${{ steps.semver.outputs.version }} [skip ci]"
|
|
79
|
+
branch: release/v/${{ steps.semver.outputs.version }}
|
|
80
|
+
title: ":bricks: chore(release): ${{ steps.semver.outputs.version }}"
|
|
81
|
+
body: |
|
|
82
|
+
This PR contains the release artifacts:
|
|
83
|
+
|
|
84
|
+
- Updated `package.json` and `package-lock.json` with version ${{ steps.semver.outputs.version }}
|
|
85
|
+
- Updated `CHANGELOG.md` with latest changes
|
|
86
|
+
- Generated `/dist` directory with build files (if applicable)
|
|
87
|
+
|
|
88
|
+
Upon approving & merging, the publish will run automatically.
|
|
89
|
+
labels: |
|
|
90
|
+
release
|
|
91
|
+
add-paths: |
|
|
92
|
+
package.json
|
|
93
|
+
CHANGELOG.md
|
|
94
|
+
dist/**
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Publish Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
id-token: write # ๐ REQUIRED for OIDC
|
|
12
|
+
packages: write
|
|
13
|
+
pull-requests: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: publish-release
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
publish:
|
|
21
|
+
# Only runs when:
|
|
22
|
+
# - The PR has been merged
|
|
23
|
+
# - And the branch is release/v/*
|
|
24
|
+
if: >
|
|
25
|
+
github.event.pull_request.merged == true &&
|
|
26
|
+
startsWith(github.event.pull_request.head.ref, 'release/v/')
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: Checkout repository
|
|
31
|
+
uses: actions/checkout@v4
|
|
32
|
+
with:
|
|
33
|
+
fetch-depth: 0
|
|
34
|
+
|
|
35
|
+
- name: Setup Node.js
|
|
36
|
+
uses: actions/setup-node@v4
|
|
37
|
+
with:
|
|
38
|
+
node-version: 24
|
|
39
|
+
registry-url: https://registry.npmjs.org/
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: npm ci
|
|
43
|
+
|
|
44
|
+
- name: Install release-suite locally (self usage)
|
|
45
|
+
run: npm install .
|
|
46
|
+
|
|
47
|
+
- name: Build
|
|
48
|
+
run: npm run build --if-present
|
|
49
|
+
|
|
50
|
+
- name: Create Git Tag
|
|
51
|
+
run: rs-create-tag
|
|
52
|
+
|
|
53
|
+
# Publish to npm using Trusted Publishing (OIDC)
|
|
54
|
+
- name: Publish to npm (Trusted Publishing)
|
|
55
|
+
run: npm publish
|
|
56
|
+
|
|
57
|
+
# Generate release notes for GitHub Release
|
|
58
|
+
- name: Generate GitHub Release Notes
|
|
59
|
+
run: rs-generate-release-notes
|
|
60
|
+
env:
|
|
61
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
62
|
+
|
|
63
|
+
# Create GitHub Release with notes and attach built assets
|
|
64
|
+
- name: Create GitHub Release + Tag
|
|
65
|
+
env:
|
|
66
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
67
|
+
run: |
|
|
68
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
69
|
+
|
|
70
|
+
gh release create "$VERSION" \
|
|
71
|
+
--title "$VERSION" \
|
|
72
|
+
--notes-file RELEASE_NOTES.md \
|
|
73
|
+
./dist/* || true
|
package/.prettierrc
ADDED
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jonas Souza
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# ๐ Release Suite
|
|
2
|
+
|
|
3
|
+
Semantic versioning tools for Git-based projects, providing automated version computation, changelog generation and release notes creation.
|
|
4
|
+
|
|
5
|
+
## ๐ Features
|
|
6
|
+
|
|
7
|
+
- Automatic version bump based on commit messages
|
|
8
|
+
- Conventional commit parsing (custom prefixes supported)
|
|
9
|
+
- Auto-generated `CHANGELOG.md`
|
|
10
|
+
- Auto-generated `RELEASE_NOTES.md` using GitHub CLI (gh)
|
|
11
|
+
- Local preview mode (`CHANGELOG.preview.md`, `RELEASE_NOTES.preview.md`)
|
|
12
|
+
- CI/CD ready for GitHub Actions
|
|
13
|
+
- No commit rules enforced on the main project
|
|
14
|
+
- Trusted Publishing (OIDC) โ no npm tokens required
|
|
15
|
+
|
|
16
|
+
## โ๏ธ Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install release-suite --save-dev
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## ๐ฅ๏ธ CLI Commands
|
|
23
|
+
|
|
24
|
+
| Command | Description |
|
|
25
|
+
| --------------------------- | --------------------------------------------------------- |
|
|
26
|
+
| `rs-compute-version` | Computes next semantic version based on git commits |
|
|
27
|
+
| `rs-generate-changelog` | Generates `CHANGELOG.md` |
|
|
28
|
+
| `rs-generate-release-notes` | Generates `RELEASE_NOTES.md` using GitHub PRs |
|
|
29
|
+
| `rs-preview` | Generates preview changelog & release notes |
|
|
30
|
+
| `rs-create-tag` | Create and push a git tag based on `package.json` version |
|
|
31
|
+
|
|
32
|
+
## ๐ง Usage
|
|
33
|
+
|
|
34
|
+
Add to your project's `package.json`:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"scripts": {
|
|
39
|
+
"version:compute": "rs-compute-version",
|
|
40
|
+
"changelog": "rs-generate-changelog",
|
|
41
|
+
"release:notes": "rs-generate-release-notes",
|
|
42
|
+
"preview": "rs-preview create",
|
|
43
|
+
"preview:clear": "rs-preview remove",
|
|
44
|
+
"create-tag": "rs-create-tag",
|
|
45
|
+
"create-tag:compute": "rs-create-tag --compute",
|
|
46
|
+
"create-tag:dry": "rs-create-tag --dry-run"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## ๐ค CI/CD Usage (GitHub Actions)
|
|
52
|
+
|
|
53
|
+
This project is designed to be used in automated pipelines.
|
|
54
|
+
|
|
55
|
+
Typical flow:
|
|
56
|
+
|
|
57
|
+
1. Create a Release PR (compute version, changelog, build)
|
|
58
|
+
2. Review and merge the Release PR into `main`
|
|
59
|
+
3. Publish the release (tag, npm, GitHub Release)
|
|
60
|
+
|
|
61
|
+
Example workflows:
|
|
62
|
+
- create-release-pr.yml
|
|
63
|
+
- publish-on-merge.yml
|
|
64
|
+
|
|
65
|
+
## ๐ Release Flow
|
|
66
|
+
|
|
67
|
+
This project follows a **two-step release strategy** designed for safety,
|
|
68
|
+
automation and reusability.
|
|
69
|
+
|
|
70
|
+
### 1๏ธโฃ Prepare Release (Create Release PR)
|
|
71
|
+
|
|
72
|
+
Triggered when:
|
|
73
|
+
|
|
74
|
+
- A PR is merged into `main`
|
|
75
|
+
|
|
76
|
+
Actions:
|
|
77
|
+
|
|
78
|
+
- Computes next semantic version
|
|
79
|
+
- Updates `package.json`
|
|
80
|
+
- Generates `CHANGELOG.md`
|
|
81
|
+
- Builds the project (if applicable)
|
|
82
|
+
- Opens a **Release PR** (`release/v/x.y.z`)
|
|
83
|
+
|
|
84
|
+
### 2๏ธโฃ Publish Release
|
|
85
|
+
|
|
86
|
+
Triggered when:
|
|
87
|
+
|
|
88
|
+
- A Release PR (`release/v/*`) is merged into `main`
|
|
89
|
+
|
|
90
|
+
Actions:
|
|
91
|
+
|
|
92
|
+
- Creates a Git tag
|
|
93
|
+
- Publishes to npm using **Trusted Publishing (OIDC)**
|
|
94
|
+
- Generates GitHub Release Notes
|
|
95
|
+
- Uploads build artifacts (`dist/**`)
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### ๐ Flow Diagram
|
|
100
|
+
|
|
101
|
+
```mermaid
|
|
102
|
+
flowchart TD
|
|
103
|
+
A[Feature / Fix PR] -->|Merge| B[main]
|
|
104
|
+
B -->|create-release-pr.yml| C[Create Release PR]
|
|
105
|
+
C -->|release/v/x.y.z| D[Review & Merge]
|
|
106
|
+
D -->|publish-on-merge.yml| E[Publish Release]
|
|
107
|
+
E --> F[npm Publish]
|
|
108
|
+
E --> G[GitHub Release]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
โ๏ธ Fully automated releases
|
|
112
|
+
โ๏ธ No npm tokens or secrets required (OIDC)
|
|
113
|
+
โ๏ธ No release loops
|
|
114
|
+
โ๏ธ Safe for concurrent merges
|
|
115
|
+
โ๏ธ Reusable in any project
|
|
116
|
+
|
|
117
|
+
## ๐ฆ Publishing to npm (Trusted Publishing)
|
|
118
|
+
|
|
119
|
+
This project uses **npm [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) with GitHub Actions (OIDC)**.
|
|
120
|
+
|
|
121
|
+
- No npm tokens or secrets are required
|
|
122
|
+
- Publishing is handled entirely by GitHub Actions
|
|
123
|
+
- Triggered automatically when a Release PR is merged into `main`
|
|
124
|
+
|
|
125
|
+
## ๐ Preview Mode
|
|
126
|
+
|
|
127
|
+
Generate preview files without touching your real changelog:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm run preview
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Remove previews:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm run preview:clear
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## ๐ Development (Maintainers)
|
|
140
|
+
|
|
141
|
+
When working inside the `release-suite` repository itself, the CLI binaries
|
|
142
|
+
are **not available via npm or npx**, since they are not installed as a dependency.
|
|
143
|
+
|
|
144
|
+
In this case, run the scripts directly with Node.js:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
node bin/compute-version.js
|
|
148
|
+
node bin/generate-changelog.js
|
|
149
|
+
node bin/generate-release-notes.js
|
|
150
|
+
node bin/preview.js create
|
|
151
|
+
node bin/create-tag.js
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
To test the CLI as a real consumer, you can use:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
npm link
|
|
158
|
+
# or
|
|
159
|
+
npm install ../release-suite
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## ๐ License
|
|
163
|
+
|
|
164
|
+
This project is licensed under the [MIT License](./LICENSE).
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## โจ Author
|
|
169
|
+
|
|
170
|
+
<table>
|
|
171
|
+
<tr>
|
|
172
|
+
<td align="center">
|
|
173
|
+
<a href="https://jonasmzsouza.github.io/">
|
|
174
|
+
<img style="border-radius: 50%;" src="https://avatars.githubusercontent.com/u/61324433?v=4" width="100px;" alt=""/>
|
|
175
|
+
<br />
|
|
176
|
+
<sub><b>Jonas Souza</b></sub>
|
|
177
|
+
</a>
|
|
178
|
+
</td>
|
|
179
|
+
</tr>
|
|
180
|
+
</table>
|
|
181
|
+
|
|
182
|
+
๐ผ [LinkedIn](https://linkedin.com/in/jonasmzsouza)
|
|
183
|
+
๐ป [GitHub](https://github.com/jonasmzsouza)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
function run(cmd) {
|
|
6
|
+
return execSync(cmd, { encoding: "utf8" }).trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const isPreview = process.env.PREVIEW_MODE === "true";
|
|
10
|
+
|
|
11
|
+
function getPackageVersion() {
|
|
12
|
+
try {
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
14
|
+
return pkg.version;
|
|
15
|
+
} catch {
|
|
16
|
+
return "0.0.0";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getLastTag() {
|
|
21
|
+
try {
|
|
22
|
+
const tag = run("git describe --tags --abbrev=0");
|
|
23
|
+
return tag.replace(/^v/, "");
|
|
24
|
+
} catch {
|
|
25
|
+
const pkgVersion = getPackageVersion();
|
|
26
|
+
if (isPreview) {
|
|
27
|
+
console.log(
|
|
28
|
+
`โ No previous tag found. Using package.json version (${pkgVersion}) as base.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return pkgVersion;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getCommitsSince(version) {
|
|
36
|
+
try {
|
|
37
|
+
// If version came from package.json (no tag), include all commits
|
|
38
|
+
const hasTag = (() => {
|
|
39
|
+
try {
|
|
40
|
+
run("git describe --tags --abbrev=0");
|
|
41
|
+
return true;
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
})();
|
|
46
|
+
|
|
47
|
+
const range = hasTag ? `${version}..HEAD` : "HEAD";
|
|
48
|
+
|
|
49
|
+
return run(
|
|
50
|
+
`git log ${range} --pretty=format:%H%x1f%s%x1f%b`
|
|
51
|
+
)
|
|
52
|
+
.split("\n")
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
} catch {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseCommitLine(line) {
|
|
60
|
+
const [hash, subject = "", body = ""] = line.split("\x1f");
|
|
61
|
+
return { hash, subject, body };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function detectType(subject, body) {
|
|
65
|
+
const re =
|
|
66
|
+
/^(:\S+: )?(feat|fix|refactor|docs|chore|style|test|build|perf|ci|raw|cleanup|remove)(\(.+\))?(!)?:/i;
|
|
67
|
+
|
|
68
|
+
const m = subject.match(re);
|
|
69
|
+
const isBreaking =
|
|
70
|
+
/BREAKING CHANGE/i.test(body) || (m && m[4] === "!");
|
|
71
|
+
|
|
72
|
+
if (isBreaking) return "major";
|
|
73
|
+
if (m) {
|
|
74
|
+
const t = m[2].toLowerCase();
|
|
75
|
+
if (t === "feat") return "minor";
|
|
76
|
+
if (t === "fix") return "patch";
|
|
77
|
+
}
|
|
78
|
+
return "none";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function bumpVersion(type, version) {
|
|
82
|
+
const [major, minor, patch] = version
|
|
83
|
+
.split(".")
|
|
84
|
+
.map(n => parseInt(n, 10) || 0);
|
|
85
|
+
|
|
86
|
+
if (type === "major") return `${major + 1}.0.0`;
|
|
87
|
+
if (type === "minor") return `${major}.${minor + 1}.0`;
|
|
88
|
+
return `${major}.${minor}.${patch + 1}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function main() {
|
|
92
|
+
const baseVersion = getLastTag();
|
|
93
|
+
const commits = getCommitsSince(baseVersion).map(parseCommitLine);
|
|
94
|
+
|
|
95
|
+
if (!commits.length) process.exit(0);
|
|
96
|
+
|
|
97
|
+
let bump = null;
|
|
98
|
+
|
|
99
|
+
for (const c of commits) {
|
|
100
|
+
const t = detectType(c.subject, c.body);
|
|
101
|
+
if (t === "major") {
|
|
102
|
+
bump = "major";
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
if (t === "minor" && bump !== "major") bump = "minor";
|
|
106
|
+
if (t === "patch" && !bump) bump = "patch";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!bump) {
|
|
110
|
+
if (isPreview) {
|
|
111
|
+
console.log("Mode: PREVIEW");
|
|
112
|
+
console.log("Base version:", baseVersion);
|
|
113
|
+
console.log("Commits analyzed:", commits.length);
|
|
114
|
+
console.log("No version bump detected.");
|
|
115
|
+
}
|
|
116
|
+
process.exit(0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const nextVersion = bumpVersion(bump, baseVersion);
|
|
120
|
+
|
|
121
|
+
if (isPreview) {
|
|
122
|
+
console.log("Mode: PREVIEW");
|
|
123
|
+
console.log("Base version:", baseVersion);
|
|
124
|
+
console.log("Commits analyzed:", commits.length);
|
|
125
|
+
console.log("Highest bump detected:", bump);
|
|
126
|
+
console.log("Next version:", nextVersion);
|
|
127
|
+
} else {
|
|
128
|
+
console.log(nextVersion);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
main();
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
function run(cmd, silent = false) {
|
|
6
|
+
return execSync(cmd, {
|
|
7
|
+
stdio: silent ? "pipe" : "inherit",
|
|
8
|
+
encoding: "utf8",
|
|
9
|
+
}).trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const args = process.argv.slice(2);
|
|
13
|
+
const DRY_RUN = args.includes("--dry-run");
|
|
14
|
+
const USE_COMPUTED = args.includes("--compute");
|
|
15
|
+
|
|
16
|
+
let version;
|
|
17
|
+
|
|
18
|
+
if (USE_COMPUTED) {
|
|
19
|
+
console.log("๐ข Computing version dynamically...");
|
|
20
|
+
try {
|
|
21
|
+
version = run("node bin/compute-version.js", true);
|
|
22
|
+
} catch {
|
|
23
|
+
console.error("โ Failed to compute version.");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!version) {
|
|
28
|
+
console.log("โน No version bump detected. Skipping tag creation.");
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
console.log("๐ฆ Using version from package.json...");
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
35
|
+
version = pkg.version;
|
|
36
|
+
} catch {
|
|
37
|
+
console.error("โ Failed to read package.json version.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const tag = version;
|
|
43
|
+
|
|
44
|
+
console.log(`๐ Release version: ${tag}`);
|
|
45
|
+
|
|
46
|
+
// check if tag exists
|
|
47
|
+
try {
|
|
48
|
+
run(`git rev-parse ${tag}`, true);
|
|
49
|
+
console.error(`โ Tag ${tag} already exists.`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
} catch {
|
|
52
|
+
// OK
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (DRY_RUN) {
|
|
56
|
+
console.log("๐งช Dry-run mode enabled.");
|
|
57
|
+
console.log(`Would create and push tag: ${tag}`);
|
|
58
|
+
console.log(`VERSION=${tag}`);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
run(`git tag ${tag}`);
|
|
63
|
+
run(`git push origin ${tag}`);
|
|
64
|
+
|
|
65
|
+
console.log(`โ Tag ${tag} created and pushed`);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
function run(cmd) {
|
|
6
|
+
return execSync(cmd, { encoding: "utf8" }).trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getAllTags() {
|
|
10
|
+
try {
|
|
11
|
+
return run("git tag --sort=-creatordate")
|
|
12
|
+
.split("\n")
|
|
13
|
+
.filter(Boolean);
|
|
14
|
+
} catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getCommitsBetween(from, to) {
|
|
20
|
+
const range = from ? `${from}..${to}` : to;
|
|
21
|
+
try {
|
|
22
|
+
return run(`git log ${range} --pretty=format:%H%x1f%s%x1f%b`)
|
|
23
|
+
.split("\n")
|
|
24
|
+
.filter(Boolean);
|
|
25
|
+
} catch {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseCommit(line) {
|
|
31
|
+
const [hash, subject = "", body = ""] = line.split("\x1f");
|
|
32
|
+
return { hash, subject: subject.trim(), body: body.trim() };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function cleanSubject(subject) {
|
|
36
|
+
let s = subject.replace(/^(:\S+: )?/, "");
|
|
37
|
+
s = s.replace(
|
|
38
|
+
/^(feat|fix|refactor|docs|chore|style|test|build|perf|ci|raw|cleanup|remove)(\(.+\))?(!)?:\s*/i,
|
|
39
|
+
""
|
|
40
|
+
);
|
|
41
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function categorize(commits) {
|
|
45
|
+
const buckets = {
|
|
46
|
+
breaking: [],
|
|
47
|
+
feat: [],
|
|
48
|
+
fix: [],
|
|
49
|
+
refactor: [],
|
|
50
|
+
chore: [],
|
|
51
|
+
docs: [],
|
|
52
|
+
style: [],
|
|
53
|
+
test: [],
|
|
54
|
+
build: [],
|
|
55
|
+
perf: [],
|
|
56
|
+
ci: [],
|
|
57
|
+
raw: [],
|
|
58
|
+
cleanup: [],
|
|
59
|
+
remove: [],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const reType =
|
|
63
|
+
/^(:\S+: )?(feat|fix|refactor|docs|chore|style|test|build|perf|ci|raw|cleanup|remove)(\(.+\))?(!)?:/i;
|
|
64
|
+
|
|
65
|
+
for (const c of commits) {
|
|
66
|
+
const { subject, body } = c;
|
|
67
|
+
|
|
68
|
+
if (/BREAKING CHANGE/i.test(body) || /!:/i.test(subject)) {
|
|
69
|
+
buckets.breaking.push({ desc: cleanSubject(subject), hash: c.hash });
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const match = subject.match(reType);
|
|
74
|
+
const desc = cleanSubject(subject);
|
|
75
|
+
|
|
76
|
+
if (!match) {
|
|
77
|
+
buckets.chore.push({ desc: desc || subject, hash: c.hash });
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const type = match[2].toLowerCase();
|
|
82
|
+
(buckets[type] || buckets.chore).push({ desc, hash: c.hash });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return buckets;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildSection(version, buckets) {
|
|
89
|
+
const out = [];
|
|
90
|
+
out.push(`## ${version}\n`);
|
|
91
|
+
|
|
92
|
+
const sections = [
|
|
93
|
+
["breaking", "### ๐ฅ Breaking Changes"],
|
|
94
|
+
["feat", "### โจ Features"],
|
|
95
|
+
["fix", "### ๐ Fixes"],
|
|
96
|
+
["refactor", "### โ๏ธ Refactor"],
|
|
97
|
+
["chore", "### ๐ง Chore"],
|
|
98
|
+
["docs", "### ๐ Docs"],
|
|
99
|
+
["style", "### ๐จ Style"],
|
|
100
|
+
["test", "### ๐งช Tests"],
|
|
101
|
+
["build", "### ๐ Build"],
|
|
102
|
+
["perf", "### โก Performance"],
|
|
103
|
+
["ci", "### ๐ CI"],
|
|
104
|
+
["raw", "### ๐ Raw"],
|
|
105
|
+
["cleanup", "### ๐งน Cleanup"],
|
|
106
|
+
["remove", "### ๐ Remove"],
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
let hasContent = false;
|
|
110
|
+
|
|
111
|
+
for (const [key, title] of sections) {
|
|
112
|
+
if (buckets[key].length) {
|
|
113
|
+
hasContent = true;
|
|
114
|
+
out.push(`${title}\n`);
|
|
115
|
+
for (const c of buckets[key]) out.push(`- ${c.desc}`);
|
|
116
|
+
out.push("");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!hasContent) out.push("_No changes._\n");
|
|
121
|
+
|
|
122
|
+
return out.join("\n");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function changelogHasVersion(file, version) {
|
|
126
|
+
if (!fs.existsSync(file)) return false;
|
|
127
|
+
const content = fs.readFileSync(file, "utf8");
|
|
128
|
+
const safe = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
129
|
+
return new RegExp(`^##\\s+${safe}\\b`, "m").test(content);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function main() {
|
|
133
|
+
const isPreview = process.env.PREVIEW_MODE === "true";
|
|
134
|
+
const CHANGELOG_FILE = isPreview
|
|
135
|
+
? "CHANGELOG.preview.md"
|
|
136
|
+
: "CHANGELOG.md";
|
|
137
|
+
|
|
138
|
+
const tags = getAllTags();
|
|
139
|
+
const sections = [];
|
|
140
|
+
|
|
141
|
+
if (tags.length === 0) {
|
|
142
|
+
const pkgVersion = (() => {
|
|
143
|
+
try {
|
|
144
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
145
|
+
return pkg.version || "0.1.0";
|
|
146
|
+
} catch {
|
|
147
|
+
return "0.1.0";
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
|
|
151
|
+
if (!changelogHasVersion(CHANGELOG_FILE, pkgVersion)) {
|
|
152
|
+
const commits = getCommitsBetween(null, "HEAD").map(parseCommit);
|
|
153
|
+
const buckets = categorize(commits);
|
|
154
|
+
sections.push(buildSection(pkgVersion, buckets));
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
for (let i = 0; i < tags.length; i++) {
|
|
158
|
+
const tag = tags[i];
|
|
159
|
+
const previous = tags[i + 1] || null;
|
|
160
|
+
|
|
161
|
+
if (changelogHasVersion(CHANGELOG_FILE, tag)) continue;
|
|
162
|
+
|
|
163
|
+
const commits = getCommitsBetween(previous, tag).map(parseCommit);
|
|
164
|
+
const buckets = categorize(commits);
|
|
165
|
+
sections.push(buildSection(tag, buckets));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!sections.length) {
|
|
170
|
+
console.log("โน No new versions to add.");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (isPreview) {
|
|
175
|
+
fs.writeFileSync(CHANGELOG_FILE, sections.join("\n"), "utf8");
|
|
176
|
+
} else {
|
|
177
|
+
const existing = fs.existsSync(CHANGELOG_FILE)
|
|
178
|
+
? "\n" + fs.readFileSync(CHANGELOG_FILE, "utf8")
|
|
179
|
+
: "";
|
|
180
|
+
fs.writeFileSync(
|
|
181
|
+
CHANGELOG_FILE,
|
|
182
|
+
sections.join("\n") + existing,
|
|
183
|
+
"utf8"
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.log(
|
|
188
|
+
isPreview ? "CHANGELOG preview generated." : "CHANGELOG updated."
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
main();
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
const run = (cmd) => execSync(cmd, { encoding: "utf8" }).trim();
|
|
6
|
+
|
|
7
|
+
const isPreview = process.env.PREVIEW_MODE === "true";
|
|
8
|
+
const RELEASE_NOTES_FILE = isPreview
|
|
9
|
+
? "RELEASE_NOTES.preview.md"
|
|
10
|
+
: "RELEASE_NOTES.md";
|
|
11
|
+
|
|
12
|
+
function ensureGhCLI() {
|
|
13
|
+
try {
|
|
14
|
+
run("gh --version");
|
|
15
|
+
} catch {
|
|
16
|
+
console.error("โ GitHub CLI (gh) is required but not installed.");
|
|
17
|
+
console.error(" Install: https://cli.github.com/");
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getDefaultBranch() {
|
|
23
|
+
try {
|
|
24
|
+
return run("git remote show origin | sed -n 's/.*HEAD branch: //p'");
|
|
25
|
+
} catch {
|
|
26
|
+
return "main";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeRepoURL(url) {
|
|
31
|
+
if (!url) return null;
|
|
32
|
+
url = url.replace(/\.git$/, "");
|
|
33
|
+
if (url.startsWith("git@")) {
|
|
34
|
+
const m = url.match(/^git@(.*?):(.*)$/);
|
|
35
|
+
if (m) return `https://${m[1]}/${m[2]}`;
|
|
36
|
+
}
|
|
37
|
+
return url;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isPreview) {
|
|
41
|
+
ensureGhCLI();
|
|
42
|
+
} else {
|
|
43
|
+
console.log("โน Preview mode: GH CLI not required.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
|
|
47
|
+
const version = pkg.version;
|
|
48
|
+
const repoURL = normalizeRepoURL(pkg.repository?.url);
|
|
49
|
+
|
|
50
|
+
let lastTag = "";
|
|
51
|
+
try {
|
|
52
|
+
lastTag = run("git describe --tags --abbrev=0");
|
|
53
|
+
} catch {
|
|
54
|
+
console.log("โ No previous tags found โ first release?");
|
|
55
|
+
lastTag = "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log("Last tag:", lastTag || "(none)");
|
|
59
|
+
|
|
60
|
+
const baseBranch = getDefaultBranch();
|
|
61
|
+
|
|
62
|
+
let prList = [];
|
|
63
|
+
|
|
64
|
+
if (!isPreview) {
|
|
65
|
+
let prQuery =
|
|
66
|
+
`gh pr list --state merged --base ${baseBranch} ` +
|
|
67
|
+
`--json number,title,author,url`;
|
|
68
|
+
|
|
69
|
+
if (lastTag) prQuery += ` --search "merged:>${lastTag}"`;
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
prList = JSON.parse(run(prQuery));
|
|
73
|
+
} catch {
|
|
74
|
+
prList = [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let notes = `# What's Changed\n\n`;
|
|
79
|
+
|
|
80
|
+
if (!prList.length) {
|
|
81
|
+
notes += "_No changes since last release._\n\n";
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const pr of prList) {
|
|
85
|
+
notes += `- ${pr.title} by @${pr.author.login} in ${pr.url}\n`;
|
|
86
|
+
|
|
87
|
+
let messages = [];
|
|
88
|
+
if (!isPreview) {
|
|
89
|
+
try {
|
|
90
|
+
messages = run(
|
|
91
|
+
`gh pr view ${pr.number} --json commits --jq '.commits[].messageHeadline'`
|
|
92
|
+
)
|
|
93
|
+
.split("\n")
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error(`โ Could not fetch commits for PR #${pr.number}: ${err && err.message ? err.message : err}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const msg of messages) {
|
|
101
|
+
notes += ` - ${msg}\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
notes += "\n";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const compareLink = repoURL
|
|
108
|
+
? lastTag
|
|
109
|
+
? `${repoURL}/compare/${lastTag}...${version}`
|
|
110
|
+
: repoURL
|
|
111
|
+
: "";
|
|
112
|
+
|
|
113
|
+
if (compareLink) {
|
|
114
|
+
notes += `**Full Changelog**: ${compareLink}\n`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
fs.writeFileSync(RELEASE_NOTES_FILE, notes, "utf8");
|
|
118
|
+
|
|
119
|
+
console.log(
|
|
120
|
+
isPreview
|
|
121
|
+
? "โ Generated RELEASE_NOTES.preview.md"
|
|
122
|
+
: "โ Generated RELEASE_NOTES.md"
|
|
123
|
+
);
|
package/bin/preview.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
|
|
5
|
+
process.env.PREVIEW_MODE = "true";
|
|
6
|
+
|
|
7
|
+
const run = (cmd) => execSync(cmd, { encoding: "utf8" }).trim();
|
|
8
|
+
|
|
9
|
+
const filesMap = {
|
|
10
|
+
changelog: "CHANGELOG.preview.md",
|
|
11
|
+
notes: "RELEASE_NOTES.preview.md",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const action = process.argv[2];
|
|
15
|
+
|
|
16
|
+
if (!["create", "remove"].includes(action)) {
|
|
17
|
+
console.log("Usage: preview.js [create|remove]");
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (action === "create") {
|
|
22
|
+
console.log("๐ง Generating preview files...");
|
|
23
|
+
|
|
24
|
+
const versionOutput = run("node bin/compute-version.js");
|
|
25
|
+
|
|
26
|
+
if (versionOutput) {
|
|
27
|
+
console.log("๐ Computed version:");
|
|
28
|
+
console.log(versionOutput);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run("node bin/generate-changelog.js");
|
|
32
|
+
run("node bin/generate-release-notes.js");
|
|
33
|
+
|
|
34
|
+
console.log("โ
Preview ready:");
|
|
35
|
+
console.log(" -", filesMap.changelog);
|
|
36
|
+
console.log(" -", filesMap.notes);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (action === "remove") {
|
|
41
|
+
console.log("๐งน Removing preview files...");
|
|
42
|
+
for (const f of Object.values(filesMap)) {
|
|
43
|
+
if (fs.existsSync(f)) fs.unlinkSync(f);
|
|
44
|
+
}
|
|
45
|
+
console.log("โ Preview cleared.");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// eslint.config.js
|
|
2
|
+
import js from "@eslint/js";
|
|
3
|
+
import prettierConfig from "eslint-config-prettier";
|
|
4
|
+
import pluginImport from "eslint-plugin-import";
|
|
5
|
+
import globals from "globals";
|
|
6
|
+
|
|
7
|
+
export default [
|
|
8
|
+
js.configs.recommended,
|
|
9
|
+
{
|
|
10
|
+
files: ["bin/**/*.js"],
|
|
11
|
+
plugins: {
|
|
12
|
+
import: pluginImport,
|
|
13
|
+
},
|
|
14
|
+
languageOptions: {
|
|
15
|
+
ecmaVersion: "latest",
|
|
16
|
+
sourceType: "module",
|
|
17
|
+
globals: {
|
|
18
|
+
...globals.node,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
rules: {
|
|
22
|
+
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
|
|
23
|
+
"no-console": "off",
|
|
24
|
+
"prefer-const": "warn",
|
|
25
|
+
eqeqeq: ["error", "always"],
|
|
26
|
+
curly: ["error", "all"],
|
|
27
|
+
"import/order": [
|
|
28
|
+
"warn",
|
|
29
|
+
{
|
|
30
|
+
groups: [
|
|
31
|
+
"builtin",
|
|
32
|
+
"external",
|
|
33
|
+
"internal",
|
|
34
|
+
"parent",
|
|
35
|
+
"sibling",
|
|
36
|
+
"index",
|
|
37
|
+
],
|
|
38
|
+
alphabetize: { order: "asc", caseInsensitive: true },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
prettierConfig,
|
|
44
|
+
{
|
|
45
|
+
ignores: ["dist/**/*.js"],
|
|
46
|
+
},
|
|
47
|
+
];
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "release-suite",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Semantic versioning tools for Git-based projects, providing automated version computation, changelog generation and release notes creation.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"rs-compute-version": "bin/compute-version.js",
|
|
11
|
+
"rs-generate-changelog": "bin/generate-changelog.js",
|
|
12
|
+
"rs-generate-release-notes": "bin/generate-release-notes.js",
|
|
13
|
+
"rs-preview": "bin/preview.js",
|
|
14
|
+
"rs-create-tag": "bin/create-tag.js"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"lint": "eslint bin/",
|
|
18
|
+
"lint:fix": "eslint bin/ --fix",
|
|
19
|
+
"version:compute": "node bin/compute-version.js",
|
|
20
|
+
"changelog": "node bin/generate-changelog.js",
|
|
21
|
+
"release:notes": "node bin/generate-release-notes.js",
|
|
22
|
+
"preview": "node bin/preview.js create",
|
|
23
|
+
"preview:clear": "node bin/preview.js remove",
|
|
24
|
+
"create-tag": "node bin/create-tag.js",
|
|
25
|
+
"create-tag:compute": "node bin/create-tag.js --compute",
|
|
26
|
+
"create-tag:dry": "node bin/create-tag.js --dry-run",
|
|
27
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/jonasmzsouza/release-suite"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"release",
|
|
38
|
+
"tools",
|
|
39
|
+
"semantic",
|
|
40
|
+
"versioning",
|
|
41
|
+
"changelog",
|
|
42
|
+
"release-notes",
|
|
43
|
+
"publish",
|
|
44
|
+
"npm",
|
|
45
|
+
"nodejs"
|
|
46
|
+
],
|
|
47
|
+
"author": {
|
|
48
|
+
"name": "Jonas Souza",
|
|
49
|
+
"email": "jonasmzsouza@gmail.com"
|
|
50
|
+
},
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/jonasmzsouza/release-suite/issues"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"esbuild": "^0.25.11",
|
|
57
|
+
"eslint": "9.38.0",
|
|
58
|
+
"eslint-config-prettier": "^10.1.8",
|
|
59
|
+
"eslint-plugin-import": "^2.32.0",
|
|
60
|
+
"globals": "^16.5.0"
|
|
61
|
+
}
|
|
62
|
+
}
|