@entro314labs/release-kit 2.3.3 → 2.4.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 +39 -0
- package/package.json +5 -3
- package/release.mjs +24 -3
package/README.md
CHANGED
|
@@ -342,6 +342,11 @@ the version. Only the version itself is rewritten, so comments and formatting su
|
|
|
342
342
|
because the TOML match is anchored to the start of a line, a dependency's
|
|
343
343
|
`serde = { version = "1.0" }` is left alone.
|
|
344
344
|
|
|
345
|
+
**Lockfiles are scoped automatically.** A `Cargo.lock` records a version for every
|
|
346
|
+
dependency — hundreds of them — so matching the first `version = "…"` would rewrite an
|
|
347
|
+
unrelated crate. Listing one rewrites only the `[[package]]` block whose name matches the
|
|
348
|
+
crate in the sibling `Cargo.toml`; with no sibling to read, it refuses rather than guesses.
|
|
349
|
+
|
|
345
350
|
For anything else, give a pattern with one capture group around the version. `versionFiles`
|
|
346
351
|
takes the same entries, so several files stay in sync across formats:
|
|
347
352
|
|
|
@@ -352,6 +357,29 @@ takes the same entries, so several files stay in sync across formats:
|
|
|
352
357
|
}
|
|
353
358
|
```
|
|
354
359
|
|
|
360
|
+
A desktop app usually carries the same version in a lot of places at once — a workspace
|
|
361
|
+
manifest, per-platform bundle configs, a crate manifest and its lockfile. They stay in step
|
|
362
|
+
in one release, across three formats, with no scripting:
|
|
363
|
+
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"versionFiles": [
|
|
367
|
+
"apps/desktop/package.json",
|
|
368
|
+
"apps/desktop/src-tauri/tauri.conf.json",
|
|
369
|
+
"apps/desktop/src-tauri/tauri.macos.conf.json",
|
|
370
|
+
"apps/desktop/src-tauri/tauri.windows.conf.json",
|
|
371
|
+
"apps/desktop/src-tauri/tauri.linux.conf.json",
|
|
372
|
+
"apps/desktop/src-tauri/Cargo.toml",
|
|
373
|
+
"apps/desktop/src-tauri/Cargo.lock"
|
|
374
|
+
],
|
|
375
|
+
"publish": null,
|
|
376
|
+
"steps": ["version", "changelog", "tag", "push"]
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Stopping at `push` because the tag is what triggers the build pipeline — see
|
|
381
|
+
[Libraries versus apps](#-libraries-versus-apps).
|
|
382
|
+
|
|
355
383
|
The project name comes from the manifest when there is one (`name` in `package.json`,
|
|
356
384
|
`Cargo.toml` or `pyproject.toml`), and falls back to the repository directory.
|
|
357
385
|
|
|
@@ -642,6 +670,17 @@ with the reasoning — are in [ROADMAP.md](ROADMAP.md).
|
|
|
642
670
|
|
|
643
671
|
## 🤝 Contributing
|
|
644
672
|
|
|
673
|
+
```sh
|
|
674
|
+
pnpm install
|
|
675
|
+
pnpm test # 63 tests, node --test, no framework
|
|
676
|
+
pnpm check # format + lint + tests, the same gate CI runs
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
`test/` holds unit suites for the pure functions and an integration suite that builds real
|
|
680
|
+
throwaway repositories with a real bare remote and stubbed `gh`/`npm`. The integration tests
|
|
681
|
+
pin defects found in use, so a name like "refuses to reuse a tag while still producing a
|
|
682
|
+
commit" is describing something that actually happened.
|
|
683
|
+
|
|
645
684
|
The tool releases itself, so a change ships the same way it would in any consuming project:
|
|
646
685
|
add a `## [Unreleased]` entry to `CHANGELOG.md`, then run `pnpm release <bump>` from a clone.
|
|
647
686
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entro314labs/release-kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.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",
|
|
@@ -41,12 +41,14 @@
|
|
|
41
41
|
"format:check": "oxfmt --check .",
|
|
42
42
|
"lint": "oxlint .",
|
|
43
43
|
"lint:ci": "oxlint --deny-warnings .",
|
|
44
|
-
"
|
|
44
|
+
"test": "node --test \"test/**/*.test.mjs\"",
|
|
45
|
+
"check": "pnpm run format:check && pnpm run lint:ci && pnpm run test",
|
|
45
46
|
"release": "node release.mjs"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"oxfmt": "^0.63.0",
|
|
49
|
-
"oxlint": "^1.78.0"
|
|
50
|
+
"oxlint": "^1.78.0",
|
|
51
|
+
"semver": "^7.8.5"
|
|
50
52
|
},
|
|
51
53
|
"engines": {
|
|
52
54
|
"node": ">=22"
|
package/release.mjs
CHANGED
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
writeFileSync,
|
|
42
42
|
} from 'node:fs'
|
|
43
43
|
import { homedir, tmpdir } from 'node:os'
|
|
44
|
-
import { basename, join, relative, resolve, sep } from 'node:path'
|
|
44
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path'
|
|
45
45
|
import { createInterface } from 'node:readline/promises'
|
|
46
46
|
|
|
47
47
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -919,9 +919,29 @@ function readNameFrom(entry) {
|
|
|
919
919
|
/** Normalise a versionFile / versionFiles entry to { path, pattern }. */
|
|
920
920
|
const versionSource = (entry) => (typeof entry === 'string' ? { path: entry } : entry)
|
|
921
921
|
|
|
922
|
+
/**
|
|
923
|
+
* A lockfile records a version for every dependency — hundreds of them — so the first
|
|
924
|
+
* `version = "…"` in the file belongs to whichever crate sorts first, not to this project.
|
|
925
|
+
* Rewriting it corrupts an unrelated dependency, silently. Scope to the named package block.
|
|
926
|
+
*/
|
|
927
|
+
function cargoLockPattern(lockPath) {
|
|
928
|
+
const sibling = join(dirname(lockPath), 'Cargo.toml')
|
|
929
|
+
const crate = existsSync(sibling) ? readNameFrom({ path: sibling }) : null
|
|
930
|
+
if (!crate) {
|
|
931
|
+
throw new Error(
|
|
932
|
+
`${lockPath} lists every dependency's version, so it needs to know which package is ` +
|
|
933
|
+
`yours.\n No Cargo.toml beside it to read the name from — give an explicit ` +
|
|
934
|
+
`pattern:\n { "path": "${lockPath}", "pattern": "name = \\"<crate>\\"\\nversion = ` +
|
|
935
|
+
`\\"(.+)\\"" }`,
|
|
936
|
+
)
|
|
937
|
+
}
|
|
938
|
+
return new RegExp(`\\[\\[package\\]\\]\\nname = "${escapeRe(crate)}"\\nversion = "([^"]*)"`)
|
|
939
|
+
}
|
|
940
|
+
|
|
922
941
|
/** The regex for a source, or null when the whole file is the version. */
|
|
923
942
|
function patternFor({ path, pattern }) {
|
|
924
943
|
if (pattern) return new RegExp(pattern, 'm')
|
|
944
|
+
if (basename(path) === 'Cargo.lock') return cargoLockPattern(path)
|
|
925
945
|
if (path.endsWith('.json')) return VERSION_PATTERNS.json
|
|
926
946
|
if (path.endsWith('.toml')) return VERSION_PATTERNS.toml
|
|
927
947
|
return null
|
|
@@ -941,9 +961,10 @@ function readVersionFrom(entry) {
|
|
|
941
961
|
* Replace the version in a source file, touching nothing else: only the captured range is
|
|
942
962
|
* rewritten, so formatting, key order and comments all survive.
|
|
943
963
|
*
|
|
964
|
+
* @param {{dryRun?: boolean}} [options] report the change without making it
|
|
944
965
|
* @returns {boolean} whether the file needed changing
|
|
945
966
|
*/
|
|
946
|
-
function writeVersionInto(entry, version) {
|
|
967
|
+
function writeVersionInto(entry, version, { dryRun = false } = {}) {
|
|
947
968
|
const source = versionSource(entry)
|
|
948
969
|
const text = readFileSync(source.path, 'utf8')
|
|
949
970
|
const pattern = patternFor(source)
|
|
@@ -1754,7 +1775,7 @@ if (bumping) {
|
|
|
1754
1775
|
for (const entry of [versionFile, ...config.versionFiles]) {
|
|
1755
1776
|
const source = versionSource(entry)
|
|
1756
1777
|
if (!existsSync(source.path)) abort(`versionFiles entry ${source.path} does not exist`)
|
|
1757
|
-
if (writeVersionInto(source, version)) {
|
|
1778
|
+
if (writeVersionInto(source, version, { dryRun })) {
|
|
1758
1779
|
staged.push(source.path)
|
|
1759
1780
|
console.log(` ${dryRun ? yellow('would write') : dim('wrote')} ${source.path}`)
|
|
1760
1781
|
}
|