@webjsdev/cli 0.10.57 → 0.10.58
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 +6 -1
- package/bin/webjs.js +219 -9
- package/lib/app-tasks.js +70 -10
- package/lib/check-target.js +1 -1
- package/lib/ci-config.js +250 -0
- package/lib/ci-runner.js +499 -0
- package/lib/create.js +51 -1
- package/lib/run-tasks.js +23 -3
- package/package.json +3 -3
- package/templates/.agents/rules/workflow.md +15 -9
- package/templates/.agents/skills/webjs/SKILL.md +1 -0
- package/templates/.agents/skills/webjs/references/built-ins.md +42 -0
- package/templates/.agents/skills/webjs/references/styling.md +1 -1
- package/templates/.agents/skills/webjs/references/testing.md +11 -0
- package/templates/.agents/skills/webjs/references/ui-kit.md +25 -0
- package/templates/.github/pull_request_template.md +3 -4
- package/templates/.github/workflows/ci.yml +38 -88
- package/templates/.hooks/pre-commit +5 -4
- package/templates/partials/agents-playbook-api.md +14 -8
- package/templates/partials/agents-playbook-fullstack.md +16 -10
|
@@ -50,15 +50,21 @@ Read `AGENTS.md` first. Full hosted docs are at https://webjs.dev/docs.
|
|
|
50
50
|
2. Browser tests in `test/<feature>/browser/*.test.js` for hydration, DOM, slots,
|
|
51
51
|
and the client router.
|
|
52
52
|
3. Documentation stays in sync on the SAME PR as the code, never a follow-up.
|
|
53
|
-
4. `npm run
|
|
54
|
-
|
|
55
|
-
`webjs
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
`package.json`
|
|
53
|
+
4. `npm run ci` must pass before you push. It runs the step list declared in
|
|
54
|
+
`package.json` under `webjs.ci`, one result line per step: `webjs check`
|
|
55
|
+
(correctness), `webjs doctor` (project health), `webjs typecheck`, a
|
|
56
|
+
dependency audit, then the server, browser, and e2e test layers. The GitHub
|
|
57
|
+
workflow runs the same list on every PR and push, so the two cannot drift;
|
|
58
|
+
`npm run ci -- --only Tests` runs one layer while you iterate, and
|
|
59
|
+
`npm run ci -- --signoff` posts a green commit status (basecamp/gh-signoff)
|
|
60
|
+
a branch-protection rule can require. Doctor fails on whatever your
|
|
61
|
+
`package.json` `webjs.doctor.gate` marks `error`, which starts as the
|
|
62
|
+
un-versioned stylesheet link check, plus the two hard toolchain checks that
|
|
63
|
+
default to `error` with no gate entry at all: `NODE_VERSION` (the Node
|
|
64
|
+
floor) and `TSCONFIG_ERASABLE` (`erasableSyntaxOnly` missing from an
|
|
65
|
+
existing tsconfig), either of which would 500 the app at runtime. Everything
|
|
66
|
+
else it reports is a warning that cannot fail the build. Widen or narrow the
|
|
67
|
+
gate, and the step list, in `package.json` rather than in the workflow.
|
|
62
68
|
|
|
63
69
|
How a PR gets REVIEWED is deliberately not specified here. Use whatever your
|
|
64
70
|
team already does. WebJs has opinions about the code (the conventions above,
|
|
@@ -264,6 +264,7 @@ Success is a 303 (PRG); failure re-renders the page at 422 with the result on `a
|
|
|
264
264
|
|
|
265
265
|
## Testing Defaults
|
|
266
266
|
|
|
267
|
+
- `npm run ci` before every push: it runs the `webjs.ci` step list in `package.json` (correctness, project health, types, a dependency audit, then the server, browser, and e2e test layers) with a result line per step, and CI runs the same list, so a green local run predicts the pipeline. `npm run ci -- --only Tests` runs one layer while iterating. See `references/testing.md` and `references/built-ins.md`.
|
|
267
268
|
- Prefer server/handler tests first: drive the app with `handle()` from `@webjsdev/server/testing` and assert on the `Response`.
|
|
268
269
|
- Add a browser test (`npm run test:browser`) for anything touching hydration, the client router, slots, or custom-element upgrade. A unit test is necessary but NOT sufficient for a browser-facing change.
|
|
269
270
|
- Render the app and LOOK for any UI change: `npm run check` and `npm run typecheck` pass even when a layout collapses. Static tools give no signal for a visual defect.
|
|
@@ -216,6 +216,48 @@ An over-limit body responds `413` without buffering the whole payload.
|
|
|
216
216
|
|
|
217
217
|
`before` runs to completion first (a non-zero exit aborts the boot). `parallel` (dev only) runs long-lived watchers alongside the server and tears them down on exit. `watch` (dev only) adds extra live-reload directories outside the app tree.
|
|
218
218
|
|
|
219
|
+
### Local CI (`webjs.ci`)
|
|
220
|
+
|
|
221
|
+
`webjs ci` runs the step list the block declares, the Rails 8.1 `bin/ci` posture: your machine is the first CI runner, and a cloud pipeline runs the SAME list by calling `npm run ci`, so the two cannot drift. Each step prints a heading, then `✅ <title> passed in 2.11s` or `❌ <title> failed in 0.01s`; the run ends with every failure listed and one total line, and exits 1 on any failure.
|
|
222
|
+
|
|
223
|
+
```jsonc
|
|
224
|
+
{ "webjs": { "ci": { "steps": [
|
|
225
|
+
{ "title": "Setup", "run": "webjs db migrate" },
|
|
226
|
+
{ "title": "Checks", "parallel": 2, "steps": [ // two at a time
|
|
227
|
+
"webjs check", // a string is a command titled by itself
|
|
228
|
+
{ "title": "Types", "run": "webjs typecheck" },
|
|
229
|
+
{ "title": "Tests", "steps": [ // a nested group takes ONE slot, runs in order
|
|
230
|
+
{ "title": "Tests: server", "run": "webjs test --server" },
|
|
231
|
+
{ "title": "Tests: e2e", "run": "webjs test --server", "env": { "WEBJS_E2E": "1" } }
|
|
232
|
+
] }
|
|
233
|
+
] }
|
|
234
|
+
] } } }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
A step is a string, a `{ title, run, env? }` command, or a `{ title, steps, parallel? }` group. `parallel` is a slot count (default 1); a parallel group captures each step's output and replays it whole when the step finishes, so two steps never interleave, and a group nested inside it takes one slot and runs sequentially (it cannot declare `parallel`, which the reader reports rather than honours). `env` is per-step, so the e2e opt-in does not depend on a shell prefix. Every child runs with `CI=true`, `node_modules/.bin` on PATH, and `.env` loaded first (a real env var wins), so a `webjs db migrate` step sees `DATABASE_URL`.
|
|
238
|
+
|
|
239
|
+
Flags: `-f` / `--fail-fast` stops after the first failure (the default runs everything and lists every failure), `--only <title>` runs one step or group by title (repeatable, an unknown title is an error rather than an empty green run), `--json` emits one document on stdout (`{ ok, seconds, steps: [{ title, run, group, ok, code, seconds, output? }] }`, failed steps carrying their captured output, the human report on stderr) for an agent loop, and `--signoff` runs `gh signoff` after a green run. To hold a merge until a LOCAL run is green, install `basecamp/gh-signoff`, run `gh signoff install` once (a branch-protection rule requiring the `signoff` status), and run `npm run ci -- --signoff`; a red run posts nothing.
|
|
240
|
+
|
|
241
|
+
Under GitHub Actions each step is a `::group::` in the log, a failed step is an `::error::` annotation, and a step table is appended to the job summary, so a single job running the whole list still names the layer that broke. The scaffold's workflow is exactly that one job; `webjs create --skip-ci` omits it and the local list always ships. A malformed block (a group with no title, a nested `parallel`, an unknown key) refuses to run and names every problem by JSON path, because a silently dropped step is a check that never ran. Nothing declared is exit 1 too, naming any workspace member that declares one, since "ran zero steps" would read as green.
|
|
242
|
+
|
|
243
|
+
### Bring your own ORM (`webjs.db`)
|
|
244
|
+
|
|
245
|
+
Drizzle is the scaffold DEFAULT, not lock-in. The runtime never imports it, `db/connection.server.ts` is the app's own file, and `webjs db` is adapter-driven: a `db` block maps each verb to the shell command `webjs db <verb>` runs instead of the drizzle-kit default (node_modules/.bin on PATH like a `before` step, extra CLI args appended).
|
|
246
|
+
|
|
247
|
+
```jsonc
|
|
248
|
+
{ "webjs": {
|
|
249
|
+
"db": {
|
|
250
|
+
"generate": "prisma migrate dev --create-only",
|
|
251
|
+
"migrate": "prisma migrate deploy",
|
|
252
|
+
"push": "prisma db push",
|
|
253
|
+
"studio": "prisma studio",
|
|
254
|
+
"reset": "prisma migrate reset --force"
|
|
255
|
+
}
|
|
256
|
+
} }
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Any key is a verb (`reset` above adds `webjs db reset`). A verb the block does not name keeps its default (drizzle-kit for `generate` / `migrate` / `push` / `studio`, `db/seed.server.ts` for `seed`), so an app with no block is unchanged and the scaffold emits none. The payoff is that `webjs db migrate` stays one spelling across ORMs, so the scaffolded `dev.before` / `start.before`, the Dockerfile, CI, and the deploy docs all keep working after a swap. Write the bare binary (`prisma migrate deploy`), not `npx prisma ...`, since a pure Bun image has no `npx`. The swap itself is the app's own files: replace `db/connection.server.ts` with the new client, drop `drizzle.config.ts` / `db/columns.server.ts`, and keep server-only imports behind `.server.ts` as before.
|
|
260
|
+
|
|
219
261
|
### Doctor severity gate
|
|
220
262
|
|
|
221
263
|
`webjs doctor` reports project health, and by default only a broken toolchain fails the exit. `--strict` makes EVERY warning fatal, which is unusable in CI, because four checks are environment-shaped: `GIT_HOOK` wants a local pre-commit hook a runner has no reason to have, `ENV_DRIFT` compares against a `.env` CI does not carry, `VENDOR_PIN` fetches the network, and `FRAMEWORK_RESOLVE` plus `FRAMEWORK_LINKS` depend on the environment. So per-check severity is CONFIG, keyed by the stable code every result carries.
|
|
@@ -115,7 +115,7 @@ So near an island the fragment is the smaller and more predictable choice, not a
|
|
|
115
115
|
|
|
116
116
|
### A design system for repeated PRIMITIVES: class helpers built on `@webjsdev/ui`
|
|
117
117
|
|
|
118
|
-
An `html`-fragment helper is right for a repeated CHUNK of markup (the rubric above). For a repeated UI PRIMITIVE (button, input, card, badge) that needs variants and sizes, use a class helper instead: a function that returns a Tailwind class STRING you spread onto a native element. That is exactly what `@webjsdev/ui` ships (`buttonClass({ variant, size })`, `cardClass()`, `inputClass()`, `badgeClass({ variant })`), and it is what the scaffold gallery uses in `components/ui/`. To style a ONE-OFF that a variant does not cover (a circular icon button, a pill), compose the helper and override the bespoke bits with `cn()`: `cn(buttonClass({ variant: 'secondary', size: 'none' }), 'w-9 h-9 rounded-full')`. `cn` resolves Tailwind conflicts so a later class wins, including a shorthand over the axis it subsumes (`p-0` beats an earlier `px-4 py-2`), so an override just works. Conflicts are keyed on the CSS PROPERTY wherever `cn` can tell the properties apart, rather than on the shared class prefix, so the common prefix collisions do NOT evict: `cn('border-2', 'border-primary')` keeps both (a width and a colour), `cn('flex', 'flex-1')` keeps both (a `display` and a `flex-grow`, the shape an element that is both a flex container and a flex child needs), `cn('shadow-lg', 'shadow-red-500')` keeps both (a box-shadow and its colour), `cn('bg-clip-text', 'bg-primary')` keeps both (a clip and a colour, so the gradient-text idiom survives a later background), and an arbitrary value carrying a type hint is read as the property the hint names (`cn('shadow-lg', 'shadow-[color:red]')` keeps both). It is a small hand-rolled merger, not `tailwind-merge`, so it is still coarse in two ways. A prefix outside the families it knows is not grouped at all, so both classes are emitted and the winner is left to compiled stylesheet order (`inset-shadow-sm` against `inset-shadow-red-500`, `ring-2` against `ring-red-500`). And where one prefix carries two properties it reads the value against Tailwind's DEFAULT scales, so a `@theme`-extended name it cannot know about can still be misread and evict the wrong class: a custom `--shadow-card` makes `shadow-card` a box-shadow, but `cn` sees an unfamiliar name under a prefix whose bare names are usually colours and treats it as one. When an override has to win and you are unsure, pass the one class rather than layering, or install `clsx` + `tailwind-merge` and replace the helper (its header comment shows the swap). For an icon button prefer `size: 'none'` (it states "I supply my own box" by dropping the helper's padding + radius) over layering a `p-0` on top of the default size.
|
|
118
|
+
An `html`-fragment helper is right for a repeated CHUNK of markup (the rubric above). For a repeated UI PRIMITIVE (button, input, card, badge) that needs variants and sizes, use a class helper instead: a function that returns a Tailwind class STRING you spread onto a native element. That is exactly what `@webjsdev/ui` ships (`buttonClass({ variant, size })`, `cardClass()`, `inputClass()`, `badgeClass({ variant })`), and it is what the scaffold gallery uses in `components/ui/`. To style a ONE-OFF that a variant does not cover (a circular icon button, a pill), compose the helper and override the bespoke bits with `cn()`: `cn(buttonClass({ variant: 'secondary', size: 'none' }), 'w-9 h-9 rounded-full')`. `cn` resolves Tailwind conflicts so a later class wins, including a shorthand over the axis it subsumes (`p-0` beats an earlier `px-4 py-2`), so an override just works. Conflicts are keyed on the CSS PROPERTY wherever `cn` can tell the properties apart, rather than on the shared class prefix, so the common prefix collisions do NOT evict: `cn('border-2', 'border-primary')` keeps both (a width and a colour), `cn('flex', 'flex-1')` keeps both (a `display` and a `flex-grow`, the shape an element that is both a flex container and a flex child needs), `cn('shadow-lg', 'shadow-red-500')` keeps both (a box-shadow and its colour), `cn('bg-clip-text', 'bg-primary')` keeps both (a clip and a colour, so the gradient-text idiom survives a later background), and an arbitrary value carrying a type hint is read as the property the hint names (`cn('shadow-lg', 'shadow-[color:red]')` keeps both). It is a small hand-rolled merger, not `tailwind-merge`, so it is still coarse in two ways. A prefix outside the families it knows is not grouped at all, so both classes are emitted and the winner is left to compiled stylesheet order (`inset-shadow-sm` against `inset-shadow-red-500`, `ring-2` against `ring-red-500`). And where one prefix carries two properties it reads the value against Tailwind's DEFAULT scales, so a `@theme`-extended name it cannot know about can still be misread and evict the wrong class: a custom `--shadow-card` makes `shadow-card` a box-shadow, but `cn` sees an unfamiliar name under a prefix whose bare names are usually colours and treats it as one. When an override has to win and you are unsure, pass the one class rather than layering, or install `clsx` + `tailwind-merge` and replace the helper (its header comment shows the swap). For an icon button prefer `size: 'none'` (it states "I supply my own box" by dropping the helper's padding + radius) over layering a `p-0` on top of the default size. Under the opt-in `webjsui lint` (configured by a `lint` block in `components.json`, see `references/ui-kit.md`), `w-9 h-9` is layout and `rounded-full` is shape in the shadcn category taxonomy, so an app running it sets `no-restyle` to `allow: ["layout", "rounded"]` for this idiom to pass: the plain radius group is granted by name without opening the whole `shape` category, and `border-2` beside a helper still fires.
|
|
119
119
|
|
|
120
120
|
```ts
|
|
121
121
|
// components/ui/button.ts (npx webjsdev ui add button, themed to your app)
|
|
@@ -189,6 +189,17 @@ WEBJS_ELIDE=0 npm run test:e2e
|
|
|
189
189
|
|
|
190
190
|
A test that passes under one and fails under the other is a wrong verdict, and `webjs elision` tells you which module and on what evidence. If the component's interactivity is genuinely invisible to static analysis, the fix is `static interactive = true` on it; see `components.md` for what that override does and does not rescue.
|
|
191
191
|
|
|
192
|
+
## One command for every layer (`webjs ci`)
|
|
193
|
+
|
|
194
|
+
```sh
|
|
195
|
+
npm run ci # the webjs.ci list: check, doctor, typecheck, audit, then every test layer
|
|
196
|
+
npm run ci -- --only Tests # one step or group by title
|
|
197
|
+
npm run ci -- --fail-fast # stop at the first failure
|
|
198
|
+
npm run ci -- --json # one JSON document for an agent loop
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The scaffold declares its gate once, in `package.json` under `webjs.ci`, and `npm run ci` runs it with a timed result line per step (the Rails `bin/ci` model). The generated GitHub workflow runs the same list, so a green local run predicts CI. Run it before every push; the pre-commit hook deliberately runs none of it so a commit stays fast. Browser and e2e steps need a Chromium on the machine (`npx playwright install chromium`, plus `puppeteer-core` for the e2e layer), which the workflow installs for itself. The list, the flags, the `--signoff` merge gate, and the JSON shape are in `references/built-ins.md` under "Local CI".
|
|
202
|
+
|
|
192
203
|
## Type-checking your tests (`webjs typecheck`)
|
|
193
204
|
|
|
194
205
|
Your tests are inside the tsconfig `include`, so `npm run typecheck` reads them (#1299). Treat a type error in a test as a failed gate, not a review catch: the checker sees a wrong argument shape or an unannotated parameter in a test the same way it sees one in `app/`.
|
|
@@ -52,6 +52,31 @@ So the loop is: `add` the component, then query `ui <name>` (MCP) or
|
|
|
52
52
|
that ships inside the installed `@webjsdev/ui`, with no network. This pins you
|
|
53
53
|
to the installed version; run `npx webjsdev ui diff` to see where your local copies
|
|
54
54
|
drift from the upstream (that command alone compares against the live registry).
|
|
55
|
+
- `npx webjsdev ui lint` is an OPT-IN design-system linter over the app's own
|
|
56
|
+
source. It reads the Tailwind classes in `html` templates, `cn()` calls and
|
|
57
|
+
`class=${...}` holes and reports, at the line, a raw palette colour where the
|
|
58
|
+
theme declares a role token (`no-raw-colors`, with a message naming only the
|
|
59
|
+
`--color-*` tokens the configured `tailwind.css` actually declares), an
|
|
60
|
+
arbitrary value such as `p-[13px]` (`no-arbitrary-values`; an arbitrary
|
|
61
|
+
VARIANT like `[&_svg]:size-4` never fires), and a class composed over a kit
|
|
62
|
+
helper (`no-restyle`, naming the helper's real variants and sizes read from
|
|
63
|
+
the app's copied `components/ui/*.ts`). It is off until `components.json`
|
|
64
|
+
carries a `lint` block, and with no block it reports nothing and exits 0.
|
|
65
|
+
`components/ui/**` is skipped by default (a copied primitive owns structural
|
|
66
|
+
values no variant expresses), and `allow` uses shadcn's category taxonomy
|
|
67
|
+
(`layout`, `color`, `typography`, `spacing`, `shape`, `effects`, `motion`) or
|
|
68
|
+
a class-group id such as `rounded`. `--json` emits `{ violations, summary }`
|
|
69
|
+
for an agent loop; `--max-warnings <n>` pins a count.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
"lint": {
|
|
73
|
+
"rules": {
|
|
74
|
+
"no-raw-colors": "warn",
|
|
75
|
+
"no-arbitrary-values": { "severity": "warn", "allow": ["layout"] },
|
|
76
|
+
"no-restyle": { "severity": "error", "allow": ["layout", "rounded"] }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
55
80
|
|
|
56
81
|
## Inventory (run `npx webjsdev ui list` or the MCP `ui` tool for the authoritative, current set)
|
|
57
82
|
|
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## Test plan
|
|
6
6
|
|
|
7
|
-
- [ ]
|
|
8
|
-
- [ ]
|
|
9
|
-
- [ ]
|
|
10
|
-
- [ ] `webjs doctor` passes (project health; it fails on whatever `webjs.doctor.gate` marks `error`, plus the hard `NODE_VERSION` / `TSCONFIG_ERASABLE` checks)
|
|
7
|
+
- [ ] `webjs ci` passes locally (the `webjs.ci` step list in package.json: `webjs check`, `webjs doctor`, `webjs typecheck`, the dependency audit, and the server / browser / e2e test layers; CI runs the same list)
|
|
8
|
+
- [ ] Unit tests added/updated
|
|
9
|
+
- [ ] E2E tests added/updated for user-facing changes (`WEBJS_E2E=1 webjs test`)
|
|
11
10
|
|
|
12
11
|
## Definition of done
|
|
13
12
|
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
name: CI
|
|
2
2
|
|
|
3
|
-
# The
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
3
|
+
# The cloud half of CI for {{APP_NAME}}. The step list itself lives in
|
|
4
|
+
# package.json under "webjs": { "ci": { "steps": [...] } }, and `npm run ci`
|
|
5
|
+
# runs it: on a developer machine before a push, and here on every PR into
|
|
6
|
+
# main and every push to main, so the two can never drift. This job only
|
|
7
|
+
# prepares the runner (Node, dependencies, a browser, the database) and then
|
|
8
|
+
# runs that one command.
|
|
8
9
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
10
|
+
# One job on purpose. Each step is still a collapsible log group with its own
|
|
11
|
+
# result line, a failing step is annotated, and the run's step table lands in
|
|
12
|
+
# the job summary, so a failure names the layer that broke. Mark this job as a
|
|
13
|
+
# required status check in the branch-protection rule for main. A team that
|
|
14
|
+
# wants one required check PER LAYER can add a matrix job that runs
|
|
15
|
+
# `npx webjs ci --only "<group title>"` per entry.
|
|
16
|
+
#
|
|
17
|
+
# The local pre-commit hook deliberately runs none of this, so a commit stays
|
|
18
|
+
# fast; `npm run ci` before pushing is the local gate. To hold a merge until a
|
|
19
|
+
# LOCAL run is green, `npm run ci -- --signoff` posts a green commit status
|
|
20
|
+
# through basecamp/gh-signoff, which branch protection can require (`gh signoff
|
|
21
|
+
# install`), the Rails posture. Free on public repos (ubuntu-latest has
|
|
22
|
+
# unlimited Actions minutes).
|
|
13
23
|
|
|
14
24
|
on:
|
|
15
25
|
pull_request:
|
|
@@ -17,40 +27,21 @@ on:
|
|
|
17
27
|
push:
|
|
18
28
|
branches: [main]
|
|
19
29
|
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
|
|
20
33
|
# A newer push to the same branch cancels the older in-flight run.
|
|
21
34
|
concurrency:
|
|
22
35
|
group: ci-${{ github.ref }}
|
|
23
36
|
cancel-in-progress: true
|
|
24
37
|
|
|
25
38
|
jobs:
|
|
26
|
-
|
|
27
|
-
name:
|
|
28
|
-
runs-on: ubuntu-latest
|
|
29
|
-
steps:
|
|
30
|
-
- uses: actions/checkout@v6
|
|
31
|
-
- uses: actions/setup-node@v6
|
|
32
|
-
with:
|
|
33
|
-
node-version: '24'
|
|
34
|
-
cache: npm
|
|
35
|
-
- run: npm ci
|
|
36
|
-
- run: npm run check
|
|
37
|
-
# Project health, on top of the correctness checks. WHICH findings are
|
|
38
|
-
# fatal is your call, declared in package.json under
|
|
39
|
-
# "webjs": { "doctor": { "gate": { "<CODE>": "off" | "warn" | "error" } } },
|
|
40
|
-
# so this step and a local `npm run doctor` always agree. The scaffold
|
|
41
|
-
# starts with UNMARKED_ASSET_LINKS at error (an un-versioned /public url
|
|
42
|
-
# is a real deploy-staleness bug). Two checks fail with no gate entry at
|
|
43
|
-
# all, NODE_VERSION and TSCONFIG_ERASABLE, because either would 500 the
|
|
44
|
-
# app at runtime; everything else stays a warn and cannot fail this job.
|
|
45
|
-
# Widen or narrow the gate in package.json, not
|
|
46
|
-
# here. Deliberately not --strict: the git-hook, env-drift, vendor-pin,
|
|
47
|
-
# and framework-resolve checks are environment-shaped and would fail a
|
|
48
|
-
# perfectly healthy runner.
|
|
49
|
-
- run: npm run doctor
|
|
50
|
-
|
|
51
|
-
unit:
|
|
52
|
-
name: Unit + integration (node --test)
|
|
39
|
+
ci:
|
|
40
|
+
name: CI (npm run ci)
|
|
53
41
|
runs-on: ubuntu-latest
|
|
42
|
+
timeout-minutes: 20
|
|
43
|
+
env:
|
|
44
|
+
DATABASE_URL: file:./ci.db
|
|
54
45
|
steps:
|
|
55
46
|
- uses: actions/checkout@v6
|
|
56
47
|
- uses: actions/setup-node@v6
|
|
@@ -58,58 +49,17 @@ jobs:
|
|
|
58
49
|
node-version: '24'
|
|
59
50
|
cache: npm
|
|
60
51
|
- run: npm ci
|
|
61
|
-
- name:
|
|
62
|
-
run: npm run db:generate && npm run db:migrate
|
|
63
|
-
env:
|
|
64
|
-
DATABASE_URL: file:./ci.db
|
|
65
|
-
# --server keeps this job to node:test (the browser layer is its own
|
|
66
|
-
# job below). Without WEBJS_E2E the e2e folders are skipped too.
|
|
67
|
-
- run: npm run test:server
|
|
68
|
-
env:
|
|
69
|
-
DATABASE_URL: file:./ci.db
|
|
70
|
-
|
|
71
|
-
browser:
|
|
72
|
-
name: Browser (web-test-runner / Playwright)
|
|
73
|
-
runs-on: ubuntu-latest
|
|
74
|
-
steps:
|
|
75
|
-
- uses: actions/checkout@v6
|
|
76
|
-
- uses: actions/setup-node@v6
|
|
77
|
-
with:
|
|
78
|
-
node-version: '24'
|
|
79
|
-
cache: npm
|
|
80
|
-
- run: npm ci
|
|
81
|
-
- name: Install Playwright Chromium
|
|
52
|
+
- name: Install Playwright Chromium (browser + e2e layers)
|
|
82
53
|
run: npx playwright install --with-deps chromium
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
- uses: actions/checkout@v6
|
|
90
|
-
- uses: actions/setup-node@v6
|
|
91
|
-
with:
|
|
92
|
-
node-version: '24'
|
|
93
|
-
cache: npm
|
|
94
|
-
- run: npm ci
|
|
95
|
-
- name: Set up the database (generate + apply migrations)
|
|
96
|
-
run: npm run db:generate && npm run db:migrate
|
|
97
|
-
env:
|
|
98
|
-
DATABASE_URL: file:./ci.db
|
|
99
|
-
# The scaffold's e2e test (test/hello/e2e/) drives a real browser
|
|
100
|
-
# via puppeteer-core, which is not a default dependency (the test
|
|
101
|
-
# skips when it is absent). Install it and Chromium so the e2e
|
|
102
|
-
# layer actually runs in CI rather than skipping silently.
|
|
103
|
-
- name: Install puppeteer-core + Chromium
|
|
104
|
-
run: |
|
|
105
|
-
npm install --no-save puppeteer-core
|
|
106
|
-
npx playwright install --with-deps chromium
|
|
54
|
+
# The scaffold's e2e test (test/hello/e2e/) drives a real browser via
|
|
55
|
+
# puppeteer-core, which is not a default dependency (the test skips when
|
|
56
|
+
# it is absent). Install it so the e2e layer runs here rather than
|
|
57
|
+
# skipping silently.
|
|
58
|
+
- name: Install puppeteer-core
|
|
59
|
+
run: npm install --no-save puppeteer-core
|
|
107
60
|
- name: Resolve the Chromium binary path
|
|
108
61
|
run: echo "CHROMIUM_PATH=$(node -e "console.log(require('playwright-core').chromium.executablePath())")" >> "$GITHUB_ENV"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
WEBJS_E2E: '1'
|
|
114
|
-
DATABASE_URL: file:./ci.db
|
|
115
|
-
run: npm run test:server
|
|
62
|
+
- name: Set up the database (generate + apply migrations)
|
|
63
|
+
run: npm run db:generate && npm run db:migrate
|
|
64
|
+
# Everything above prepares the runner. This is the whole gate.
|
|
65
|
+
- run: npm run ci
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
#
|
|
8
8
|
# To bypass in emergencies: git commit --no-verify
|
|
9
9
|
#
|
|
10
|
-
# Tests and convention checks run
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# on every push and pull
|
|
10
|
+
# Tests and convention checks do not run here, so a commit stays fast. The
|
|
11
|
+
# step list lives in package.json under "webjs": { "ci" }. Run it locally
|
|
12
|
+
# with `npm run ci` before pushing (the local gate), and the CI workflow
|
|
13
|
+
# (.github/workflows/ci.yml) runs the same list on every push and pull
|
|
14
|
+
# request, where a local --no-verify cannot skip it.
|
|
14
15
|
#
|
|
15
16
|
# Running more than one AI agent on this repo at once? Give each task its own
|
|
16
17
|
# git worktree, not a shared checkout. Two agents in one working directory
|
|
@@ -44,15 +44,20 @@ cross-origin access use the `cors()` middleware from `@webjsdev/server`; with
|
|
|
44
44
|
|
|
45
45
|
### 5. Verify before you call it done
|
|
46
46
|
|
|
47
|
-
Run
|
|
47
|
+
Run `npm run ci` and fix what it reports. It is one command for every gate,
|
|
48
|
+
the step list declared in `package.json` under `webjs.ci`, with a result line
|
|
49
|
+
per step:
|
|
48
50
|
|
|
49
|
-
- `
|
|
50
|
-
- `
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
51
|
+
- `webjs check` (correctness: no browser-import or boundary violation).
|
|
52
|
+
- `webjs doctor` (project health). It fails on whatever `package.json`
|
|
53
|
+
`webjs.doctor.gate` marks `error`, plus the two hard toolchain checks that
|
|
54
|
+
are fatal with no gate entry, `NODE_VERSION` and `TSCONFIG_ERASABLE`.
|
|
55
|
+
- `webjs typecheck` (zero type errors).
|
|
56
|
+
- A dependency audit.
|
|
57
|
+
- The test layers for the endpoints and modules you built.
|
|
58
|
+
|
|
59
|
+
The GitHub workflow runs the same list, so a green local run predicts CI.
|
|
60
|
+
While iterating, `npm run ci -- --only Tests` runs one layer.
|
|
56
61
|
|
|
57
62
|
Then boot `npm run dev` and probe each endpoint for the expected status and JSON
|
|
58
63
|
shape.
|
|
@@ -66,6 +71,7 @@ npm run dev # dev server at http://localhost:8080
|
|
|
66
71
|
npm run start # production server
|
|
67
72
|
npm test # unit + browser tests
|
|
68
73
|
npm run typecheck
|
|
74
|
+
npm run ci # every gate, one command (the webjs.ci steps in package.json)
|
|
69
75
|
npm run check # correctness checks
|
|
70
76
|
npm run doctor # project health (severity per check: webjs.doctor.gate)
|
|
71
77
|
npm run db:generate && npm run db:migrate
|
|
@@ -95,16 +95,21 @@ accessor). Use the shorthand for primitives
|
|
|
95
95
|
|
|
96
96
|
### 7. Verify before you call it done
|
|
97
97
|
|
|
98
|
-
Run
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
`
|
|
105
|
-
|
|
106
|
-
- `
|
|
107
|
-
-
|
|
98
|
+
Run `npm run ci` and fix what it reports. It is one command for every gate,
|
|
99
|
+
the step list declared in `package.json` under `webjs.ci`, with a result line
|
|
100
|
+
per step:
|
|
101
|
+
|
|
102
|
+
- `webjs check` (correctness: no browser-import or boundary violation).
|
|
103
|
+
- `webjs doctor` (project health). It fails on whatever `package.json`
|
|
104
|
+
`webjs.doctor.gate` marks `error`, plus the two hard toolchain checks that
|
|
105
|
+
are fatal with no gate entry, `NODE_VERSION` and `TSCONFIG_ERASABLE`.
|
|
106
|
+
- `webjs typecheck` (zero type errors).
|
|
107
|
+
- A dependency audit.
|
|
108
|
+
- The server, browser, and e2e test layers for the features you built.
|
|
109
|
+
|
|
110
|
+
The GitHub workflow runs the same list, so a green local run predicts CI.
|
|
111
|
+
While iterating, `npm run ci -- --only Tests` runs one layer. Then
|
|
112
|
+
`npm run css:build` (compile Tailwind).
|
|
108
113
|
|
|
109
114
|
Then boot `npm run dev`, confirm every page route returns HTTP 200, and open
|
|
110
115
|
every route you changed in a real browser and play through its states: `check`
|
|
@@ -121,6 +126,7 @@ npm run start # production server
|
|
|
121
126
|
npm test # unit + browser tests
|
|
122
127
|
npm run typecheck
|
|
123
128
|
npm run css:build # compile Tailwind
|
|
129
|
+
npm run ci # every gate, one command (the webjs.ci steps in package.json)
|
|
124
130
|
npm run check # correctness checks
|
|
125
131
|
npm run doctor # project health (severity per check: webjs.doctor.gate)
|
|
126
132
|
npx webjsdev ui add <name> # copy a ui primitive into components/ui/
|