@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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as git, c as runScript, i as getWorkspacePackages, n as chooseForEach, o as gitRead, s as runQualityChecks, t as branchExists } from "./utils-rnvdLrkF.mjs";
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
- if (packages.length === 0) {
65
- console.error("No publishable workspace packages found.");
66
- process.exit(1);
67
- }
68
- console.log("\nSelect a version bump for each package (or skip):");
69
- const bumps = 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
- const hasMajor = bumps.some((b) => b.bump === "major");
76
- const hasMinor = bumps.some((b) => b.bump === "minor");
77
- const highestBump = hasMajor ? "major" : hasMinor ? "minor" : "patch";
78
- try {
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
- const shipped = bumps.map((b) => b.package.name).join(", ");
105
- console.log(`\n✅ Hotfix ${shipped} shipped to production staging → main`);
106
- console.log("CI will publish changed packages to npm.");
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 runScript, i as getWorkspacePackages, n as chooseForEach, o as gitRead, r as confirm, s as runQualityChecks, t as branchExists } from "./utils-rnvdLrkF.mjs";
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
- if (packages.length === 0) {
55
- console.error("No publishable workspace packages found.");
56
- process.exit(1);
57
- }
58
- console.log("\nSelect a version bump for each package (or skip):");
59
- const bumps = chooseForEach(packages);
60
- if (bumps.length === 0) {
61
- console.log("\nNo packages selected for release. Aborted.");
62
- process.exit(0);
63
- }
64
- if (!confirm(`\nShip to production?\n${bumps.map((b) => ` ${b.package.name} → ${b.bump}`).join("\n")}\n`)) {
65
- console.log("Aborted.");
66
- process.exit(0);
67
- }
68
- console.log("\nBumping versions and updating CHANGELOGs...");
69
- const hasMajor = bumps.some((b) => b.bump === "major");
70
- const hasMinor = bumps.some((b) => b.bump === "minor");
71
- const highestBump = hasMajor ? "major" : hasMinor ? "minor" : "patch";
72
- try {
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
- const shipped = bumps.map((b) => b.package.name).join(", ");
90
- console.log(`\n✅ Shipped ${shipped} to production. CI will publish changed packages to npm.`);
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, o as gitRead, s as runQualityChecks, t as branchExists } from "./utils-rnvdLrkF.mjs";
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, runScript as c, getWorkspacePackages as i, chooseForEach as n, gitRead as o, confirm as r, runQualityChecks as s, branchExists as t };
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 };
@@ -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`, and wrapper commands keep every package behaving the same way.
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": "exec-s fix:pkg fix:md fix:biome",
33
- "fix:biome": "lint-biome check --write --unsafe .",
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, `lint-package` does the work:
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
- lint-package # Sort package.json in current directory
59
- lint-package path/to/pkg # Sort a specific package.json
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`.
@@ -20,7 +20,7 @@ Husky configures itself through the `prepare` script:
20
20
  ```json
21
21
  {
22
22
  "scripts": {
23
- "prepare": "exec-husky"
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
- pnpm lint-commit --edit $1
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`; projects reach for the `lint-md` wrapper rather than calling the tool directly.
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": "lint-md --fix \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\"",
32
- "lint:md": "lint-md \"**/*.md\" \"**/*.mdx\" \"!**/node_modules/**\" \"!**/dist/**\""
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: "Branch-based release workflow for Regardio packagesmain staging production, driven by commit-and-tag-version."
5
- publishedAt: 2026-04-17
4
+ description: "GitLab-flow release workflow for Regardio reposbranches 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
- Branch-based release workflow for Regardio packages. `main` `staging` `production`. `commit-and-tag-version` reads conventional commits to determine the version bump; quality gates run locally before any branch promotion. Nothing broken reaches a shared branch.
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
- ## Overview
16
+ ## The Three-Branch Model
17
17
 
18
- Branches mirror deployment environments:
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
- main staging (optional) production
39
+ production ──► hotfix/<name> (fix here with conventional commits)
40
+
41
+ └──► production ──► staging ──► main
22
42
  ```
23
43
 
24
- - **`main`** active development, always deployable. Version bumps happen at ship time, not per-commit.
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
- When `ship-production` runs, it discovers all publishable workspace packages and asks you to assign a bump type — `0` (skip), `1` (patch), `2` (minor), or `3` (major) to each one individually. Packages you skip are left untouched. It determines the highest bump type across all selected packages and runs `pnpm release:<type>` at the root, which bumps versions, rewrites `CHANGELOG.md`, and commits on `main`. If no release script exists (non-publishing repos like channels), versioning is skipped entirely and only the git merges happen. The branches merge once after all bumps are applied. CI on `production` publishes only the packages whose version changed.
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
- ## How It Works
48
+ ## Commands
31
49
 
32
- ### Design principles
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
- 1. **Branches mirror environments.** `staging` reflects what is deployed to the staging server (when used); `production` always reflects what is published to npm. There is never ambiguity about what is running where.
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
- ### Full flow diagram
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
- ```text
44
- ship-staging (OPTIONAL)
45
- ┌──────────────────────────┐
46
- main ───────────┤ quality checks pass ├──► staging (pushed)
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
- ### What each branch represents at any point in time
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
- | Branch | Contains | Version bumped? |
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
- ### CI role
70
+ ## CI — What Happens on Each Branch
98
71
 
