@regardio/dev 1.24.0 → 2.0.2
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 +2 -2
- package/dist/bin/ship/hotfix.bin.mjs +140 -0
- package/dist/bin/ship/production.bin.mjs +120 -0
- package/dist/bin/ship/staging.bin.mjs +70 -0
- package/dist/bin/ship/utils-BQ-JZ2D5.mjs +45 -0
- package/dist/playwright/index.d.mts +24 -0
- package/dist/playwright/index.mjs +61 -0
- package/dist/vitest/node.d.mts +22 -0
- package/dist/vitest/node.mjs +28 -0
- package/dist/vitest/react.d.mts +22 -0
- package/dist/vitest/react.mjs +28 -0
- package/docs/en/README.md +95 -0
- package/docs/en/agents.md +57 -0
- package/docs/en/standards/api.md +324 -0
- package/docs/en/standards/coding.md +144 -0
- package/docs/en/standards/commits.md +111 -0
- package/docs/en/standards/documentation.md +173 -0
- package/docs/en/standards/naming.md +180 -0
- package/docs/en/standards/principles.md +84 -0
- package/docs/en/standards/react.md +246 -0
- package/docs/en/standards/sql.md +258 -0
- package/docs/en/standards/testing.md +139 -0
- package/docs/en/standards/writing.md +119 -0
- package/docs/en/tools/biome.md +89 -0
- package/docs/en/tools/commitlint.md +92 -0
- package/docs/en/tools/dependencies.md +116 -0
- package/docs/en/tools/husky.md +90 -0
- package/docs/en/tools/markdownlint.md +84 -0
- package/docs/en/tools/playwright.md +117 -0
- package/docs/en/tools/releases.md +242 -0
- package/docs/en/tools/typescript.md +89 -0
- package/docs/en/tools/vitest.md +146 -0
- package/package.json +57 -70
- package/src/biome/preset.json +3 -0
- package/templates/changeset/README.md +14 -0
- package/templates/changeset/config.json +11 -0
- package/templates/github/release.yml +77 -0
- package/dist/bin/exec/clean.d.ts +0 -3
- package/dist/bin/exec/clean.d.ts.map +0 -1
- package/dist/bin/exec/clean.js +0 -25
- package/dist/bin/exec/clean.test.d.ts +0 -2
- package/dist/bin/exec/clean.test.d.ts.map +0 -1
- package/dist/bin/exec/clean.test.js +0 -45
- package/dist/bin/exec/husky.d.ts +0 -3
- package/dist/bin/exec/husky.d.ts.map +0 -1
- package/dist/bin/exec/husky.js +0 -9
- package/dist/bin/exec/p.d.ts +0 -3
- package/dist/bin/exec/p.d.ts.map +0 -1
- package/dist/bin/exec/p.js +0 -8
- package/dist/bin/exec/s.d.ts +0 -3
- package/dist/bin/exec/s.d.ts.map +0 -1
- package/dist/bin/exec/s.js +0 -8
- package/dist/bin/exec/tsc.d.ts +0 -3
- package/dist/bin/exec/tsc.d.ts.map +0 -1
- package/dist/bin/exec/tsc.js +0 -8
- package/dist/bin/lint/biome.d.ts +0 -3
- package/dist/bin/lint/biome.d.ts.map +0 -1
- package/dist/bin/lint/biome.js +0 -8
- package/dist/bin/lint/commit.d.ts +0 -3
- package/dist/bin/lint/commit.d.ts.map +0 -1
- package/dist/bin/lint/commit.js +0 -8
- package/dist/bin/lint/md.d.ts +0 -3
- package/dist/bin/lint/md.d.ts.map +0 -1
- package/dist/bin/lint/md.js +0 -16
- package/dist/bin/lint/package.d.ts +0 -4
- package/dist/bin/lint/package.d.ts.map +0 -1
- package/dist/bin/lint/package.js +0 -81
- package/dist/bin/lint/package.test.d.ts +0 -2
- package/dist/bin/lint/package.test.d.ts.map +0 -1
- package/dist/bin/lint/package.test.js +0 -65
- package/dist/bin/ship/hotfix.d.ts +0 -3
- package/dist/bin/ship/hotfix.d.ts.map +0 -1
- package/dist/bin/ship/hotfix.js +0 -141
- package/dist/bin/ship/production.d.ts +0 -3
- package/dist/bin/ship/production.d.ts.map +0 -1
- package/dist/bin/ship/production.js +0 -124
- package/dist/bin/ship/staging.d.ts +0 -3
- package/dist/bin/ship/staging.d.ts.map +0 -1
- package/dist/bin/ship/staging.js +0 -51
- package/dist/bin/ship/utils.d.ts +0 -9
- package/dist/bin/ship/utils.d.ts.map +0 -1
- package/dist/bin/ship/utils.js +0 -63
- package/dist/bin/ship/utils.test.d.ts +0 -2
- package/dist/bin/ship/utils.test.d.ts.map +0 -1
- package/dist/bin/ship/utils.test.js +0 -127
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -101
- package/dist/playwright/index.d.ts +0 -10
- package/dist/playwright/index.d.ts.map +0 -1
- package/dist/playwright/index.js +0 -42
- package/dist/playwright/index.test.d.ts +0 -2
- package/dist/playwright/index.test.d.ts.map +0 -1
- package/dist/playwright/index.test.js +0 -55
- package/dist/testing/setup-react.d.ts +0 -2
- package/dist/testing/setup-react.d.ts.map +0 -1
- package/dist/testing/setup-react.js +0 -1
- package/dist/vitest/node.d.ts +0 -22
- package/dist/vitest/node.d.ts.map +0 -1
- package/dist/vitest/node.js +0 -16
- package/dist/vitest/react.d.ts +0 -17
- package/dist/vitest/react.d.ts.map +0 -1
- package/dist/vitest/react.js +0 -12
- package/src/bin/exec/clean.test.ts +0 -63
- package/src/bin/exec/clean.ts +0 -36
- package/src/bin/exec/husky.ts +0 -14
- package/src/bin/exec/p.ts +0 -13
- package/src/bin/exec/s.ts +0 -13
- package/src/bin/exec/tsc.ts +0 -13
- package/src/bin/lint/biome.ts +0 -13
- package/src/bin/lint/commit.ts +0 -13
- package/src/bin/lint/md.ts +0 -28
- package/src/bin/lint/package.test.ts +0 -83
- package/src/bin/lint/package.ts +0 -108
- package/src/bin/ship/hotfix.ts +0 -241
- package/src/bin/ship/production.ts +0 -240
- package/src/bin/ship/staging.ts +0 -108
- package/src/bin/ship/utils.test.ts +0 -178
- package/src/bin/ship/utils.ts +0 -109
- package/src/config.test.ts +0 -129
- package/src/markdownlint/markdownlint-cli2.jsonc +0 -9
- package/src/playwright/index.test.ts +0 -73
- package/src/playwright/index.ts +0 -63
- package/src/templates/release.yml +0 -128
- package/src/testing/setup-react.ts +0 -8
- package/src/vitest/node.ts +0 -25
- package/src/vitest/react.ts +0 -19
- /package/{src → templates}/sqlfluff/setup.cfg +0 -0
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ The goal is code that's correct, consistent, and a pleasure to work with.
|
|
|
27
27
|
| **Testing** | Vitest, Playwright, Testing Library |
|
|
28
28
|
| **Build** | TypeScript, tsx, Vite |
|
|
29
29
|
| **Workflow** | Husky, GitLab Flow |
|
|
30
|
-
| **CLI utilities** |
|
|
30
|
+
| **CLI utilities** | `ship-staging`, `ship-production`, `ship-hotfix` |
|
|
31
31
|
|
|
32
32
|
## Quick Start
|
|
33
33
|
|
|
@@ -42,7 +42,7 @@ Extend the shared configs in your project:
|
|
|
42
42
|
{ "extends": ["@regardio/dev/biome"] }
|
|
43
43
|
|
|
44
44
|
// tsconfig.json
|
|
45
|
-
{ "extends": "@regardio/dev/typescript/base
|
|
45
|
+
{ "extends": "@regardio/dev/typescript/base" }
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
## Documentation
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as runQualityChecks, i as gitRead, o as runScript, r as git, t as branchExists } from "./utils-BQ-JZ2D5.mjs";
|
|
3
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
//#region src/bin/ship/hotfix.ts
|
|
7
|
+
/**
|
|
8
|
+
* ship-hotfix: Manage hotfix branches based on production code.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ship-hotfix start <name> - Create hotfix/<name> from production
|
|
12
|
+
* ship-hotfix finish - Finish and propagate the hotfix
|
|
13
|
+
*
|
|
14
|
+
* GitLab workflow:
|
|
15
|
+
* production → hotfix/<name> → production → staging → main
|
|
16
|
+
*
|
|
17
|
+
* During a hotfix, add a changeset (`pnpm changeset`) describing the fix.
|
|
18
|
+
* `ship-hotfix finish` consumes all pending changesets, commits the version
|
|
19
|
+
* bumps, and propagates the hotfix through production → staging → main.
|
|
20
|
+
* CI on `production` handles the `changeset publish` to npm.
|
|
21
|
+
*/
|
|
22
|
+
function runShipHotfix(subcommand, subArgs, cwd = process.cwd()) {
|
|
23
|
+
if (subcommand === "start") {
|
|
24
|
+
const name = subArgs[0];
|
|
25
|
+
if (!name) {
|
|
26
|
+
console.error("Usage: ship-hotfix start <name>");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
const hotfixBranch = `hotfix/${name}`;
|
|
30
|
+
if (gitRead("status", "--porcelain")) {
|
|
31
|
+
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
console.log("\nFetching latest state from origin...");
|
|
35
|
+
git("fetch", "origin");
|
|
36
|
+
if (!branchExists("production")) {
|
|
37
|
+
console.error("Branch \"production\" does not exist. Create it first:\n git checkout -b production && git push -u origin production");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
git("checkout", "production");
|
|
41
|
+
git("pull", "--ff-only", "origin", "production");
|
|
42
|
+
git("checkout", "-b", hotfixBranch);
|
|
43
|
+
console.log(`\n✅ Hotfix branch "${hotfixBranch}" created from production.`);
|
|
44
|
+
console.log("Apply your fix, add a changeset (`pnpm changeset`), commit, then run:");
|
|
45
|
+
console.log(" ship-hotfix finish");
|
|
46
|
+
process.exit(0);
|
|
47
|
+
}
|
|
48
|
+
if (subcommand === "finish") {
|
|
49
|
+
const currentBranch = gitRead("branch", "--show-current");
|
|
50
|
+
if (!currentBranch.startsWith("hotfix/")) {
|
|
51
|
+
console.error(`Must be on a hotfix/* branch. Currently on: ${currentBranch}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
if (gitRead("status", "--porcelain")) {
|
|
55
|
+
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
if (!existsSync(join(cwd, ".changeset"))) {
|
|
59
|
+
console.error("\nNo .changeset/ directory found. This tooling requires Changesets.\nCopy the template from @regardio/dev/templates/changeset and run:\n pnpm changeset init");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
if (execFileSync("sh", ["-c", `ls .changeset/*.md 2>/dev/null | grep -v README.md | wc -l | tr -d ' '`], { encoding: "utf-8" }).trim() === "0") {
|
|
63
|
+
console.error("\nNo pending changesets. Run `pnpm changeset` before finishing the hotfix.");
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
console.log("\nRunning quality checks...");
|
|
67
|
+
try {
|
|
68
|
+
runQualityChecks();
|
|
69
|
+
} catch {
|
|
70
|
+
console.error("\nQuality checks failed. Fix all issues before finishing the hotfix.");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
console.log("✅ Quality checks passed");
|
|
74
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
75
|
+
if (!existsSync(packageJsonPath)) {
|
|
76
|
+
console.error("No package.json found in current directory.");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const { name: packageName } = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
80
|
+
console.log("\nApplying changesets (bumping versions and updating CHANGELOGs)...");
|
|
81
|
+
runScript("changeset version");
|
|
82
|
+
try {
|
|
83
|
+
runScript("fix:pkg");
|
|
84
|
+
} catch {}
|
|
85
|
+
try {
|
|
86
|
+
git("add", "-A");
|
|
87
|
+
const changedFiles = gitRead("diff", "--cached", "--name-only").split("\n").filter(Boolean);
|
|
88
|
+
for (const file of changedFiles) {
|
|
89
|
+
if (file.endsWith(".json")) try {
|
|
90
|
+
execSync(`npx biome check --write ${file}`, {
|
|
91
|
+
cwd,
|
|
92
|
+
stdio: "inherit"
|
|
93
|
+
});
|
|
94
|
+
} catch {}
|
|
95
|
+
if (file.endsWith(".md")) try {
|
|
96
|
+
execSync(`npx markdownlint-cli2 --fix ${file}`, {
|
|
97
|
+
cwd,
|
|
98
|
+
stdio: "inherit"
|
|
99
|
+
});
|
|
100
|
+
} catch {}
|
|
101
|
+
}
|
|
102
|
+
} catch {}
|
|
103
|
+
git("add", "-A");
|
|
104
|
+
git("commit", "-m", "chore(hotfix): version packages");
|
|
105
|
+
console.log("\nFetching latest state from origin...");
|
|
106
|
+
git("fetch", "origin");
|
|
107
|
+
console.log("\nMerging hotfix into production...");
|
|
108
|
+
git("checkout", "production");
|
|
109
|
+
git("pull", "--ff-only", "origin", "production");
|
|
110
|
+
git("merge", "--no-ff", currentBranch, "-m", `chore(hotfix): merge ${currentBranch} into production`);
|
|
111
|
+
git("push", "origin", "production");
|
|
112
|
+
console.log("\nPropagating hotfix to staging...");
|
|
113
|
+
git("checkout", "staging");
|
|
114
|
+
git("pull", "--ff-only", "origin", "staging");
|
|
115
|
+
git("merge", "--no-ff", "production", "-m", "chore(hotfix): merge production into staging");
|
|
116
|
+
git("push", "origin", "staging");
|
|
117
|
+
console.log("\nPropagating hotfix to main...");
|
|
118
|
+
git("checkout", "main");
|
|
119
|
+
git("pull", "--ff-only", "origin", "main");
|
|
120
|
+
git("merge", "--no-ff", "staging", "-m", "chore(hotfix): merge staging into main");
|
|
121
|
+
git("push", "origin", "main");
|
|
122
|
+
git("branch", "-d", currentBranch);
|
|
123
|
+
try {
|
|
124
|
+
git("push", "origin", "--delete", currentBranch);
|
|
125
|
+
} catch {}
|
|
126
|
+
console.log(`\n✅ Hotfix ${packageName} shipped to production → staging → main`);
|
|
127
|
+
console.log("CI will publish changed packages to npm.");
|
|
128
|
+
console.log("You are on main and ready to keep working.");
|
|
129
|
+
process.exit(0);
|
|
130
|
+
}
|
|
131
|
+
console.error("Usage:");
|
|
132
|
+
console.error(" ship-hotfix start <name>");
|
|
133
|
+
console.error(" ship-hotfix finish");
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/bin/ship/hotfix.bin.ts
|
|
138
|
+
runShipHotfix(process.argv[2], process.argv.slice(3));
|
|
139
|
+
//#endregion
|
|
140
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as runQualityChecks, i as gitRead, n as confirm, o as runScript, r as git, t as branchExists } from "./utils-BQ-JZ2D5.mjs";
|
|
3
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
//#region src/bin/ship/production.ts
|
|
7
|
+
/**
|
|
8
|
+
* ship-production: Promote main to production following the GitLab workflow.
|
|
9
|
+
*
|
|
10
|
+
* Usage: ship-production
|
|
11
|
+
*
|
|
12
|
+
* GitLab workflow:
|
|
13
|
+
* main → (changeset version + commit) → production → staging → main
|
|
14
|
+
*
|
|
15
|
+
* Versioning is driven by Changesets. Authors add a changeset (`pnpm changeset`)
|
|
16
|
+
* alongside their change on main. This script consumes all pending changesets
|
|
17
|
+
* at ship time, commits the version bumps, then merges main → production.
|
|
18
|
+
* CI on `production` handles the actual `changeset publish` to npm.
|
|
19
|
+
*/
|
|
20
|
+
function runShipProduction(cwd = process.cwd()) {
|
|
21
|
+
const currentBranch = gitRead("branch", "--show-current");
|
|
22
|
+
if (currentBranch !== "main") {
|
|
23
|
+
console.error(`Must be on the main branch to ship. Currently on: ${currentBranch}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
if (gitRead("status", "--porcelain")) {
|
|
27
|
+
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
console.log("\nFetching latest state from origin...");
|
|
31
|
+
git("fetch", "origin");
|
|
32
|
+
if (!branchExists("staging")) {
|
|
33
|
+
console.error("Branch \"staging\" does not exist. Create it first:\n git checkout -b staging && git push -u origin staging");
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
if (!branchExists("production")) {
|
|
37
|
+
console.error("Branch \"production\" does not exist. Create it first:\n git checkout -b production && git push -u origin production");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
git("pull", "--ff-only", "origin", "main");
|
|
41
|
+
const ahead = gitRead("log", "origin/production..HEAD", "--oneline");
|
|
42
|
+
if (!ahead) {
|
|
43
|
+
console.error("main is already in sync with production. Nothing to ship.");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
console.log("\nCommits to be shipped to production:");
|
|
47
|
+
console.log(ahead);
|
|
48
|
+
if (!existsSync(join(cwd, ".changeset"))) {
|
|
49
|
+
console.error("\nNo .changeset/ directory found. This tooling requires Changesets.\nCopy the template from @regardio/dev/templates/changeset and run:\n pnpm changeset init");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const pendingChangesets = execFileSync("sh", ["-c", `ls .changeset/*.md 2>/dev/null | grep -v README.md | wc -l | tr -d ' '`], { encoding: "utf-8" }).trim();
|
|
53
|
+
if (pendingChangesets === "0") {
|
|
54
|
+
console.error("\nNo pending changesets. Run `pnpm changeset` before shipping to production.");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
58
|
+
if (!existsSync(packageJsonPath)) {
|
|
59
|
+
console.error("No package.json found in current directory.");
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const { name: packageName } = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
63
|
+
console.log(`\n${pendingChangesets} pending changeset(s) will be consumed.`);
|
|
64
|
+
if (!confirm(`Ship ${packageName} to production?`)) {
|
|
65
|
+
console.log("Aborted.");
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
console.log("\nRunning quality checks on main...");
|
|
69
|
+
try {
|
|
70
|
+
runQualityChecks();
|
|
71
|
+
} catch {
|
|
72
|
+
console.error("\nQuality checks failed on main. Fix issues before shipping.");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
console.log("✅ Quality checks passed");
|
|
76
|
+
console.log("\nApplying changesets (bumping versions and updating CHANGELOGs)...");
|
|
77
|
+
runScript("changeset version");
|
|
78
|
+
try {
|
|
79
|
+
runScript("fix:pkg");
|
|
80
|
+
} catch {}
|
|
81
|
+
try {
|
|
82
|
+
git("add", "-A");
|
|
83
|
+
const changedFiles = gitRead("diff", "--cached", "--name-only").split("\n").filter(Boolean);
|
|
84
|
+
for (const file of changedFiles) {
|
|
85
|
+
if (file.endsWith(".json")) try {
|
|
86
|
+
execSync(`npx biome check --write ${file}`, {
|
|
87
|
+
cwd,
|
|
88
|
+
stdio: "inherit"
|
|
89
|
+
});
|
|
90
|
+
} catch {}
|
|
91
|
+
if (file.endsWith(".md")) try {
|
|
92
|
+
execSync(`npx markdownlint-cli2 --fix ${file}`, {
|
|
93
|
+
cwd,
|
|
94
|
+
stdio: "inherit"
|
|
95
|
+
});
|
|
96
|
+
} catch {}
|
|
97
|
+
}
|
|
98
|
+
} catch {}
|
|
99
|
+
git("add", "-A");
|
|
100
|
+
git("commit", "-m", "chore(release): version packages");
|
|
101
|
+
console.log("\nMerging main into production...");
|
|
102
|
+
git("checkout", "production");
|
|
103
|
+
git("pull", "--ff-only", "origin", "production");
|
|
104
|
+
git("merge", "--ff-only", "main");
|
|
105
|
+
git("push", "origin", "production");
|
|
106
|
+
console.log("\nSyncing staging with production...");
|
|
107
|
+
git("checkout", "staging");
|
|
108
|
+
git("pull", "--ff-only", "origin", "staging");
|
|
109
|
+
git("merge", "--ff-only", "production");
|
|
110
|
+
git("push", "origin", "staging");
|
|
111
|
+
git("checkout", "main");
|
|
112
|
+
git("push", "origin", "main");
|
|
113
|
+
console.log(`\n✅ Shipped ${packageName} to production. CI will publish changed packages to npm.`);
|
|
114
|
+
console.log("You are on main and ready to keep working.");
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/bin/ship/production.bin.ts
|
|
118
|
+
runShipProduction();
|
|
119
|
+
//#endregion
|
|
120
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as runQualityChecks, i as gitRead, r as git, t as branchExists } from "./utils-BQ-JZ2D5.mjs";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region src/bin/ship/staging.ts
|
|
6
|
+
/**
|
|
7
|
+
* ship-staging: Deploy changes to staging following the GitLab workflow.
|
|
8
|
+
*
|
|
9
|
+
* Usage: ship-staging
|
|
10
|
+
*
|
|
11
|
+
* GitLab workflow:
|
|
12
|
+
* main → staging (staging deploy, no version bump yet)
|
|
13
|
+
*
|
|
14
|
+
* Optional step: you can ship directly to production via ship-production,
|
|
15
|
+
* which automatically syncs staging afterward. Use ship-staging when you
|
|
16
|
+
* want to validate changes in a staging environment first.
|
|
17
|
+
*
|
|
18
|
+
* Versioning is deferred to ship-production so version numbers only ever
|
|
19
|
+
* correspond to production-verified code.
|
|
20
|
+
*/
|
|
21
|
+
function runShipStaging(cwd = process.cwd()) {
|
|
22
|
+
const currentBranch = gitRead("branch", "--show-current");
|
|
23
|
+
if (currentBranch !== "main") {
|
|
24
|
+
console.error(`Must be on the main branch to release. Currently on: ${currentBranch}`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (gitRead("status", "--porcelain")) {
|
|
28
|
+
console.error("Working directory has uncommitted changes. Commit or stash them first.");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
console.log("\nFetching latest state from origin...");
|
|
32
|
+
git("fetch", "origin");
|
|
33
|
+
git("pull", "--ff-only", "origin", "main");
|
|
34
|
+
const packageJsonPath = join(cwd, "package.json");
|
|
35
|
+
if (!existsSync(packageJsonPath)) {
|
|
36
|
+
console.error("No package.json found in current directory.");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const packageName = JSON.parse(readFileSync(packageJsonPath, "utf-8")).name;
|
|
40
|
+
if (!packageName) {
|
|
41
|
+
console.error("No \"name\" field found in package.json.");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (!branchExists("staging")) {
|
|
45
|
+
console.error("Branch \"staging\" does not exist locally or on origin. Create it first:\n git checkout -b staging && git push -u origin staging");
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
console.log("\nRunning quality checks...");
|
|
49
|
+
try {
|
|
50
|
+
runQualityChecks();
|
|
51
|
+
} catch {
|
|
52
|
+
console.error("\nQuality checks failed. Fix all issues before releasing.");
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
console.log("✅ Quality checks passed");
|
|
56
|
+
console.log("\nMerging main into staging...");
|
|
57
|
+
git("checkout", "staging");
|
|
58
|
+
git("merge", "--ff-only", "main");
|
|
59
|
+
git("push", "origin", "staging");
|
|
60
|
+
git("checkout", "main");
|
|
61
|
+
git("push", "origin", "main");
|
|
62
|
+
console.log(`\n✅ ${packageName} deployed to staging`);
|
|
63
|
+
console.log("Run ship-production when ready to ship to production.");
|
|
64
|
+
console.log("(Or ship directly from main to production without using ship-staging first.)");
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/bin/ship/staging.bin.ts
|
|
68
|
+
runShipStaging();
|
|
69
|
+
//#endregion
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
3
|
+
import { closeSync, openSync, readSync } from "node:fs";
|
|
4
|
+
//#region src/bin/ship/utils.ts
|
|
5
|
+
/**
|
|
6
|
+
* Shared utilities for ship-staging, ship-production, and ship-hotfix.
|
|
7
|
+
*
|
|
8
|
+
* Git commands use execFileSync (not a shell string) so user-provided
|
|
9
|
+
* strings such as commit messages are never interpolated by the shell.
|
|
10
|
+
* pnpm script invocations use execSync via shell since script names are
|
|
11
|
+
* developer-controlled and pnpm itself is resolved through PATH.
|
|
12
|
+
*/
|
|
13
|
+
const git = (...args) => {
|
|
14
|
+
console.log(`$ git ${args.join(" ")}`);
|
|
15
|
+
execFileSync("git", args, { stdio: "inherit" });
|
|
16
|
+
};
|
|
17
|
+
const gitRead = (...args) => execFileSync("git", args, { encoding: "utf-8" }).trim();
|
|
18
|
+
const runScript = (script) => {
|
|
19
|
+
console.log(`$ pnpm ${script}`);
|
|
20
|
+
execSync(`pnpm ${script}`, { stdio: "inherit" });
|
|
21
|
+
};
|
|
22
|
+
const runQualityChecks = () => {
|
|
23
|
+
runScript("build");
|
|
24
|
+
runScript("typecheck");
|
|
25
|
+
runScript("test");
|
|
26
|
+
};
|
|
27
|
+
const branchExists = (name) => gitRead("branch", "--list", name) !== "" || gitRead("branch", "--list", "--remotes", `origin/${name}`) !== "";
|
|
28
|
+
const confirm = (prompt, ttyPath = "/dev/tty") => {
|
|
29
|
+
process.stdout.write(`${prompt} (y/N) `);
|
|
30
|
+
const buf = Buffer.alloc(1024);
|
|
31
|
+
let fd;
|
|
32
|
+
let shouldClose = false;
|
|
33
|
+
try {
|
|
34
|
+
fd = openSync(ttyPath, "r");
|
|
35
|
+
shouldClose = true;
|
|
36
|
+
} catch {
|
|
37
|
+
fd = process.stdin.fd;
|
|
38
|
+
}
|
|
39
|
+
const bytesRead = readSync(fd, buf, 0, buf.length, null);
|
|
40
|
+
if (shouldClose) closeSync(fd);
|
|
41
|
+
const answer = buf.slice(0, bytesRead).toString().trim();
|
|
42
|
+
return answer === "y" || answer === "Y";
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { runQualityChecks as a, gitRead as i, confirm as n, runScript as o, git as r, branchExists as t };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as _$_playwright_test0 from "@playwright/test";
|
|
2
|
+
import { PlaywrightTestConfig } from "@playwright/test";
|
|
3
|
+
|
|
4
|
+
//#region src/playwright/index.d.ts
|
|
5
|
+
interface BuildPlaywrightBaseConfigParams {
|
|
6
|
+
appPort: number;
|
|
7
|
+
appUrl: string;
|
|
8
|
+
ci?: boolean;
|
|
9
|
+
devices: typeof _$_playwright_test0.devices;
|
|
10
|
+
webServerCommand: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build a base Playwright config object with Regardio defaults.
|
|
14
|
+
* Consumers should wrap with defineConfig() in their local playwright.config.ts
|
|
15
|
+
*/
|
|
16
|
+
declare function buildPlaywrightBaseConfig({
|
|
17
|
+
appUrl,
|
|
18
|
+
appPort,
|
|
19
|
+
devices,
|
|
20
|
+
ci,
|
|
21
|
+
webServerCommand
|
|
22
|
+
}: BuildPlaywrightBaseConfigParams): PlaywrightTestConfig;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { BuildPlaywrightBaseConfigParams, buildPlaywrightBaseConfig };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//#region src/playwright/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Build a base Playwright config object with Regardio defaults.
|
|
4
|
+
* Consumers should wrap with defineConfig() in their local playwright.config.ts
|
|
5
|
+
*/
|
|
6
|
+
function buildPlaywrightBaseConfig({ appUrl, appPort, devices, ci = !!process.env.CI, webServerCommand }) {
|
|
7
|
+
if (!appUrl || typeof appUrl !== "string") throw new Error("[playwright] appUrl must be a non-empty string");
|
|
8
|
+
if (!appPort || typeof appPort !== "number") throw new Error("[playwright] appPort must be a number");
|
|
9
|
+
if (!devices) throw new Error("[playwright] devices must be provided from \"@playwright/test\"");
|
|
10
|
+
return {
|
|
11
|
+
forbidOnly: ci,
|
|
12
|
+
fullyParallel: true,
|
|
13
|
+
outputDir: "./tests/test-results",
|
|
14
|
+
projects: [
|
|
15
|
+
{
|
|
16
|
+
name: "chromium",
|
|
17
|
+
use: { ...devices["Desktop Chrome"] }
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "firefox",
|
|
21
|
+
use: { ...devices["Desktop Firefox"] }
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "webkit",
|
|
25
|
+
use: { ...devices["Desktop Safari"] }
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "iPad Pro 11 landscape",
|
|
29
|
+
use: { ...devices["iPad Pro 11 landscape"] }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "iPhone 14 portrait",
|
|
33
|
+
use: { ...devices["iPhone 14"] }
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "Pixel 7 portrait",
|
|
37
|
+
use: { ...devices["Pixel 7"] }
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
reporter: [["html", {
|
|
41
|
+
open: "never",
|
|
42
|
+
outputFolder: "./tests/playwright-report"
|
|
43
|
+
}]],
|
|
44
|
+
retries: ci ? 2 : 0,
|
|
45
|
+
testDir: "./tests",
|
|
46
|
+
testMatch: "**/*.e2e.ts",
|
|
47
|
+
use: {
|
|
48
|
+
baseURL: appUrl,
|
|
49
|
+
trace: "on-first-retry"
|
|
50
|
+
},
|
|
51
|
+
webServer: {
|
|
52
|
+
command: webServerCommand,
|
|
53
|
+
ignoreHTTPSErrors: true,
|
|
54
|
+
reuseExistingServer: !ci,
|
|
55
|
+
url: appUrl
|
|
56
|
+
},
|
|
57
|
+
...ci ? { workers: 1 } : {}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { buildPlaywrightBaseConfig };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/vitest/node.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base Vitest configuration for Node.js packages.
|
|
4
|
+
* Use with defineConfig() in your vitest.config.ts
|
|
5
|
+
*/
|
|
6
|
+
declare const vitestNodeConfig: {
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: "v8";
|
|
9
|
+
thresholds: {
|
|
10
|
+
branches: number;
|
|
11
|
+
functions: number;
|
|
12
|
+
lines: number;
|
|
13
|
+
statements: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
environment: string;
|
|
17
|
+
exclude: string[];
|
|
18
|
+
globals: boolean;
|
|
19
|
+
include: string[];
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { vitestNodeConfig };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/vitest/node.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base Vitest configuration for Node.js packages.
|
|
4
|
+
* Use with defineConfig() in your vitest.config.ts
|
|
5
|
+
*/
|
|
6
|
+
const vitestNodeConfig = {
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: "v8",
|
|
9
|
+
thresholds: {
|
|
10
|
+
branches: 80,
|
|
11
|
+
functions: 80,
|
|
12
|
+
lines: 80,
|
|
13
|
+
statements: 80
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
environment: "node",
|
|
17
|
+
exclude: [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"dist",
|
|
20
|
+
"build",
|
|
21
|
+
".turbo",
|
|
22
|
+
".react-router"
|
|
23
|
+
],
|
|
24
|
+
globals: true,
|
|
25
|
+
include: ["**/*.test.ts", "**/*.test.tsx"]
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { vitestNodeConfig };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/vitest/react.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Vitest configuration for React packages with jsdom environment.
|
|
4
|
+
* Use with defineConfig() in your vitest.config.ts
|
|
5
|
+
*/
|
|
6
|
+
declare const vitestReactConfig: {
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: "v8";
|
|
9
|
+
thresholds: {
|
|
10
|
+
branches: number;
|
|
11
|
+
functions: number;
|
|
12
|
+
lines: number;
|
|
13
|
+
statements: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
environment: string;
|
|
17
|
+
exclude: string[];
|
|
18
|
+
globals: boolean;
|
|
19
|
+
include: string[];
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { vitestReactConfig };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/vitest/react.ts
|
|
2
|
+
/**
|
|
3
|
+
* Vitest configuration for React packages with jsdom environment.
|
|
4
|
+
* Use with defineConfig() in your vitest.config.ts
|
|
5
|
+
*/
|
|
6
|
+
const vitestReactConfig = {
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: "v8",
|
|
9
|
+
thresholds: {
|
|
10
|
+
branches: 80,
|
|
11
|
+
functions: 80,
|
|
12
|
+
lines: 80,
|
|
13
|
+
statements: 80
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
environment: "jsdom",
|
|
17
|
+
exclude: [
|
|
18
|
+
"node_modules",
|
|
19
|
+
"dist",
|
|
20
|
+
"build",
|
|
21
|
+
".turbo",
|
|
22
|
+
".react-router"
|
|
23
|
+
],
|
|
24
|
+
globals: true,
|
|
25
|
+
include: ["**/*.test.ts", "**/*.test.tsx"]
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { vitestReactConfig };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
title: @regardio/dev Documentation
|
|
4
|
+
type: guide
|
|
5
|
+
status: published
|
|
6
|
+
summary: Complete documentation for the @regardio/dev toolchain
|
|
7
|
+
related: [coding-standards, development-principles]
|
|
8
|
+
locale: en-US
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Documentation
|
|
12
|
+
|
|
13
|
+
Documentation index and quick reference for the `@regardio/dev` toolchain.
|
|
14
|
+
|
|
15
|
+
## Concepts
|
|
16
|
+
|
|
17
|
+
Foundational principles and standards that guide development:
|
|
18
|
+
|
|
19
|
+
| Document | Description |
|
|
20
|
+
|----------|-------------|
|
|
21
|
+
| [AI Agents](./agents.md) | Instructions for AI coding assistants |
|
|
22
|
+
| [API](./standards/api.md) | API design and implementation guidelines |
|
|
23
|
+
| [Coding](./standards/coding.md) | TypeScript, React, and general patterns |
|
|
24
|
+
| [Commits](./standards/commits.md) | Conventional commits and changelog generation |
|
|
25
|
+
| [Documentation](./standards/documentation.md) | Documentation structure and conventions |
|
|
26
|
+
| [Naming](./standards/naming.md) | Consistent naming across languages |
|
|
27
|
+
| [Principles](./standards/principles.md) | Code quality, architecture, maintainability |
|
|
28
|
+
| [React](./standards/react.md) | React and TypeScript development patterns |
|
|
29
|
+
| [SQL](./standards/sql.md) | PostgreSQL schema styling, structure, and access control |
|
|
30
|
+
| [Testing](./standards/testing.md) | Testing philosophy and patterns |
|
|
31
|
+
| [Writing](./standards/writing.md) | Voice, tone, and language |
|
|
32
|
+
|
|
33
|
+
## Toolchain
|
|
34
|
+
|
|
35
|
+
Configuration and usage for each tool in the stack:
|
|
36
|
+
|
|
37
|
+
| Document | Description |
|
|
38
|
+
|----------|-------------|
|
|
39
|
+
| [Biome](./tools/biome.md) | Linting and formatting |
|
|
40
|
+
| [Commitlint](./tools/commitlint.md) | Commit message validation |
|
|
41
|
+
| [Dependencies](./tools/dependencies.md) | Safe dependency updates and supply-chain controls |
|
|
42
|
+
| [Husky](./tools/husky.md) | Git hooks |
|
|
43
|
+
| [Markdownlint](./tools/markdownlint.md) | Markdown quality |
|
|
44
|
+
| [Playwright](./tools/playwright.md) | End-to-end testing |
|
|
45
|
+
| [Releases](./tools/releases.md) | GitLab-flow-based versioning and releases |
|
|
46
|
+
| [TypeScript](./tools/typescript.md) | Strict TypeScript configuration |
|
|
47
|
+
| [Vitest](./tools/vitest.md) | Unit and integration testing |
|
|
48
|
+
|
|
49
|
+
## Quick Reference
|
|
50
|
+
|
|
51
|
+
### Commands
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm build # Build all packages
|
|
55
|
+
pnpm dev # Start development
|
|
56
|
+
pnpm fix # Run all fixes and linting
|
|
57
|
+
pnpm lint # Run linting only
|
|
58
|
+
pnpm report # Run tests with coverage
|
|
59
|
+
pnpm changeset # Declare a version bump alongside your change
|
|
60
|
+
pnpm ship:staging # Deploy main to staging for validation (optional)
|
|
61
|
+
pnpm ship:production # Consume changesets, merge main → production, trigger publish
|
|
62
|
+
pnpm ship:hotfix start <name> # Start an urgent fix from production
|
|
63
|
+
pnpm ship:hotfix finish # Propagate the hotfix back through production → staging → main
|
|
64
|
+
pnpm test # Run tests
|
|
65
|
+
pnpm typecheck # TypeScript type checking
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Config Files
|
|
69
|
+
|
|
70
|
+
| File | Purpose |
|
|
71
|
+
|------|---------|
|
|
72
|
+
| `.commitlintrc.json` | Commit message rules |
|
|
73
|
+
| `.markdownlint.json` | Markdown rules |
|
|
74
|
+
| `biome.jsonc` | Linting and formatting |
|
|
75
|
+
| `playwright.config.ts` | E2E test configuration |
|
|
76
|
+
| `pnpm-workspace.yaml` | Workspace dependency and supply-chain policy |
|
|
77
|
+
| `tsconfig.json` | TypeScript configuration |
|
|
78
|
+
| `vitest.config.ts` | Unit test configuration |
|
|
79
|
+
| `.sqlfluff` | SQL linting (copy from `@regardio/dev/sqlfluff/setup.cfg`) |
|
|
80
|
+
|
|
81
|
+
### Extending Presets
|
|
82
|
+
|
|
83
|
+
```jsonc
|
|
84
|
+
// biome.jsonc
|
|
85
|
+
{ "extends": ["@regardio/dev/biome"] }
|
|
86
|
+
|
|
87
|
+
// tsconfig.json
|
|
88
|
+
{ "extends": "@regardio/dev/typescript/base" }
|
|
89
|
+
|
|
90
|
+
// .commitlintrc.json
|
|
91
|
+
{ "extends": ["@regardio/dev/commitlint"] }
|
|
92
|
+
|
|
93
|
+
// .markdownlint.json
|
|
94
|
+
{ "extends": "@regardio/dev/markdownlint" }
|
|
95
|
+
```
|