@entro314labs/release-kit 2.1.0 → 2.2.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 +65 -21
- package/package.json +1 -1
- package/release.mjs +34 -1
package/README.md
CHANGED
|
@@ -65,45 +65,70 @@ Released v2.5.0
|
|
|
65
65
|
|
|
66
66
|
## 📦 Install
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
Pick by what the project is, not by preference.
|
|
69
|
+
|
|
70
|
+
### Node projects — devDependency
|
|
71
|
+
|
|
72
|
+
Pins the version, so every machine and CI run behave identically.
|
|
69
73
|
|
|
70
74
|
```sh
|
|
71
75
|
pnpm add -D @entro314labs/release-kit
|
|
72
76
|
```
|
|
73
77
|
|
|
74
78
|
```json
|
|
75
|
-
{
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
{ "scripts": { "release": "release-kit" } }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Non-Node projects — global install
|
|
83
|
+
|
|
84
|
+
A Rust, Python or Go repository has no manifest to hang a devDependency on, so install it
|
|
85
|
+
once and use it everywhere.
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
npm i -g @entro314labs/release-kit
|
|
89
|
+
release-kit minor
|
|
80
90
|
```
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
### CI, any language — pinned npx
|
|
93
|
+
|
|
94
|
+
No global state to drift, no install step, and the version is explicit in the command.
|
|
84
95
|
|
|
85
96
|
```sh
|
|
86
|
-
npx @entro314labs/release-kit --
|
|
97
|
+
npx @entro314labs/release-kit@2.1.0 minor --yes
|
|
87
98
|
```
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
### Vendored — no registry at release time
|
|
101
|
+
|
|
102
|
+
For a project that should not depend on the registry it is about to publish to, or that
|
|
103
|
+
needs releases to work offline. The file is self-contained, so a copy is a complete install.
|
|
92
104
|
|
|
93
105
|
```sh
|
|
94
|
-
npx @entro314labs/release-kit --sync .
|
|
106
|
+
npx @entro314labs/release-kit --sync . # writes scripts/release.mjs
|
|
95
107
|
```
|
|
96
108
|
|
|
97
109
|
```json
|
|
98
|
-
{
|
|
99
|
-
"scripts": {
|
|
100
|
-
"release": "node scripts/release.mjs"
|
|
101
|
-
}
|
|
102
|
-
}
|
|
110
|
+
{ "scripts": { "release": "node scripts/release.mjs" } }
|
|
103
111
|
```
|
|
104
112
|
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
### Piped — nothing installed at all
|
|
114
|
+
|
|
115
|
+
`release.mjs` runs straight from stdin, arguments and all. Useful for a one-off release on a
|
|
116
|
+
machine you do not want to install anything on.
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
curl -fsSL https://raw.githubusercontent.com/entro314-labs/release-kit/v2.1.0/release.mjs \
|
|
120
|
+
| node - minor --yes
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Pin the URL to a tag, never `main`: piping an unpinned remote script into an interpreter
|
|
124
|
+
means whatever is at that URL runs against your repository and your credentials. `--sync` is
|
|
125
|
+
the one thing that does not work this way — copying itself needs a file on disk.
|
|
126
|
+
|
|
127
|
+
> **All five paths run the same file and need Node 18+.** That includes the Rust, Python and
|
|
128
|
+
> Go projects: `release-kit` is a Node program regardless of what it is releasing.
|
|
129
|
+
|
|
130
|
+
Zero-config works on the conventions below; add a [`release.config.json`](#️-configuration)
|
|
131
|
+
only for what differs.
|
|
107
132
|
|
|
108
133
|
## ⚡ Usage
|
|
109
134
|
|
|
@@ -224,6 +249,7 @@ rather than stopping at the first problem.
|
|
|
224
249
|
- The remote exists, is reachable, and the branch is not behind it
|
|
225
250
|
- The tag is free — or already exists at `HEAD`, in which case it is reused
|
|
226
251
|
- `gh` is installed and authenticated
|
|
252
|
+
- Commit and tag signing can actually sign, when `commit.gpgsign` or `tag.gpgsign` is on
|
|
227
253
|
- The publishing CLI is authenticated, and the version is not already on the registry
|
|
228
254
|
- Configured release assets exist
|
|
229
255
|
- A changelog section for the version exists _(a warning, not a failure — it falls back
|
|
@@ -320,6 +346,23 @@ name, `%d` npm dist-tag. In the `publish` command line the substituted values ar
|
|
|
320
346
|
shell-quoted, so a version carrying shell metacharacters is passed through as one literal
|
|
321
347
|
argument.
|
|
322
348
|
|
|
349
|
+
### Signing
|
|
350
|
+
|
|
351
|
+
Signing is git's, not this tool's: commits and tags are made with plain `git commit` and
|
|
352
|
+
`git tag`, so they are signed exactly when `commit.gpgsign` and `tag.gpgsign` say to, with
|
|
353
|
+
whatever key `user.signingkey` resolves to. There is no key handling here to get wrong.
|
|
354
|
+
|
|
355
|
+
What it does add is a preflight check, because an unusable key otherwise fails at the commit
|
|
356
|
+
step with the version already written. For CI, where a signing key usually is not present,
|
|
357
|
+
disable signing for that run rather than configuring keys:
|
|
358
|
+
|
|
359
|
+
```sh
|
|
360
|
+
git -c commit.gpgsign=false -c tag.gpgsign=false release-kit minor --yes
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
For commits to show as **Verified** on GitHub, the SSH key must be registered as a _signing_
|
|
364
|
+
key in your account, which is a separate list from authentication keys.
|
|
365
|
+
|
|
323
366
|
### Publishing and authentication
|
|
324
367
|
|
|
325
368
|
The registry preflight (`whoami`, the already-published lookup) runs with whichever CLI the
|
|
@@ -445,7 +488,8 @@ including a directory that is not a repository.
|
|
|
445
488
|
|
|
446
489
|
## 📋 Requirements
|
|
447
490
|
|
|
448
|
-
- Node 18+
|
|
491
|
+
- **Node 18+ — including for Rust, Python and Go projects.** `release-kit` is a Node
|
|
492
|
+
program whatever it releases; there is no standalone binary.
|
|
449
493
|
- `git`
|
|
450
494
|
- `gh`, authenticated — only when creating GitHub releases
|
|
451
495
|
- Whatever the `publish` command needs — for the default, a live `npm login` session
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entro314labs/release-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Single-file, zero-dependency release mechanism for JS/TS/Node projects: version bump, changelog roll, commit, annotated tag, push, publish, GitHub release",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"changelog",
|
package/release.mjs
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
import { execFileSync, execSync } from 'node:child_process'
|
|
35
35
|
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from 'node:fs'
|
|
36
|
-
import { tmpdir } from 'node:os'
|
|
36
|
+
import { homedir, tmpdir } from 'node:os'
|
|
37
37
|
import { basename, join, relative, resolve, sep } from 'node:path'
|
|
38
38
|
import { createInterface } from 'node:readline/promises'
|
|
39
39
|
|
|
@@ -762,6 +762,15 @@ if (flag('--help') || flag('-h')) {
|
|
|
762
762
|
// --sync copies this file into other projects and exits; it touches no git state.
|
|
763
763
|
if (flag('--sync')) {
|
|
764
764
|
const self = new URL(import.meta.url).pathname
|
|
765
|
+
// Piped from stdin (`curl … | node -`) there is no file to copy: import.meta.url points
|
|
766
|
+
// at a synthetic [eval] path. Say so instead of failing on a missing file.
|
|
767
|
+
if (!existsSync(self)) {
|
|
768
|
+
abort(
|
|
769
|
+
'--sync copies this script from disk, and it was piped from stdin so there is no ' +
|
|
770
|
+
'file to copy.\n Run it from an installed copy instead: ' +
|
|
771
|
+
'npx @entro314labs/release-kit --sync <dir>',
|
|
772
|
+
)
|
|
773
|
+
}
|
|
765
774
|
const targets = argv.slice(argv.indexOf('--sync') + 1).filter((a) => !a.startsWith('-'))
|
|
766
775
|
if (!targets.length) abort('--sync needs at least one project directory')
|
|
767
776
|
|
|
@@ -1122,6 +1131,30 @@ if (!succeeds('git', ['remote', 'get-url', config.remote])) {
|
|
|
1122
1131
|
}
|
|
1123
1132
|
}
|
|
1124
1133
|
|
|
1134
|
+
// Signing is configured per repository and inherited, never managed here — git already
|
|
1135
|
+
// owns that. But a signing setup that cannot produce a signature fails at the commit step,
|
|
1136
|
+
// after the version has been written, so it is worth catching before anything mutates.
|
|
1137
|
+
const signsSomething = ['commit', 'version', 'changelog', 'tag'].some(runs)
|
|
1138
|
+
const signingKeys = ['commit.gpgsign', 'tag.gpgsign'].filter(
|
|
1139
|
+
(key) => tryRead('git', ['config', '--get', key]) === 'true',
|
|
1140
|
+
)
|
|
1141
|
+
if (signsSomething && signingKeys.length) {
|
|
1142
|
+
const format = tryRead('git', ['config', '--get', 'gpg.format']) || 'openpgp'
|
|
1143
|
+
const signingKey = tryRead('git', ['config', '--get', 'user.signingkey'])
|
|
1144
|
+
const keyPath = signingKey?.replace(/^~/, homedir())
|
|
1145
|
+
if (!signingKey) {
|
|
1146
|
+
fail(`${signingKeys.join(' and ')} enabled but user.signingkey is not set`)
|
|
1147
|
+
} else if (format === 'ssh' && /^[~/.]/.test(signingKey) && !existsSync(keyPath)) {
|
|
1148
|
+
fail(
|
|
1149
|
+
`signing key ${signingKey} does not exist.\n` +
|
|
1150
|
+
' Point user.signingkey at a key that is present, or disable signing for this ' +
|
|
1151
|
+
'run with `git -c commit.gpgsign=false -c tag.gpgsign=false`.',
|
|
1152
|
+
)
|
|
1153
|
+
} else {
|
|
1154
|
+
ok(`signing commits and tags (${format})`)
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1125
1158
|
const head = tryRead('git', ['rev-parse', 'HEAD'])
|
|
1126
1159
|
const taggedCommit = tryRead('git', ['rev-list', '-n', '1', tag])
|
|
1127
1160
|
if (!runs('tag')) {
|