super-release 0.4.0 → 0.5.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 +152 -162
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# super-release
|
|
2
2
|
|
|
3
|
-
A fast and opinionated [semantic-release](https://semantic-release.gitbook.io/semantic-release) alternative for
|
|
3
|
+
A fast and opinionated [semantic-release](https://semantic-release.gitbook.io/semantic-release) alternative for
|
|
4
|
+
monorepos, written in Rust.
|
|
4
5
|
|
|
5
|
-
Analyzes [conventional commits](https://www.conventionalcommits.org/) to determine version bumps, generate changelogs,
|
|
6
|
+
Analyzes [conventional commits](https://www.conventionalcommits.org/) to determine version bumps, generate changelogs,
|
|
7
|
+
update `package.json` files, publish to npm, and create git tags -- across all packages in a monorepo, in parallel.
|
|
6
8
|
|
|
7
9
|
## Features
|
|
8
10
|
|
|
@@ -10,13 +12,16 @@ Analyzes [conventional commits](https://www.conventionalcommits.org/) to determi
|
|
|
10
12
|
- Prerelease branches (`beta`, `next`, or dynamic from branch name)
|
|
11
13
|
- Maintenance branches (`1.x`, `2.x`) with major-version capping
|
|
12
14
|
- Changelog generation powered by [git-cliff](https://git-cliff.org/)
|
|
15
|
+
- Auto-detects package manager (npm, yarn, pnpm)
|
|
13
16
|
- Configurable tag format templates
|
|
14
|
-
-
|
|
17
|
+
- Plugin system: changelog, npm, exec (extensible)
|
|
18
|
+
- Global file dependencies and ignore patterns
|
|
19
|
+
- Idempotent: safe to rerun after partial failures
|
|
15
20
|
- Dry-run mode with pretty, truncated output
|
|
16
21
|
|
|
17
22
|
## Installation
|
|
18
23
|
|
|
19
|
-
The easiest way
|
|
24
|
+
The easiest way -- no install needed:
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
27
|
npx -y super-release --dry-run
|
|
@@ -25,7 +30,6 @@ npx -y super-release --dry-run
|
|
|
25
30
|
Or install as a dev dependency:
|
|
26
31
|
|
|
27
32
|
```bash
|
|
28
|
-
# npm / yarn / pnpm
|
|
29
33
|
pnpm add -D super-release
|
|
30
34
|
```
|
|
31
35
|
|
|
@@ -75,91 +79,104 @@ Options:
|
|
|
75
79
|
|
|
76
80
|
### `--show-next-version`
|
|
77
81
|
|
|
78
|
-
Outputs only the next version (or the current version if no bump is needed) and exits silently. Useful for CI scripts
|
|
82
|
+
Outputs only the next version (or the current version if no bump is needed) and exits silently. Useful for CI scripts:
|
|
79
83
|
|
|
80
84
|
```bash
|
|
81
|
-
# Use in CI to set a version variable
|
|
82
85
|
VERSION=$(super-release --show-next-version)
|
|
83
|
-
echo "Next version: $VERSION"
|
|
84
|
-
|
|
85
|
-
# Build with the correct version baked in
|
|
86
86
|
SUPER_RELEASE_VERSION=$VERSION cargo build --release
|
|
87
|
-
|
|
88
|
-
# In a monorepo, query a specific package
|
|
89
|
-
super-release --show-next-version -p @acme/core
|
|
90
87
|
```
|
|
91
88
|
|
|
92
|
-
In
|
|
89
|
+
In monorepos, use `--package` to select which package: `super-release --show-next-version -p @acme/core`
|
|
93
90
|
|
|
94
91
|
## How It Works
|
|
95
92
|
|
|
96
|
-
1. **Discover packages** -- finds all directories with a `package.json`
|
|
97
|
-
2. **Resolve tags** -- finds the latest release tag per package (filtered by branch context)
|
|
93
|
+
1. **Discover packages** -- finds all directories with a `package.json` (respects `.gitignore`)
|
|
94
|
+
2. **Resolve tags** -- finds the latest release tag per package (filtered by branch context, only reachable from HEAD)
|
|
98
95
|
3. **Walk commits** -- only analyzes commits since the oldest tag (not the entire history)
|
|
99
|
-
4. **Associate commits to packages** -- maps changed files to their owning package
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
4. **Associate commits to packages** -- maps changed files to their owning package (respects `dependencies` and `ignore`
|
|
97
|
+
config)
|
|
98
|
+
5. **Calculate versions** -- determines bump levels from conventional commits
|
|
99
|
+
6. **Run plugins** -- changelog, npm publish, exec commands
|
|
100
|
+
7. **Git finalize** -- commits modified files, creates tags, optionally pushes
|
|
102
101
|
|
|
103
102
|
## Conventional Commits
|
|
104
103
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
|
108
|
-
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `perf: ...` | patch |
|
|
113
|
-
| `chore: ...`, `docs: ...`, `ci: ...` | no release |
|
|
104
|
+
| Commit | Bump |
|
|
105
|
+
|-------------------------------------------------------|------------|
|
|
106
|
+
| `fix: ...` | patch |
|
|
107
|
+
| `feat: ...` | minor |
|
|
108
|
+
| `feat!: ...` or `BREAKING CHANGE:` in footer | major |
|
|
109
|
+
| `perf: ...` | patch |
|
|
110
|
+
| `chore: ...`, `docs: ...`, `ci: ...`, `refactor: ...` | no release |
|
|
114
111
|
|
|
115
112
|
## Configuration
|
|
116
113
|
|
|
117
|
-
Create a `.release.yaml` (or `.release.yml`, `.super-release.yaml`) in your repository root. All fields are optional
|
|
114
|
+
Create a `.release.yaml` (or `.release.yml`, `.super-release.yaml`) in your repository root. All fields are optional
|
|
115
|
+
with sensible defaults.
|
|
116
|
+
|
|
117
|
+
A [JSON Schema](schema.json) is available for editor autocompletion:
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/bowlingx/super-release/main/schema.json
|
|
121
|
+
```
|
|
118
122
|
|
|
119
123
|
### Full Example
|
|
120
124
|
|
|
121
125
|
```yaml
|
|
122
|
-
# Branch configurations
|
|
123
126
|
branches:
|
|
124
|
-
# Stable branches (simple string = stable, no prerelease)
|
|
125
127
|
- main
|
|
126
128
|
- master
|
|
127
|
-
|
|
128
|
-
# Prerelease with a fixed channel name
|
|
129
129
|
- name: beta
|
|
130
|
-
prerelease: beta
|
|
131
|
-
|
|
132
|
-
- name: next
|
|
133
|
-
prerelease: next # -> 2.0.0-next.1, ...
|
|
134
|
-
|
|
135
|
-
# Prerelease using the branch name as the channel (for branch patterns)
|
|
130
|
+
prerelease: beta
|
|
136
131
|
- name: "test-*"
|
|
137
|
-
prerelease: true
|
|
138
|
-
|
|
139
|
-
# Maintenance branches (caps major version, breaking changes -> minor)
|
|
132
|
+
prerelease: true
|
|
140
133
|
- name: "1.x"
|
|
141
|
-
maintenance: true
|
|
134
|
+
maintenance: true
|
|
135
|
+
|
|
136
|
+
tag_format: "v{version}"
|
|
137
|
+
tag_format_package: "{name}/v{version}"
|
|
142
138
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
tag_format_package: "{name}/v{version}" # sub-packages: @acme/core/v1.2.3
|
|
139
|
+
packages:
|
|
140
|
+
- "@acme/*"
|
|
146
141
|
|
|
147
|
-
# Packages to exclude from releasing (substring match on package name)
|
|
148
142
|
exclude:
|
|
149
|
-
- my-
|
|
143
|
+
- my-monorepo-root
|
|
144
|
+
|
|
145
|
+
# Files that trigger ALL packages when changed
|
|
146
|
+
dependencies:
|
|
147
|
+
- yarn.lock
|
|
148
|
+
- pnpm-lock.yaml
|
|
149
|
+
|
|
150
|
+
# Files to ignore -- commits touching only these won't trigger releases
|
|
151
|
+
ignore:
|
|
152
|
+
- "README.md"
|
|
153
|
+
- "docs/**"
|
|
154
|
+
- "**/*.md"
|
|
150
155
|
|
|
151
|
-
# Plugins run in order: prepare phase first, then publish phase
|
|
152
156
|
plugins:
|
|
153
157
|
- name: changelog
|
|
154
158
|
- name: npm
|
|
155
|
-
|
|
159
|
+
options:
|
|
160
|
+
provenance: true
|
|
161
|
+
- name: exec
|
|
162
|
+
options:
|
|
163
|
+
prepare_cmd: "sed -i'' -e 's/^version = .*/version = \"{version}\"/' Cargo.toml"
|
|
164
|
+
files:
|
|
165
|
+
- Cargo.toml
|
|
166
|
+
- Cargo.lock
|
|
167
|
+
|
|
168
|
+
git:
|
|
169
|
+
commit_message: "chore(release): {releases} [skip ci]"
|
|
170
|
+
push: false
|
|
171
|
+
remote: origin
|
|
156
172
|
```
|
|
157
173
|
|
|
158
174
|
### Reference
|
|
159
175
|
|
|
160
176
|
#### `branches`
|
|
161
177
|
|
|
162
|
-
Defines which branches can produce releases
|
|
178
|
+
Defines which branches can produce releases. Only configured branches are allowed -- running on an unconfigured branch
|
|
179
|
+
exits cleanly.
|
|
163
180
|
|
|
164
181
|
| Form | Type | Example versions |
|
|
165
182
|
|--------------------------------------------|-------------------------------------|---------------------------------|
|
|
@@ -168,144 +185,124 @@ Defines which branches can produce releases and what kind.
|
|
|
168
185
|
| `- name: "test-*"`<br>` prerelease: true` | Prerelease (branch name as channel) | `2.0.0-test-my-feature.1` |
|
|
169
186
|
| `- name: "1.x"`<br>` maintenance: true` | Maintenance | `1.5.1`, `1.6.0` (major capped) |
|
|
170
187
|
|
|
171
|
-
**Tag filtering by branch**: Stable branches only see stable tags. Prerelease branches see their own channel's tags plus
|
|
188
|
+
**Tag filtering by branch**: Stable branches only see stable tags. Prerelease branches see their own channel's tags plus
|
|
189
|
+
stable tags. Tags on other branches that haven't been merged are ignored.
|
|
172
190
|
|
|
173
|
-
|
|
191
|
+
Default: `["main", "master"]`
|
|
174
192
|
|
|
175
|
-
|
|
193
|
+
#### `tag_format` / `tag_format_package`
|
|
176
194
|
|
|
177
|
-
|
|
195
|
+
Templates for git tag names. Placeholders: `{version}`, `{name}`.
|
|
178
196
|
|
|
179
|
-
|
|
197
|
+
```yaml
|
|
198
|
+
tag_format: "v{version}" # root: v1.2.3 (default)
|
|
199
|
+
tag_format_package: "{name}/v{version}" # sub-packages: @acme/core/v1.2.3 (default)
|
|
200
|
+
tag_format_package: "{name}@{version}" # semantic-release compat
|
|
201
|
+
```
|
|
180
202
|
|
|
181
|
-
|
|
182
|
-
- `{version}` -- the semver version (e.g. `1.2.3`, `2.0.0-beta.1`)
|
|
183
|
-
- `{name}` -- the package name from `package.json`
|
|
203
|
+
#### `dependencies`
|
|
184
204
|
|
|
185
|
-
|
|
205
|
+
Global file dependency patterns. When a commit changes any matching file, ALL packages are considered affected.
|
|
186
206
|
|
|
187
|
-
Examples:
|
|
188
207
|
```yaml
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
208
|
+
dependencies:
|
|
209
|
+
- yarn.lock
|
|
210
|
+
- pnpm-lock.yaml
|
|
211
|
+
- package.json
|
|
212
|
+
- ".github/**"
|
|
192
213
|
```
|
|
193
214
|
|
|
194
|
-
#### `
|
|
195
|
-
|
|
196
|
-
Template for sub-package tags in a monorepo.
|
|
215
|
+
#### `ignore`
|
|
197
216
|
|
|
198
|
-
|
|
217
|
+
Glob patterns for files to ignore. Commits that only touch ignored files will not trigger a release. If a commit touches
|
|
218
|
+
both ignored and non-ignored files, only the non-ignored files determine which packages are affected.
|
|
199
219
|
|
|
200
|
-
Examples:
|
|
201
220
|
```yaml
|
|
202
|
-
|
|
203
|
-
|
|
221
|
+
ignore:
|
|
222
|
+
- "README.md"
|
|
223
|
+
- "docs/**"
|
|
224
|
+
- "**/*.md"
|
|
225
|
+
- ".prettierrc"
|
|
204
226
|
```
|
|
205
227
|
|
|
206
|
-
|
|
228
|
+
#### `packages` / `exclude`
|
|
207
229
|
|
|
208
|
-
|
|
230
|
+
Filter which packages are released. `packages` is an allow-list (glob patterns), `exclude` is a deny-list.
|
|
209
231
|
|
|
210
|
-
|
|
232
|
+
```yaml
|
|
233
|
+
packages:
|
|
234
|
+
- "@acme/*"
|
|
235
|
+
exclude:
|
|
236
|
+
- my-monorepo-root
|
|
237
|
+
```
|
|
211
238
|
|
|
212
|
-
|
|
239
|
+
#### `plugins`
|
|
240
|
+
|
|
241
|
+
Ordered list of plugins. Each plugin has a `name`, optional `packages` filter (glob), and `options`.
|
|
213
242
|
|
|
214
243
|
```yaml
|
|
215
244
|
plugins:
|
|
216
245
|
- name: changelog
|
|
217
246
|
options:
|
|
218
|
-
filename: CHANGELOG.md
|
|
219
|
-
preview_lines: 20
|
|
247
|
+
filename: CHANGELOG.md
|
|
248
|
+
preview_lines: 20
|
|
220
249
|
|
|
221
250
|
- name: npm
|
|
251
|
+
packages: [ "@acme/*" ] # only publish @acme packages
|
|
222
252
|
options:
|
|
223
|
-
access: public
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
package_manager: yarn
|
|
253
|
+
access: public
|
|
254
|
+
provenance: true
|
|
255
|
+
registry: https://registry.npmjs.org
|
|
256
|
+
tag: next # dist-tag (default: auto from prerelease channel)
|
|
257
|
+
publish_args: [ "--otp=123456" ]
|
|
258
|
+
package_manager: yarn # force specific PM (default: auto-detect)
|
|
229
259
|
|
|
230
260
|
- name: exec
|
|
261
|
+
packages: [ "my-rust-lib" ]
|
|
231
262
|
options:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
#
|
|
235
|
-
# packages: ["@acme/core"]
|
|
236
|
-
|
|
237
|
-
# Multiple exec blocks for different packages:
|
|
238
|
-
# - name: exec
|
|
239
|
-
# options:
|
|
240
|
-
# packages: ["my-rust-project"]
|
|
241
|
-
# prepare_cmd: "sed -i'' -e 's/^version = .*/version = \"{version}\"/' Cargo.toml"
|
|
242
|
-
# - name: exec
|
|
243
|
-
# options:
|
|
244
|
-
# packages: ["@acme/*"]
|
|
245
|
-
# publish_cmd: "deploy.sh {name} {version}"
|
|
246
|
-
|
|
247
|
-
- name: git-commit
|
|
248
|
-
options:
|
|
249
|
-
# Commit message template. Placeholders:
|
|
250
|
-
# {releases} - comma-separated list: "@acme/core@1.1.0, @acme/utils@1.0.1"
|
|
251
|
-
# {summary} - one per line: " - @acme/core 1.0.0 -> 1.1.0"
|
|
252
|
-
# {count} - number of packages released
|
|
253
|
-
message: "chore(release): {releases} [skip ci]"
|
|
254
|
-
push: false # push after commit (default: false)
|
|
255
|
-
remote: origin # git remote (default: "origin")
|
|
256
|
-
paths: # paths to stage (default: ["."])
|
|
257
|
-
- "."
|
|
258
|
-
|
|
259
|
-
- name: git-tag
|
|
260
|
-
options:
|
|
261
|
-
push: false # push tags to remote after creation (default: false)
|
|
262
|
-
remote: origin # git remote to push to (default: "origin")
|
|
263
|
+
prepare_cmd: "sed -i'' -e 's/^version = .*/version = \"{version}\"/' Cargo.toml"
|
|
264
|
+
publish_cmd: "cargo publish"
|
|
265
|
+
files: [ Cargo.toml, Cargo.lock ] # include in git commit
|
|
263
266
|
```
|
|
264
267
|
|
|
265
|
-
| Plugin
|
|
266
|
-
|
|
267
|
-
| `changelog`
|
|
268
|
-
| `npm`
|
|
269
|
-
| `exec`
|
|
270
|
-
| `git-commit` | -- | Stages changed files, commits with release message, optionally pushes |
|
|
271
|
-
| `git-tag` | -- | Creates annotated git tags, optionally pushes |
|
|
268
|
+
| Plugin | Prepare | Publish |
|
|
269
|
+
|-------------|--------------------------------------------------------------|--------------------------------------------------------|
|
|
270
|
+
| `changelog` | Generates/updates changelog per package (parallel) | -- |
|
|
271
|
+
| `npm` | Updates `package.json` versions (auto-detects npm/yarn/pnpm) | Publishes packages (parallel within dependency levels) |
|
|
272
|
+
| `exec` | Runs custom shell command per package | Runs custom shell command per package |
|
|
272
273
|
|
|
273
|
-
The
|
|
274
|
+
Plugins return the files they modified. The core git step stages exactly those files for the commit -- no `git add .`.
|
|
274
275
|
|
|
275
|
-
|
|
276
|
+
Default: `[changelog, npm]`
|
|
276
277
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
```yaml
|
|
280
|
-
# Only release packages in the @acme scope
|
|
281
|
-
packages:
|
|
282
|
-
- "@acme/*"
|
|
278
|
+
#### `git`
|
|
283
279
|
|
|
284
|
-
|
|
285
|
-
packages:
|
|
286
|
-
- "@acme/core"
|
|
287
|
-
- "@acme/utils"
|
|
280
|
+
Core git behavior after all plugins run. Not a plugin -- always runs.
|
|
288
281
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
282
|
+
```yaml
|
|
283
|
+
git:
|
|
284
|
+
commit_message: "chore(release): {releases} [skip ci]"
|
|
285
|
+
push: false # push commit + tags to remote
|
|
286
|
+
remote: origin
|
|
292
287
|
```
|
|
293
288
|
|
|
294
|
-
|
|
289
|
+
Commit message placeholders:
|
|
295
290
|
|
|
296
|
-
|
|
291
|
+
- `{releases}` -- comma-separated: `@acme/core@1.1.0, @acme/utils@1.0.1`
|
|
292
|
+
- `{summary}` -- one per line: ` - @acme/core 1.0.0 -> 1.1.0`
|
|
293
|
+
- `{count}` -- number of packages released
|
|
297
294
|
|
|
298
|
-
|
|
295
|
+
The git step:
|
|
299
296
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
297
|
+
1. Stages only files reported by plugins
|
|
298
|
+
2. Also stages any tracked-file changes (for exec commands that can't report files)
|
|
299
|
+
3. Commits (or skips if nothing changed)
|
|
300
|
+
4. Creates annotated tags for each release
|
|
301
|
+
5. Pushes commit + tags if `push: true`
|
|
305
302
|
|
|
306
|
-
|
|
303
|
+
Tags are idempotent -- existing tags are skipped. Already-published npm packages are detected and skipped on rerun.
|
|
307
304
|
|
|
308
|
-
|
|
305
|
+
## Monorepo Structure
|
|
309
306
|
|
|
310
307
|
```
|
|
311
308
|
my-monorepo/
|
|
@@ -320,30 +317,23 @@ my-monorepo/
|
|
|
320
317
|
src/
|
|
321
318
|
```
|
|
322
319
|
|
|
323
|
-
|
|
320
|
+
Packages are discovered by finding `package.json` files recursively (respects `.gitignore`). Each commit is associated
|
|
321
|
+
to a package based on which files it changed.
|
|
324
322
|
|
|
325
323
|
## Performance
|
|
326
324
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
- **
|
|
330
|
-
- **
|
|
331
|
-
- **Single-pass commit collection**: commits are fetched once and partitioned per package
|
|
332
|
-
- **Precomputed file mapping**: file-to-package association is computed once, not per-package
|
|
333
|
-
|
|
334
|
-
Benchmark (2001 commits, 8 packages, Apple Silicon):
|
|
335
|
-
|
|
336
|
-
| Scenario | Time |
|
|
337
|
-
|-------------------------------|--------|
|
|
338
|
-
| All commits since initial tag | 0.11s |
|
|
339
|
-
| 100 commits since recent tags | 0.035s |
|
|
325
|
+
- **Parallel diff computation**: commit diffs computed across multiple threads
|
|
326
|
+
- **Tag-bounded history walk**: only walks commits since the oldest package tag
|
|
327
|
+
- **Single-pass commit collection**: commits fetched once, partitioned per package
|
|
328
|
+
- **Reachable-only tags**: single revwalk to check tag reachability, stops early
|
|
340
329
|
|
|
341
330
|
## Acknowledgements
|
|
342
331
|
|
|
343
332
|
super-release is inspired by and builds on the ideas of:
|
|
344
333
|
|
|
345
|
-
- **[semantic-release](https://github.com/semantic-release/semantic-release)**
|
|
346
|
-
|
|
334
|
+
- **[semantic-release](https://github.com/semantic-release/semantic-release)** -- the original automated release tool
|
|
335
|
+
that pioneered conventional-commit-based versioning
|
|
336
|
+
- **[git-cliff](https://github.com/orhun/git-cliff)** -- powers changelog generation via `git-cliff-core`
|
|
347
337
|
|
|
348
338
|
## License
|
|
349
339
|
|