99
- CI is intentionally minimal. On push to `production` it:
72
+ CI behaviour differs by what the repo produces.
100
73
 
101
- 1. Installs dependencies and builds
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
- ## Commands
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
- | Command | Usage | Purpose |
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
- ## Typical Release Flow
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
- ### 1. Develop with conventional commits
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
- On `main`, use conventional commits so the bump type is inferred automatically:
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. Ship to production
107
+ ### 2. (Optional) Test in staging first
131
108
 
132
109
  ```bash
133
- pnpm ship:production
110
+ pnpm ship:staging
134
111
  ```
135
112
 
136
- This will:
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
- CI on `production` runs `pnpm -r publish` to push changed packages to npm.
151
-
152
- ### Option: Test in staging first
115
+ ### 3. Ship to production
153
116
 
154
117
  ```bash
155
- pnpm ship:staging
118
+ pnpm ship:production
156
119
  ```
157
120
 
158
- Deploys `main` to `staging` without touching versions. Useful for validating in a staging environment before shipping to production.
159
-
160
- ## Hotfix Flow
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
- For urgent fixes that must go directly to production:
133
+ ### 4. Hotfix flow
163
134
 
164
135
  ```bash
165
136
  pnpm ship:hotfix start fix-auth-bug
166
- # apply your fix with conventional commits
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` asks you to assign a bump type (`0` (skip), `1` (patch), `2` (minor), or `3` (major)) to each publishable package, runs `pnpm release:<type>` at the root (highest bump type) to bump versions and update `CHANGELOG.md`. If no release script exists, versioning is skipped. Then it merges `hotfix → production → staging → main` and deletes the hotfix branch. CI publishes from `production`.
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
- Install `@regardio/dev` and:
146
+ ### 1. Add ship scripts to `package.json`
176
147
 
177
- 1. **Add the scripts to `package.json`** (optional, for publishing repos):
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
- For repos that publish to npm, add release scripts to the root `package.json`:
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
- ```json
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
- For non-publishing repos (e.g., channels that deploy to servers), omit the release scripts — the ship commands will skip versioning and only perform the git merges. This makes the tools reusable across both publishing and non-publishing repositories.
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
- 2. **Add a `.versionrc.json` to each publishable package** with a scoped `tagPrefix` so tags and changelogs stay isolated per package:
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
- ```bash
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
- For a single-package repo, put `.versionrc.json` at the root instead.
182
+ ### 3. Create the branches
205
183
 
206
- 3. **Create the branches**:
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
- ```bash
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
- 4. **Copy the release workflow** for your forge:
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
- ```bash
217
- # GitHub
218
- mkdir -p .github/workflows
219
- cp node_modules/@regardio/dev/templates/github/release.yml .github/workflows/release.yml
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
- # Forgejo / Codeberg
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
- 5. **First publish of any new package must be done locally**:
206
+ ```bash
207
+ pnpm build && npm publish --access public
208
+ ```
227
209
 
228
- ```bash
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 no broken code reaches any environment:
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 that should never be published to npm must set `"private": true` in `package.json`. `pnpm -r publish` skips them automatically.
224
+ Packages with `"private": true` are skipped by `pnpm -r publish` automatically.
245
225
 
246
- For non-publishing repos (e.g., channels that deploy directly to servers instead of npm), omit the `release:*` scripts from `package.json`. The ship tools will skip versioning entirely and only perform the git merges. CI can then handle deployment to servers based on the `production` branch.
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
 
@@ -65,7 +65,7 @@ export default defineConfig({ test: vitestReactConfig });
65
65
  ```json
66
66
  {
67
67
  "scripts": {
68
- "test": "exec-s test:*",
68
+ "test": "vitest run",
69
69
  "test:unit": "vitest run",
70
70
  "report": "vitest run --coverage"
71
71
  }
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.1",
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.0",
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-s lint:md lint:biome",
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",
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "commitAll": false,
3
+ "gitRawCommitsOpts": { "path": "." },
3
4
  "sign": false,
4
5
  "tagPrefix": "v",
5
6
  "types": [