@regardio/dev 2.4.1 → 2.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/dist/bin/ship/hotfix.bin.mjs +23 -25
- package/dist/bin/ship/production.bin.mjs +24 -26
- package/dist/bin/ship/staging.bin.mjs +1 -1
- package/dist/bin/ship/{utils-rnvdLrkF.mjs → utils-BPxpSgv6.mjs} +8 -1
- package/docs/en/tools/biome.md +6 -18
- package/docs/en/tools/husky.md +3 -6
- package/docs/en/tools/markdownlint.md +3 -12
- package/docs/en/tools/releases.md +139 -159
- package/docs/en/tools/vitest.md +1 -1
- package/package.json +3 -3
- package/templates/versionrc/.versionrc.json +1 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git, c as
|
|
2
|
+
import { a as git, c as runQualityChecks, i as getWorkspacePackages, n as chooseForEach, o as gitRead, s as runPackageRelease, t as branchExists } from "./utils-BPxpSgv6.mjs";
|
|
3
3
|
//#region src/bin/ship/hotfix.ts
|
|
4
4
|
/**
|
|
5
5
|
* ship-hotfix: Manage hotfix branches based on production code.
|
|
@@ -52,6 +52,8 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
52
52
|
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
53
53
|
process.exit(1);
|
|
54
54
|
}
|
|
55
|
+
console.log("\nFetching latest state from origin...");
|
|
56
|
+
git("fetch", "origin");
|
|
55
57
|
console.log("\nRunning quality checks...");
|
|
56
58
|
try {
|
|
57
59
|
runQualityChecks();
|
|
@@ -61,27 +63,21 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
61
63
|
}
|
|
62
64
|
console.log("✅ Quality checks passed");
|
|
63
65
|
const packages = getWorkspacePackages(cwd);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
runScript(`release:${highestBump}`);
|
|
80
|
-
} catch {
|
|
81
|
-
console.log("No release script found - skipping versioning (non-publishing repo)");
|
|
82
|
-
}
|
|
83
|
-
console.log("\nFetching latest state from origin...");
|
|
84
|
-
git("fetch", "origin");
|
|
66
|
+
const bumps = [];
|
|
67
|
+
if (packages.length > 0) {
|
|
68
|
+
console.log("\nSelect a version bump for each package (or skip):");
|
|
69
|
+
bumps.push(...chooseForEach(packages));
|
|
70
|
+
if (bumps.length === 0) {
|
|
71
|
+
console.log("\nNo packages selected for release. Aborted.");
|
|
72
|
+
process.exit(0);
|
|
73
|
+
}
|
|
74
|
+
console.log("\nBumping versions and updating CHANGELOGs...");
|
|
75
|
+
for (const { package: pkg, bump } of bumps) try {
|
|
76
|
+
runPackageRelease(pkg.path, bump);
|
|
77
|
+
} catch {
|
|
78
|
+
console.log(`No versioning configured for ${pkg.name} — skipping.`);
|
|
79
|
+
}
|
|
80
|
+
} else console.log("No publishable packages — skipping versioning.");
|
|
85
81
|
console.log("\nMerging hotfix into production...");
|
|
86
82
|
git("checkout", "production");
|
|
87
83
|
git("pull", "--ff-only", "origin", "production");
|
|
@@ -101,9 +97,11 @@ function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
|
101
97
|
try {
|
|
102
98
|
git("push", "origin", "--delete", currentBranch);
|
|
103
99
|
} catch {}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
100
|
+
if (bumps.length > 0) {
|
|
101
|
+
const shipped = bumps.map((b) => b.package.name).join(", ");
|
|
102
|
+
console.log(`\n✅ Hotfix ${shipped} shipped to production → staging → main`);
|
|
103
|
+
console.log("CI will publish changed packages to npm.");
|
|
104
|
+
} else console.log("\n✅ Hotfix shipped to production → staging → main.");
|
|
107
105
|
console.log("You are on main and ready to keep working.");
|
|
108
106
|
process.exit(0);
|
|
109
107
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git, c as
|
|
2
|
+
import { a as git, c as runQualityChecks, i as getWorkspacePackages, n as chooseForEach, o as gitRead, r as confirm, s as runPackageRelease, t as branchExists } from "./utils-BPxpSgv6.mjs";
|
|
3
3
|
//#region src/bin/ship/production.ts
|
|
4
4
|
/**
|
|
5
5
|
* ship-production: Promote main to production following the GitLab workflow.
|
|
@@ -51,29 +51,25 @@ function runShipProduction(cwd = process.cwd()) {
|
|
|
51
51
|
}
|
|
52
52
|
console.log("✅ Quality checks passed");
|
|
53
53
|
const packages = getWorkspacePackages(cwd);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
runScript(`release:${highestBump}`);
|
|
74
|
-
} catch {
|
|
75
|
-
console.log("No release script found - skipping versioning (non-publishing repo)");
|
|
76
|
-
}
|
|
54
|
+
const bumps = [];
|
|
55
|
+
if (packages.length > 0) {
|
|
56
|
+
console.log("\nSelect a version bump for each package (or skip):");
|
|
57
|
+
bumps.push(...chooseForEach(packages));
|
|
58
|
+
if (bumps.length === 0) {
|
|
59
|
+
console.log("\nNo packages selected for release. Aborted.");
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
if (!confirm(`\nShip to production?\n${bumps.map((b) => ` ${b.package.name} → ${b.bump}`).join("\n")}\n`)) {
|
|
63
|
+
console.log("Aborted.");
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
console.log("\nBumping versions and updating CHANGELOGs...");
|
|
67
|
+
for (const { package: pkg, bump } of bumps) try {
|
|
68
|
+
runPackageRelease(pkg.path, bump);
|
|
69
|
+
} catch {
|
|
70
|
+
console.log(`No versioning configured for ${pkg.name} — skipping.`);
|
|
71
|
+
}
|
|
72
|
+
} else if (!confirm("\nNo publishable packages — ship commits to production?\n")) process.exit(0);
|
|
77
73
|
console.log("\nMerging main into production...");
|
|
78
74
|
git("checkout", "production");
|
|
79
75
|
git("pull", "--ff-only", "origin", "production");
|
|
@@ -86,8 +82,10 @@ function runShipProduction(cwd = process.cwd()) {
|
|
|
86
82
|
git("push", "origin", "staging");
|
|
87
83
|
git("checkout", "main");
|
|
88
84
|
git("push", "--follow-tags", "origin", "main");
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
if (bumps.length > 0) {
|
|
86
|
+
const shipped = bumps.map((b) => b.package.name).join(", ");
|
|
87
|
+
console.log(`\n✅ Shipped ${shipped} to production. CI will publish changed packages to npm.`);
|
|
88
|
+
} else console.log("\n✅ Commits shipped to production.");
|
|
91
89
|
console.log("You are on main and ready to keep working.");
|
|
92
90
|
}
|
|
93
91
|
//#endregion
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as git,
|
|
2
|
+
import { a as git, c as runQualityChecks, o as gitRead, t as branchExists } from "./utils-BPxpSgv6.mjs";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/bin/ship/staging.ts
|
|
@@ -19,6 +19,13 @@ const runScript = (script) => {
|
|
|
19
19
|
console.log(`$ pnpm ${script}`);
|
|
20
20
|
execSync(`pnpm ${script}`, { stdio: "inherit" });
|
|
21
21
|
};
|
|
22
|
+
const runPackageRelease = (pkgPath, bump) => {
|
|
23
|
+
console.log(`$ commit-and-tag-version --release-as ${bump}`);
|
|
24
|
+
execSync(`pnpm exec commit-and-tag-version --release-as ${bump}`, {
|
|
25
|
+
cwd: pkgPath,
|
|
26
|
+
stdio: "inherit"
|
|
27
|
+
});
|
|
28
|
+
};
|
|
22
29
|
const runQualityChecks = () => {
|
|
23
30
|
runScript("lint");
|
|
24
31
|
runScript("typecheck");
|
|
@@ -120,4 +127,4 @@ const choose = (prompt, options, ttyPath = "/dev/tty") => {
|
|
|
120
127
|
return chosen.value;
|
|
121
128
|
};
|
|
122
129
|
//#endregion
|
|
123
|
-
export { git as a,
|
|
130
|
+
export { git as a, runQualityChecks as c, getWorkspacePackages as i, chooseForEach as n, gitRead as o, confirm as r, runPackageRelease as s, branchExists as t };
|
package/docs/en/tools/biome.md
CHANGED
|
@@ -11,7 +11,7 @@ area: "dev"
|
|
|
11
11
|
|
|
12
12
|
# Biome
|
|
13
13
|
|
|
14
|
-
[Biome](https://biomejs.dev/) is the linter and formatter every Regardio package reaches for. Configuration is centralised through `@regardio/dev
|
|
14
|
+
[Biome](https://biomejs.dev/) is the linter and formatter every Regardio package reaches for. Configuration is centralised through `@regardio/dev`.
|
|
15
15
|
|
|
16
16
|
## Configuration
|
|
17
17
|
|
|
@@ -29,34 +29,22 @@ A `biome.jsonc` at the package root is all it takes:
|
|
|
29
29
|
```json
|
|
30
30
|
{
|
|
31
31
|
"scripts": {
|
|
32
|
-
"fix": "
|
|
33
|
-
"
|
|
34
|
-
"lint": "exec-s lint:md lint:biome",
|
|
35
|
-
"lint:biome": "lint-biome check ."
|
|
32
|
+
"fix:biome": "biome check --write --unsafe .",
|
|
33
|
+
"lint:biome": "biome check ."
|
|
36
34
|
}
|
|
37
35
|
}
|
|
38
36
|
```
|
|
39
37
|
|
|
40
|
-
## CLI wrapper
|
|
41
|
-
|
|
42
|
-
Use `lint-biome` rather than `biome` directly — the wrapper keeps behaviour consistent across the monorepo.
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
lint-biome check . # Check for issues
|
|
46
|
-
lint-biome check --write . # Fix the auto-fixable
|
|
47
|
-
lint-biome format . # Format only
|
|
48
|
-
```
|
|
49
|
-
|
|
50
38
|
## `package.json` handling
|
|
51
39
|
|
|
52
|
-
The Biome preset leaves `package.json` alone. For those files, `
|
|
40
|
+
The Biome preset leaves `package.json` alone. For those files, use `sort-package-json`:
|
|
53
41
|
|
|
54
42
|
1. Sorts `package.json` using the well-known field order
|
|
55
43
|
2. Fixes export-condition order (`types` before `default` for TypeScript)
|
|
56
44
|
|
|
57
45
|
```bash
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
pnpm sort-package-json # Sort package.json in current directory
|
|
47
|
+
pnpm sort-package-json path/to/pkg # Sort a specific package.json
|
|
60
48
|
```
|
|
61
49
|
|
|
62
50
|
It runs automatically as part of `pnpm fix`.
|
package/docs/en/tools/husky.md
CHANGED
|
@@ -20,7 +20,7 @@ Husky configures itself through the `prepare` script:
|
|
|
20
20
|
```json
|
|
21
21
|
{
|
|
22
22
|
"scripts": {
|
|
23
|
-
"prepare": "
|
|
23
|
+
"prepare": "husky"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
```
|
|
@@ -35,7 +35,8 @@ Validates commit messages against the conventional-commit format:
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
#!/bin/sh
|
|
38
|
-
|
|
38
|
+
. "$(dirname "$0")/_/husky.sh"
|
|
39
|
+
commitlint --edit $1
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
### `pre-commit` (optional)
|
|
@@ -47,10 +48,6 @@ Runs linting before a commit lands:
|
|
|
47
48
|
pnpm lint
|
|
48
49
|
```
|
|
49
50
|
|
|
50
|
-
## CLI wrapper
|
|
51
|
-
|
|
52
|
-
`exec-husky` takes the place of `husky` directly — the wrapper handles monorepo scenarios that bare Husky doesn't.
|
|
53
|
-
|
|
54
51
|
## Bypassing hooks
|
|
55
52
|
|
|
56
53
|
In the rare case where a hook truly has to be skipped:
|
|
@@ -11,7 +11,7 @@ area: "dev"
|
|
|
11
11
|
|
|
12
12
|
# Markdownlint
|
|
13
13
|
|
|
14
|
-
Markdownlint checks Markdown structure and formatting. The rules live in `@regardio/dev
|
|
14
|
+
Markdownlint checks Markdown structure and formatting. The rules live in `@regardio/dev`.
|
|
15
15
|
|
|
16
16
|
## Configuration
|
|
17
17
|
|
|
@@ -28,21 +28,12 @@ A `.markdownlint.json` at the package root:
|
|
|
28
28
|
```json
|
|
29
29
|
{
|
|
30
30
|
"scripts": {
|
|
31
|
-
"fix:md": "
|
|
32
|
-
"lint:md": "
|
|
31
|
+
"fix:md": "markdownlint-cli2 --fix \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\"",
|
|
32
|
+
"lint:md": "markdownlint-cli2 \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\""
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
## CLI wrapper
|
|
38
|
-
|
|
39
|
-
Use `lint-md` rather than `markdownlint-cli2` directly:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
lint-md "**/*.md" # Check all Markdown files
|
|
43
|
-
lint-md --fix "**/*.md" # Auto-fix what can be auto-fixed
|
|
44
|
-
```
|
|
45
|
-
|
|
46
37
|
## Key rules
|
|
47
38
|
|
|
48
39
|
| Rule | Description |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
|
|
3
3
|
title: "Release Workflow"
|
|
4
|
-
description: "
|
|
5
|
-
publishedAt: 2026-
|
|
4
|
+
description: "GitLab-flow release workflow for Regardio repos — branches mirror environments, ship tooling drives versioning, CI carries it out."
|
|
5
|
+
publishedAt: 2026-05-07
|
|
6
6
|
language: "en"
|
|
7
7
|
status: "published"
|
|
8
8
|
kind: "guide"
|
|
@@ -11,115 +11,92 @@ area: "dev"
|
|
|
11
11
|
|
|
12
12
|
# Release Workflow
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Branches mirror environments. `main` is where development happens. `staging` reflects what is deployed to the staging server. `production` reflects what is live. Version bumps happen locally at ship time — CI on `production` carries the result out (publishes to npm or deploys to a server).
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## The Three-Branch Model
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
```text
|
|
19
|
+
main ──── ship:staging ────► staging (optional staging server)
|
|
20
|
+
│
|
|
21
|
+
└──── ship:production ──► production (live / npm)
|
|
22
|
+
│
|
|
23
|
+
sync back to staging
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
| Branch | Contains | CI role |
|
|
27
|
+
|--------|----------|---------|
|
|
28
|
+
| `main` | All committed, tested work | None (Husky runs local gates on commit) |
|
|
29
|
+
| `staging` | Deployed to staging server or preview | Verify + deploy to staging |
|
|
30
|
+
| `production` | Only shipped, versioned releases | Verify + deploy or publish |
|
|
31
|
+
|
|
32
|
+
**Why version bumps happen locally, not in CI.** Hotfixes start from `production` and must write their version bump back up through `staging` and `main` in one pass. Keeping the bump local means both the normal ship path and the hotfix path behave identically — there is no special case in CI.
|
|
33
|
+
|
|
34
|
+
## Hotfix Flow
|
|
35
|
+
|
|
36
|
+
A hotfix is an urgent fix that must go to production immediately, without pulling in unreleased changes from `main`.
|
|
19
37
|
|
|
20
38
|
```text
|
|
21
|
-
|
|
39
|
+
production ──► hotfix/<name> (fix here with conventional commits)
|
|
40
|
+
│
|
|
41
|
+
└──► production ──► staging ──► main
|
|
22
42
|
```
|
|
23
43
|
|
|
24
|
-
|
|
25
|
-
- **`staging`** — optional validation environment. No versioning happens here.
|
|
26
|
-
- **`production`** — versioned, published code only.
|
|
44
|
+
`ship:hotfix start <name>` creates the branch from `production`, not from `main`. This is deliberate: it avoids accidentally shipping work-in-progress that has not yet been reviewed for production.
|
|
27
45
|
|
|
28
|
-
|
|
46
|
+
`ship:hotfix finish` versions the fix, merges it into `production`, then propagates through `staging` and back to `main` so nothing is lost.
|
|
29
47
|
|
|
30
|
-
##
|
|
48
|
+
## Commands
|
|
31
49
|
|
|
32
|
-
|
|
50
|
+
| Command | Purpose |
|
|
51
|
+
|---------|---------|
|
|
52
|
+
| `pnpm ship:staging` | Fast-forward `main` → `staging`, push. Triggers staging CI. |
|
|
53
|
+
| `pnpm ship:production` | Version selected packages (if any), fast-forward `main` → `production`, sync `staging`. Triggers production CI. |
|
|
54
|
+
| `pnpm ship:hotfix start <name>` | Create `hotfix/<name>` from `production`. |
|
|
55
|
+
| `pnpm ship:hotfix finish` | Version selected packages (if any), merge hotfix → `production` → `staging` → `main`. |
|
|
33
56
|
|
|
34
|
-
|
|
35
|
-
2. **You choose the bump per package at ship time.** When running `ship-production` or `ship-hotfix finish`, every publishable package is listed and you assign it `0` (skip), `1` (patch), `2` (minor), or `3` (major). Packages you skip are untouched. The conventional commits in the log inform each choice; the decisions are always explicit.
|
|
36
|
-
3. **Version numbers are a production guarantee.** Bumps are applied only at `ship-production` time. Every version tag in git and every release on npm corresponds to code that has been validated and shipped.
|
|
37
|
-
4. **Staging is optional.** You can ship directly from `main` to `production`. Use `ship-staging` when you want to test changes in a staging environment first.
|
|
38
|
-
5. **Tests are a local gate, not a CI gate.** Quality checks (`lint`, `typecheck`, `test`) run on your machine before any commit is made. Broken code cannot enter the repository.
|
|
39
|
-
6. **You always land back on `main`.** Every command returns you to `main` when it finishes.
|
|
57
|
+
## Versioning — Per-Package, at Ship Time
|
|
40
58
|
|
|
41
|
-
|
|
59
|
+
`ship:production` and `ship:hotfix finish` discover all publishable workspace packages (those without `"private": true`) and ask you to assign a bump type to each:
|
|
42
60
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
│ ff-merge main → staging │
|
|
48
|
-
└──────────────────────────┘
|
|
49
|
-
│
|
|
50
|
-
(validated in staging)
|
|
51
|
-
│
|
|
52
|
-
▼
|
|
53
|
-
ship-production
|
|
54
|
-
┌──────────────────────────┐
|
|
55
|
-
│ quality checks on main │
|
|
56
|
-
│ select packages/bumps │
|
|
57
|
-
│ pnpm release:<type>* │
|
|
58
|
-
main ───────────┤ bumps version + ├──► production
|
|
59
|
-
│ rewrites CHANGELOG.md │
|
|
60
|
-
│ ff-merge main → prod │
|
|
61
|
-
│ ff-merge prod → staging │
|
|
62
|
-
│ push --follow-tags main │
|
|
63
|
-
└──────────────────────────┘
|
|
64
|
-
│
|
|
65
|
-
(CI: pnpm -r publish → npm)
|
|
66
|
-
▼
|
|
67
|
-
production
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ship-hotfix start fix-name
|
|
71
|
-
┌──────────────────────────┐
|
|
72
|
-
production ─────┤ create hotfix/fix-name ├──► hotfix/fix-name
|
|
73
|
-
└──────────────────────────┘
|
|
74
|
-
│
|
|
75
|
-
(author fixes with conventional commits)
|
|
76
|
-
│
|
|
77
|
-
ship-hotfix finish
|
|
78
|
-
┌──────────────────────────┐
|
|
79
|
-
│ quality checks │
|
|
80
|
-
│ pnpm release:<type>* │
|
|
81
|
-
hotfix/fix-name ┤ bumps version + ├──► production → staging → main
|
|
82
|
-
│ rewrites CHANGELOG.md │
|
|
83
|
-
│ merge to production │
|
|
84
|
-
│ propagate to staging │
|
|
85
|
-
│ propagate to main │
|
|
86
|
-
└──────────────────────────┘
|
|
87
|
-
```
|
|
61
|
+
- `0` — skip (no version change)
|
|
62
|
+
- `1` — patch
|
|
63
|
+
- `2` — minor
|
|
64
|
+
- `3` — major
|
|
88
65
|
|
|
89
|
-
|
|
66
|
+
For each selected package, `commit-and-tag-version` runs from within that package's directory, reading the package's own `.versionrc.json`. It bumps the `version` field in `package.json`, rewrites `CHANGELOG.md` to include only commits that touched files under that package's directory (`gitRawCommitsOpts.path: "."`), commits the bump, and creates a scoped tag (e.g. `@regardio/dev@v1.2.0`).
|
|
90
67
|
|
|
91
|
-
|
|
92
|
-
|--------|----------|-----------------|
|
|
93
|
-
| `main` | All committed, tested work | Only after `ship-production` / `ship-hotfix finish` |
|
|
94
|
-
| `staging` | Synced from `production` after each ship, or from `main` via `ship-staging` | After a ship propagates |
|
|
95
|
-
| `production` | Only shipped, versioned releases | Yes — always |
|
|
68
|
+
If a repo has no publishable packages (all are `private: true` or there are no `.versionrc.json` files), the versioning UI is skipped entirely and `ship:production` proceeds directly to the git merge.
|
|
96
69
|
|
|
97
|
-
|
|
70
|
+
## CI — What Happens on Each Branch
|
|
98
71
|
|
|
99
|
-
CI
|
|
72
|
+
CI behaviour differs by what the repo produces.
|
|
100
73
|
|
|
101
|
-
|
|
102
|
-
2. Runs `pnpm -r publish --access public --no-git-checks` — publishes every workspace package whose current version is not yet on npm, skipping `private: true` packages
|
|
103
|
-
3. Tags are already present (pushed by `ship-production` via `--follow-tags`)
|
|
74
|
+
### Publishing monorepos (e.g. `commons`)
|
|
104
75
|
|
|
105
|
-
|
|
76
|
+
- **staging**: Run typecheck and tests. Nothing is published.
|
|
77
|
+
- **production**: Run typecheck, tests, then `pnpm -r publish --access public --no-git-checks`. Only packages whose current version is not yet on npm are published — packages with `"private": true` are skipped automatically.
|
|
106
78
|
|
|
107
|
-
|
|
108
|
-
|---------|-------|---------|
|
|
109
|
-
| `pnpm release` | auto | Bump version from conventional commits, update CHANGELOG.md, commit |
|
|
110
|
-
| `pnpm release:patch` | explicit | Force a patch bump |
|
|
111
|
-
| `pnpm release:minor` | explicit | Force a minor bump |
|
|
112
|
-
| `pnpm release:major` | explicit | Force a major bump |
|
|
113
|
-
| `ship-staging` | `ship-staging` | (Optional) Deploy changes to staging for testing |
|
|
114
|
-
| `ship-production` | `ship-production` | Bump version, merge main → production, trigger publish |
|
|
115
|
-
| `ship-hotfix start <name>` | `ship-hotfix start <name>` | Create a hotfix branch from production |
|
|
116
|
-
| `ship-hotfix finish` | `ship-hotfix finish` | Bump version, propagate hotfix through production → staging → main |
|
|
79
|
+
### Server-deployed apps with Docker (e.g. `brass-berlin`)
|
|
117
80
|
|
|
118
|
-
|
|
81
|
+
- **staging**: Run tests, build the Docker image, push to the container registry, trigger a staging deploy (e.g. Coolify webhook).
|
|
82
|
+
- **production**: Run typecheck + tests, build and push the Docker image, trigger a production deploy.
|
|
83
|
+
|
|
84
|
+
### Cloudflare Workers apps (e.g. `magic-of-open-source`)
|
|
85
|
+
|
|
86
|
+
- **staging**: Run tests, then `wrangler versions upload --preview-alias preview`.
|
|
87
|
+
- **production**: Run typecheck + tests, then `wrangler deploy`.
|
|
88
|
+
|
|
89
|
+
### Documentation / vocabulary repos (e.g. `system`)
|
|
119
90
|
|
|
120
|
-
|
|
91
|
+
- **staging** and **production**: Verify the repo is well-formed (build, typecheck, test). No publishing or deployment — the repo is distributed via git clone, not npm.
|
|
92
|
+
|
|
93
|
+
### Static sites without a configured deployment (e.g. `bam-berlin-org`)
|
|
94
|
+
|
|
95
|
+
- **staging** and **production**: Run tests, typecheck (production only), and build. A deployment step is left as a TODO until the hosting platform is chosen.
|
|
96
|
+
|
|
97
|
+
## Typical Release Flow
|
|
121
98
|
|
|
122
|
-
|
|
99
|
+
### 1. Develop on `main` with conventional commits
|
|
123
100
|
|
|
124
101
|
```bash
|
|
125
102
|
feat: add user authentication # → minor bump
|
|
@@ -127,111 +104,114 @@ fix: resolve login redirect loop # → patch bump
|
|
|
127
104
|
feat!: redesign authentication API # → major bump
|
|
128
105
|
```
|
|
129
106
|
|
|
130
|
-
### 2.
|
|
107
|
+
### 2. (Optional) Test in staging first
|
|
131
108
|
|
|
132
109
|
```bash
|
|
133
|
-
pnpm ship:
|
|
110
|
+
pnpm ship:staging
|
|
134
111
|
```
|
|
135
112
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
1. Guard: must be on `main`, working tree clean
|
|
139
|
-
2. Fetch and verify `staging` + `production` branches exist
|
|
140
|
-
3. Show commits to be shipped
|
|
141
|
-
4. Run full quality suite on `main` — aborts on failure
|
|
142
|
-
5. For each publishable package: choose `0 (skip) / 1 (patch) / 2 (minor) / 3 (major)`
|
|
143
|
-
6. Confirm the planned bumps — abort if declined or all skipped
|
|
144
|
-
7. Run `pnpm release:<type>` at the root (highest bump type) — bumps versions, rewrites `CHANGELOG.md`, commits. If no release script exists, skip versioning.
|
|
145
|
-
8. Fast-forward merge `main` into `production` and push
|
|
146
|
-
9. Sync `staging` with `production`
|
|
147
|
-
10. Push `main` with `--follow-tags` to push all new version tags
|
|
148
|
-
11. Return to `main`
|
|
113
|
+
Deploys `main` to `staging` without touching versions.
|
|
149
114
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
### Option: Test in staging first
|
|
115
|
+
### 3. Ship to production
|
|
153
116
|
|
|
154
117
|
```bash
|
|
155
|
-
pnpm ship:
|
|
118
|
+
pnpm ship:production
|
|
156
119
|
```
|
|
157
120
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
121
|
+
1. Guard: must be on `main`, working tree clean.
|
|
122
|
+
2. Fetch and verify `staging` and `production` branches exist.
|
|
123
|
+
3. Show commits to be shipped.
|
|
124
|
+
4. Run full quality suite — aborts on failure.
|
|
125
|
+
5. If publishable packages exist: assign a bump type to each; confirm the planned bumps.
|
|
126
|
+
6. If no publishable packages: confirm whether to ship (versioning is skipped).
|
|
127
|
+
7. For each selected package: run `commit-and-tag-version --release-as <type>` from within the package directory.
|
|
128
|
+
8. Fast-forward merge `main` → `production`, push.
|
|
129
|
+
9. Sync `staging` with `production`, push.
|
|
130
|
+
10. Push `main` with `--follow-tags` to carry the new version tags.
|
|
131
|
+
11. Return to `main`.
|
|
161
132
|
|
|
162
|
-
|
|
133
|
+
### 4. Hotfix flow
|
|
163
134
|
|
|
164
135
|
```bash
|
|
165
136
|
pnpm ship:hotfix start fix-auth-bug
|
|
166
|
-
# apply
|
|
137
|
+
# apply fix with conventional commits
|
|
167
138
|
git add . && git commit -m "fix: ..."
|
|
168
139
|
pnpm ship:hotfix finish
|
|
169
140
|
```
|
|
170
141
|
|
|
171
|
-
`finish`
|
|
142
|
+
`finish` fetches origin, runs quality checks, assigns bump types (if publishable packages exist), versions the fix, then merges `hotfix → production → staging → main` and deletes the hotfix branch.
|
|
172
143
|
|
|
173
144
|
## Adoption
|
|
174
145
|
|
|
175
|
-
|
|
146
|
+
### 1. Add ship scripts to `package.json`
|
|
176
147
|
|
|
177
|
-
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"scripts": {
|
|
151
|
+
"ship:hotfix": "ship-hotfix",
|
|
152
|
+
"ship:production": "ship-production",
|
|
153
|
+
"ship:staging": "ship-staging"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
178
157
|
|
|
179
|
-
|
|
158
|
+
For repos that publish to npm, also add:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"scripts": {
|
|
163
|
+
"release": "commit-and-tag-version",
|
|
164
|
+
"release:major": "commit-and-tag-version --release-as major",
|
|
165
|
+
"release:minor": "commit-and-tag-version --release-as minor",
|
|
166
|
+
"release:patch": "commit-and-tag-version --release-as patch"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
180
170
|
|
|
181
|
-
|
|
182
|
-
{
|
|
183
|
-
"scripts": {
|
|
184
|
-
"release": "commit-and-tag-version",
|
|
185
|
-
"release:major": "commit-and-tag-version --release-as major",
|
|
186
|
-
"release:minor": "commit-and-tag-version --release-as minor",
|
|
187
|
-
"release:patch": "commit-and-tag-version --release-as patch",
|
|
188
|
-
"ship:hotfix": "ship-hotfix",
|
|
189
|
-
"ship:production": "ship-production",
|
|
190
|
-
"ship:staging": "ship-staging"
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
171
|
+
### 2. Add `.versionrc.json` to each publishable package
|
|
194
172
|
|
|
195
|
-
|
|
173
|
+
```bash
|
|
174
|
+
cp node_modules/@regardio/dev/templates/versionrc/.versionrc.json packages/my-pkg/.versionrc.json
|
|
175
|
+
# then set "tagPrefix": "@my-scope/my-pkg@v" inside that file
|
|
176
|
+
```
|
|
196
177
|
|
|
197
|
-
|
|
178
|
+
Every `.versionrc.json` must include `"gitRawCommitsOpts": { "path": "." }` to filter the git log to commits that actually touched files under that package's directory. Without this, the changelog accumulates noise from unrelated packages.
|
|
198
179
|
|
|
199
|
-
|
|
200
|
-
cp node_modules/@regardio/dev/templates/versionrc/.versionrc.json packages/my-pkg/.versionrc.json
|
|
201
|
-
# then set "tagPrefix": "@my-scope/my-pkg@v" inside that file
|
|
202
|
-
```
|
|
180
|
+
For a single-package repo, place `.versionrc.json` at the root instead.
|
|
203
181
|
|
|
204
|
-
|
|
182
|
+
### 3. Create the branches
|
|
205
183
|
|
|
206
|
-
|
|
184
|
+
```bash
|
|
185
|
+
git checkout -b staging && git push -u origin staging
|
|
186
|
+
git checkout -b production && git push -u origin production
|
|
187
|
+
git checkout main
|
|
188
|
+
```
|
|
207
189
|
|
|
208
|
-
|
|
209
|
-
git checkout -b staging && git push -u origin staging
|
|
210
|
-
git checkout -b production && git push -u origin production
|
|
211
|
-
git checkout main
|
|
212
|
-
```
|
|
190
|
+
### 4. Copy and adapt the CI workflow
|
|
213
191
|
|
|
214
|
-
|
|
192
|
+
```bash
|
|
193
|
+
# Forgejo / Codeberg
|
|
194
|
+
mkdir -p .forgejo/workflows
|
|
195
|
+
cp node_modules/@regardio/dev/templates/github/release.yml .forgejo/workflows/production.yml
|
|
215
196
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
197
|
+
# GitHub
|
|
198
|
+
mkdir -p .github/workflows
|
|
199
|
+
cp node_modules/@regardio/dev/templates/github/release.yml .github/workflows/release.yml
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Adapt the workflow for your repo type (see CI section above). The template covers the npm-publishing case; server-deployed and Cloudflare repos need the deployment step replaced.
|
|
220
203
|
|
|
221
|
-
|
|
222
|
-
mkdir -p .forgejo/workflows
|
|
223
|
-
cp node_modules/@regardio/dev/templates/github/release.yml .forgejo/workflows/release.yml
|
|
224
|
-
```
|
|
204
|
+
### 5. First publish of any new package must be done locally
|
|
225
205
|
|
|
226
|
-
|
|
206
|
+
```bash
|
|
207
|
+
pnpm build && npm publish --access public
|
|
208
|
+
```
|
|
227
209
|
|
|
228
|
-
|
|
229
|
-
pnpm build && npm publish --access public
|
|
230
|
-
```
|
|
210
|
+
CI publishes subsequent versions; the very first one requires a local publish because the package does not yet exist on npm.
|
|
231
211
|
|
|
232
212
|
## Quality Gates
|
|
233
213
|
|
|
234
|
-
Every ship command enforces the same gates
|
|
214
|
+
Every ship command enforces the same gates before any commit or merge:
|
|
235
215
|
|
|
236
216
|
```bash
|
|
237
217
|
pnpm build # Must succeed
|
|
@@ -241,9 +221,9 @@ pnpm test # Must succeed
|
|
|
241
221
|
|
|
242
222
|
## Private Packages and Non-Publishing Repos
|
|
243
223
|
|
|
244
|
-
Packages
|
|
224
|
+
Packages with `"private": true` are skipped by `pnpm -r publish` automatically.
|
|
245
225
|
|
|
246
|
-
For
|
|
226
|
+
For repos where all packages are private (channels, deployment repos), the ship tools skip the versioning UI entirely and only perform the git merges. This makes the same tooling reusable across publishing and non-publishing repositories.
|
|
247
227
|
|
|
248
228
|
## Related
|
|
249
229
|
|
package/docs/en/tools/vitest.md
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "@regardio/dev",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.5.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Regardio development presets: biome, typescript, commitlint, markdownlint, vitest, playwright, sqlfluff, husky, and GitLab-flow ship tooling",
|
|
7
7
|
"keywords": [
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
],
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@total-typescript/ts-reset": "0.6.1",
|
|
77
|
-
"@types/node": "25.6.
|
|
77
|
+
"@types/node": "25.6.1",
|
|
78
78
|
"@vitest/coverage-v8": "4.1.5",
|
|
79
79
|
"tsdown": "0.22.0",
|
|
80
80
|
"vitest": "4.1.5"
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"fix:biome": "biome check --write --unsafe .",
|
|
102
102
|
"fix:md": "markdownlint-cli2 --config ../../.markdownlint-cli2.jsonc --fix",
|
|
103
103
|
"fix:pkg": "sort-package-json",
|
|
104
|
-
"lint": "run-
|
|
104
|
+
"lint": "run-p lint:*",
|
|
105
105
|
"lint:biome": "biome check .",
|
|
106
106
|
"lint:md": "markdownlint-cli2 --config ../../.markdownlint-cli2.jsonc",
|
|
107
107
|
"lint:pkg": "sort-package-json --check",
|