@webjsdev/cli 0.10.1 → 0.10.3
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/bin/webjs.js +22 -8
- package/lib/create.js +9 -0
- package/package.json +1 -1
- package/templates/.dockerignore +43 -0
- package/templates/.github/workflows/ci.yml +105 -0
- package/templates/.hooks/pre-commit +6 -50
- package/templates/AGENTS.md +30 -12
- package/templates/CONVENTIONS.md +20 -11
- package/templates/Dockerfile +49 -0
- package/templates/compose.yaml +36 -0
package/bin/webjs.js
CHANGED
|
@@ -148,17 +148,31 @@ async function main() {
|
|
|
148
148
|
const { readdir } = await import('node:fs/promises');
|
|
149
149
|
const testFiles = [];
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
// Walk test/ recursively so the documented feature-folder layout
|
|
152
|
+
// (test/<feature>/<name>.test.ts) is discovered, not just files
|
|
153
|
+
// sitting directly in test/. Two kinds are NOT run here:
|
|
154
|
+
// - **/browser/** → real-browser tests, owned by WTR below.
|
|
155
|
+
// - **/e2e/** → full-app boot, opt-in via WEBJS_E2E=1 (the
|
|
156
|
+
// documented "WEBJS_E2E=1 webjs test adds the
|
|
157
|
+
// e2e tests" semantics).
|
|
158
|
+
const runE2E = !!process.env.WEBJS_E2E;
|
|
159
|
+
const walk = async (dir, segments) => {
|
|
160
|
+
let entries;
|
|
161
|
+
try { entries = await readdir(dir, { withFileTypes: true }); }
|
|
162
|
+
catch { return; }
|
|
163
|
+
for (const ent of entries) {
|
|
164
|
+
if (ent.name === 'node_modules') continue;
|
|
165
|
+
const full = join(dir, ent.name);
|
|
166
|
+
if (ent.isDirectory()) {
|
|
167
|
+
if (ent.name === 'browser') continue;
|
|
168
|
+
if (ent.name === 'e2e' && !runE2E) continue;
|
|
169
|
+
await walk(full, [...segments, ent.name]);
|
|
170
|
+
} else if (/\.test\.(js|ts|mjs|mts)$/.test(ent.name)) {
|
|
158
171
|
if (!testFiles.includes(full)) testFiles.push(full);
|
|
159
172
|
}
|
|
160
173
|
}
|
|
161
|
-
}
|
|
174
|
+
};
|
|
175
|
+
await walk(join(cwd, 'test'), []);
|
|
162
176
|
|
|
163
177
|
if (testFiles.length > 0) {
|
|
164
178
|
console.log(`webjs test: running ${testFiles.length} server test file(s)…\n`);
|
package/lib/create.js
CHANGED
|
@@ -392,7 +392,16 @@ export async function scaffoldApp(name, cwd, opts = {}) {
|
|
|
392
392
|
'.cursorrules',
|
|
393
393
|
'.github/copilot-instructions.md',
|
|
394
394
|
'.github/pull_request_template.md',
|
|
395
|
+
// CI is the test gate (the pre-commit hook only blocks main). Runs
|
|
396
|
+
// webjs check + the unit / browser / e2e layers on every PR and push
|
|
397
|
+
// to main, mirroring the webjs framework's own CI.
|
|
398
|
+
'.github/workflows/ci.yml',
|
|
395
399
|
'.editorconfig',
|
|
400
|
+
// Production / deploy scaffolding. `docker compose up --build` runs
|
|
401
|
+
// the app locally with the same Dockerfile production builds from.
|
|
402
|
+
'Dockerfile',
|
|
403
|
+
'compose.yaml',
|
|
404
|
+
'.dockerignore',
|
|
396
405
|
];
|
|
397
406
|
for (const f of templateFiles) {
|
|
398
407
|
const src = join(TEMPLATES, f);
|
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
.git
|
|
2
|
+
.github
|
|
3
|
+
node_modules
|
|
4
|
+
**/node_modules
|
|
5
|
+
|
|
6
|
+
# `.webjs/` is ignored EXCEPT for `.webjs/vendor/`, which holds the committed
|
|
7
|
+
# importmap manifest (and optionally downloaded bundle bytes) the server needs
|
|
8
|
+
# at boot without reaching api.jspm.io. DO NOT collapse to `**/.webjs`: parent
|
|
9
|
+
# exclusion blocks child negations and the vendor files would never ship.
|
|
10
|
+
.webjs/*
|
|
11
|
+
!.webjs/vendor/
|
|
12
|
+
!.webjs/vendor/**
|
|
13
|
+
|
|
14
|
+
dist
|
|
15
|
+
build
|
|
16
|
+
out
|
|
17
|
+
.cache
|
|
18
|
+
|
|
19
|
+
# Local env files. The container gets its env from compose / uncloud, not a
|
|
20
|
+
# committed file. Keep the example for reference.
|
|
21
|
+
.env
|
|
22
|
+
!.env.example
|
|
23
|
+
|
|
24
|
+
# Prisma local SQLite. Schema + migrations ship; the runtime volume owns the db.
|
|
25
|
+
dev.db
|
|
26
|
+
dev.db-journal
|
|
27
|
+
prisma/dev.db
|
|
28
|
+
prisma/dev.db-journal
|
|
29
|
+
|
|
30
|
+
# logs
|
|
31
|
+
*.log
|
|
32
|
+
npm-debug.log*
|
|
33
|
+
|
|
34
|
+
# editors / OS
|
|
35
|
+
.vscode
|
|
36
|
+
.idea
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# tests aren't needed in the production image
|
|
41
|
+
coverage/
|
|
42
|
+
test/
|
|
43
|
+
**/test/
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# The test gate for {{APP_NAME}}. Runs the full test pyramid on every PR
|
|
4
|
+
# into main and on every push to main. This is the gate the local
|
|
5
|
+
# pre-commit hook deliberately leaves out, so `git commit` stays fast and
|
|
6
|
+
# the test gate runs in one authoritative place a local --no-verify cannot
|
|
7
|
+
# skip. Same posture as the webjs framework's own CI.
|
|
8
|
+
#
|
|
9
|
+
# The four layers run as separate jobs so a failure names the layer that
|
|
10
|
+
# broke. Mark all four as required status checks in the branch-protection
|
|
11
|
+
# rule for main so a PR can only merge when every layer is green. Free on
|
|
12
|
+
# public repos (ubuntu-latest has unlimited Actions minutes).
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
branches: [main]
|
|
17
|
+
push:
|
|
18
|
+
branches: [main]
|
|
19
|
+
|
|
20
|
+
# A newer push to the same branch cancels the older in-flight run.
|
|
21
|
+
concurrency:
|
|
22
|
+
group: ci-${{ github.ref }}
|
|
23
|
+
cancel-in-progress: true
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
conventions:
|
|
27
|
+
name: Conventions (webjs check)
|
|
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
|
+
|
|
38
|
+
unit:
|
|
39
|
+
name: Unit + integration (node --test)
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v6
|
|
43
|
+
- uses: actions/setup-node@v6
|
|
44
|
+
with:
|
|
45
|
+
node-version: '24'
|
|
46
|
+
cache: npm
|
|
47
|
+
- run: npm ci
|
|
48
|
+
- run: npx prisma generate
|
|
49
|
+
- name: Apply migrations to the test database
|
|
50
|
+
run: npx prisma migrate deploy
|
|
51
|
+
env:
|
|
52
|
+
DATABASE_URL: file:./ci.db
|
|
53
|
+
# --server keeps this job to node:test (the browser layer is its own
|
|
54
|
+
# job below). Without WEBJS_E2E the e2e folders are skipped too.
|
|
55
|
+
- run: npm run test:server
|
|
56
|
+
env:
|
|
57
|
+
DATABASE_URL: file:./ci.db
|
|
58
|
+
|
|
59
|
+
browser:
|
|
60
|
+
name: Browser (web-test-runner / Playwright)
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v6
|
|
64
|
+
- uses: actions/setup-node@v6
|
|
65
|
+
with:
|
|
66
|
+
node-version: '24'
|
|
67
|
+
cache: npm
|
|
68
|
+
- run: npm ci
|
|
69
|
+
- run: npx prisma generate
|
|
70
|
+
- name: Install Playwright Chromium
|
|
71
|
+
run: npx playwright install --with-deps chromium
|
|
72
|
+
- run: npm run test:browser
|
|
73
|
+
|
|
74
|
+
e2e:
|
|
75
|
+
name: E2E (full app boot)
|
|
76
|
+
runs-on: ubuntu-latest
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v6
|
|
79
|
+
- uses: actions/setup-node@v6
|
|
80
|
+
with:
|
|
81
|
+
node-version: '24'
|
|
82
|
+
cache: npm
|
|
83
|
+
- run: npm ci
|
|
84
|
+
- run: npx prisma generate
|
|
85
|
+
- name: Apply migrations to the test database
|
|
86
|
+
run: npx prisma migrate deploy
|
|
87
|
+
env:
|
|
88
|
+
DATABASE_URL: file:./ci.db
|
|
89
|
+
# The scaffold's e2e test (test/hello/e2e/) drives a real browser
|
|
90
|
+
# via puppeteer-core, which is not a default dependency (the test
|
|
91
|
+
# skips when it is absent). Install it and Chromium so the e2e
|
|
92
|
+
# layer actually runs in CI rather than skipping silently.
|
|
93
|
+
- name: Install puppeteer-core + Chromium
|
|
94
|
+
run: |
|
|
95
|
+
npm install --no-save puppeteer-core
|
|
96
|
+
npx playwright install --with-deps chromium
|
|
97
|
+
- name: Resolve the Chromium binary path
|
|
98
|
+
run: echo "CHROMIUM_PATH=$(node -e "console.log(require('playwright-core').chromium.executablePath())")" >> "$GITHUB_ENV"
|
|
99
|
+
# --server with WEBJS_E2E=1 runs node:test including the e2e folders
|
|
100
|
+
# (the runner gates them on that env var) and skips the browser layer.
|
|
101
|
+
- name: Run e2e
|
|
102
|
+
env:
|
|
103
|
+
WEBJS_E2E: '1'
|
|
104
|
+
DATABASE_URL: file:./ci.db
|
|
105
|
+
run: npm run test:server
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
#
|
|
3
|
-
# pre-commit hook
|
|
3
|
+
# pre-commit hook. Blocks commits on main/master.
|
|
4
4
|
#
|
|
5
5
|
# No AI agent, no editor, no human can commit to main directly.
|
|
6
6
|
# Create a feature branch first. This is git-level enforcement.
|
|
7
7
|
#
|
|
8
8
|
# To bypass in emergencies: git commit --no-verify
|
|
9
|
+
#
|
|
10
|
+
# Tests and convention checks run in CI (.github/workflows/ci.yml), not
|
|
11
|
+
# here, so a commit stays fast and the test gate cannot be skipped by a
|
|
12
|
+
# local --no-verify. The CI workflow runs `webjs check` + `webjs test`
|
|
13
|
+
# on every push and pull request.
|
|
9
14
|
|
|
10
15
|
BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
|
|
11
16
|
|
|
@@ -21,53 +26,4 @@ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
|
21
26
|
exit 1
|
|
22
27
|
fi
|
|
23
28
|
|
|
24
|
-
# Require a test to accompany app-code changes. Tool-agnostic floor: a
|
|
25
|
-
# commit that stages app code (app/, modules/, components/, lib/) with no
|
|
26
|
-
# test (test/** or *.test.* / *.spec.*) is blocked. The full suite below
|
|
27
|
-
# proves tests PASS; this proves a test was WRITTEN. Bypass for a genuine
|
|
28
|
-
# non-code commit with WEBJS_NO_TEST_GATE=1, or --no-verify in emergencies.
|
|
29
|
-
if [ "${WEBJS_NO_TEST_GATE:-}" != "1" ]; then
|
|
30
|
-
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
31
|
-
APP_CODE=$(printf '%s\n' "$STAGED" | grep -E '^(app|modules|components|lib)/.*\.([mc]?[jt]sx?)$' || true)
|
|
32
|
-
if [ -n "$APP_CODE" ]; then
|
|
33
|
-
TESTS=$(printf '%s\n' "$STAGED" | grep -E '(^|/)test/|\.test\.[mc]?[jt]sx?$|\.spec\.[mc]?[jt]sx?$' || true)
|
|
34
|
-
if [ -z "$TESTS" ]; then
|
|
35
|
-
echo ""
|
|
36
|
-
echo "ERROR: app code changed but no test is staged."
|
|
37
|
-
echo "Every change ships with a test. Add or update the test that"
|
|
38
|
-
echo "proves the new behaviour (a browser/e2e test for interactive"
|
|
39
|
-
echo "or component code), then git add it."
|
|
40
|
-
echo ""
|
|
41
|
-
echo "Genuine non-code commit? WEBJS_NO_TEST_GATE=1 git commit ..."
|
|
42
|
-
echo "Emergency bypass: git commit --no-verify"
|
|
43
|
-
echo ""
|
|
44
|
-
exit 1
|
|
45
|
-
fi
|
|
46
|
-
fi
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
# webjs test + webjs check on every commit. Tool-agnostic enforcement:
|
|
50
|
-
# fires regardless of which agent (Claude, Cursor, Antigravity, Copilot,
|
|
51
|
-
# human) is making the commit. Skipped if the CLI is not yet installed
|
|
52
|
-
# (fresh clone before npm install).
|
|
53
|
-
if command -v webjs >/dev/null 2>&1 || [ -x "node_modules/.bin/webjs" ]; then
|
|
54
|
-
echo "Running webjs test..."
|
|
55
|
-
if ! npx --no-install webjs test; then
|
|
56
|
-
echo ""
|
|
57
|
-
echo "ERROR: webjs test failed. Fix tests before committing."
|
|
58
|
-
echo "To bypass (emergencies only): git commit --no-verify"
|
|
59
|
-
echo ""
|
|
60
|
-
exit 1
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
echo "Running webjs check..."
|
|
64
|
-
if ! npx --no-install webjs check; then
|
|
65
|
-
echo ""
|
|
66
|
-
echo "ERROR: webjs check failed. Fix convention violations before committing."
|
|
67
|
-
echo "To bypass (emergencies only): git commit --no-verify"
|
|
68
|
-
echo ""
|
|
69
|
-
exit 1
|
|
70
|
-
fi
|
|
71
|
-
fi
|
|
72
|
-
|
|
73
29
|
exit 0
|
package/templates/AGENTS.md
CHANGED
|
@@ -301,6 +301,16 @@ In Docker / Railway, prefer `npm start` (or `node node_modules/.bin/npm
|
|
|
301
301
|
start`) as the CMD over `node ... webjs.js start ...`. The npm form
|
|
302
302
|
fires `prestart`; the direct binary form skips it.
|
|
303
303
|
|
|
304
|
+
**Containerized deploy ships with the scaffold.** `Dockerfile`,
|
|
305
|
+
`compose.yaml`, and `.dockerignore` are scaffolded at the app root. The
|
|
306
|
+
Dockerfile pins `node:24-alpine` (the same Node major CI uses), installs
|
|
307
|
+
deps, runs `prisma generate`, and starts via `npm start` so `prestart`
|
|
308
|
+
applies migrations. Run it locally with `docker compose up --build` (the
|
|
309
|
+
app comes up on http://localhost:8080 against a SQLite file on a named
|
|
310
|
+
volume). For production, point `DATABASE_URL` at managed Postgres and set
|
|
311
|
+
`AUTH_SECRET`. The `.dockerignore` keeps the `.webjs/vendor/` importmap in
|
|
312
|
+
the image while excluding `node_modules`, tests, and local state.
|
|
313
|
+
|
|
304
314
|
**Health and readiness probes.** Every webjs server answers two endpoints:
|
|
305
315
|
`/__webjs/health` (liveness, 200 once the process is listening) and
|
|
306
316
|
`/__webjs/ready` (readiness, 503 until the instance is fully warm, then 200).
|
|
@@ -308,12 +318,17 @@ Fully warm means the deterministic analysis AND the first vendor attempt have
|
|
|
308
318
|
both completed, so the importmap and its build id are settled. Point your
|
|
309
319
|
platform's readiness check at `/__webjs/ready` so it holds traffic off a
|
|
310
320
|
not-yet-warmed instance instead of routing the first user request into the cold
|
|
311
|
-
analysis or the brief window where the importmap is still resolving.
|
|
312
|
-
|
|
313
|
-
`
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
analysis or the brief window where the importmap is still resolving. The
|
|
322
|
+
scaffolded `Dockerfile` and `compose.yaml` already wire this up with a
|
|
323
|
+
`HEALTHCHECK` that probes `/__webjs/ready`, so any Docker-based deploy gets the
|
|
324
|
+
gate with no extra config. On a platform that reads its own config instead,
|
|
325
|
+
point its equivalent knob at the same path: Railway `"healthcheckPath":
|
|
326
|
+
"/__webjs/ready"`, Render `healthCheckPath: /__webjs/ready`, Fly a
|
|
327
|
+
`[[http_service.checks]]` on `/__webjs/ready`, or a Kubernetes `readinessProbe`
|
|
328
|
+
with `httpGet.path: /__webjs/ready`. For dependency-aware readiness (gate on a
|
|
329
|
+
live DB ping), add an optional `readiness.{js,ts}` at the app root that
|
|
330
|
+
default-exports an async check; `/__webjs/ready` runs it once warm and reports
|
|
331
|
+
503 if it returns `false` or throws.
|
|
317
332
|
|
|
318
333
|
Scripts:
|
|
319
334
|
|
|
@@ -889,9 +904,11 @@ composition, so a nested shell ends up dropped by the HTML parser.
|
|
|
889
904
|
action called from the client needs a browser test
|
|
890
905
|
(`webjs test --browser`) asserting the behaviour in a real browser. A
|
|
891
906
|
commit that stages app code (`app/`, `modules/`, `components/`, `lib/`)
|
|
892
|
-
with no test is blocked by
|
|
893
|
-
|
|
894
|
-
|
|
907
|
+
with no test is blocked for Claude Code by
|
|
908
|
+
`.claude/hooks/require-tests-with-src.sh`. The test suite itself runs in
|
|
909
|
+
CI (`.github/workflows/ci.yml`), not in the pre-commit hook, so `git
|
|
910
|
+
commit` stays fast and the gate cannot be skipped with a local
|
|
911
|
+
`--no-verify`.
|
|
895
912
|
3. Commit and push **per logical unit**, not at the end. A logical unit is one
|
|
896
913
|
feature, one fix, one rename, one doc rewrite. If you have 5+ unstaged files
|
|
897
914
|
spanning different concerns, commit the current group before continuing.
|
|
@@ -907,9 +924,10 @@ composition, so a nested shell ends up dropped by the HTML parser.
|
|
|
907
924
|
| Antigravity (Google) | text rule only (post-write hooks not yet exposed) | `.agents/rules/workflow.md` |
|
|
908
925
|
| GitHub Copilot | text rule only (no hooks API) | `.github/copilot-instructions.md` |
|
|
909
926
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
927
|
+
The `.hooks/pre-commit` hook blocks commits to main and nothing else;
|
|
928
|
+
`webjs test` + `webjs check` run in CI (`.github/workflows/ci.yml`) on
|
|
929
|
+
every PR and push to main, regardless of which agent (or human) made
|
|
930
|
+
the commit. No AI attribution trailers in commit messages.
|
|
913
931
|
4. Run the **pre-merge self-review loop** before signaling the PR is
|
|
914
932
|
ready. After committing the work, trigger a fresh-context review
|
|
915
933
|
pass (a new chat / composer tab / subagent / Cascade thread
|
package/templates/CONVENTIONS.md
CHANGED
|
@@ -451,14 +451,14 @@ test/
|
|
|
451
451
|
- `webjs test --browser` (or `npx wtr`) runs the browser tests.
|
|
452
452
|
- `WEBJS_E2E=1 webjs test` adds the e2e tests.
|
|
453
453
|
|
|
454
|
-
**Every change ships with a test
|
|
455
|
-
|
|
456
|
-
staging a test is blocked by `.claude/hooks/require-tests-with-src.sh
|
|
457
|
-
(Claude Code) and the universal `.hooks/pre-commit` (any agent or human).
|
|
454
|
+
**Every change ships with a test.** For Claude Code, a commit that
|
|
455
|
+
stages app code (`app/`, `modules/`, `components/`, `lib/`) without
|
|
456
|
+
staging a test is blocked by `.claude/hooks/require-tests-with-src.sh`.
|
|
458
457
|
A unit test alone is not enough for interactive or component code: add
|
|
459
|
-
the browser test that asserts the rendered/hydrated behaviour.
|
|
460
|
-
|
|
461
|
-
|
|
458
|
+
the browser test that asserts the rendered/hydrated behaviour. The test
|
|
459
|
+
suite itself runs in CI (`.github/workflows/ci.yml`) on every PR and
|
|
460
|
+
push to main, not in the local pre-commit hook, so `git commit` stays
|
|
461
|
+
fast and the gate cannot be skipped with a local `--no-verify`.
|
|
462
462
|
|
|
463
463
|
### Choosing a feature folder
|
|
464
464
|
|
|
@@ -1003,10 +1003,19 @@ This project enforces a git workflow via agent-specific config files
|
|
|
1003
1003
|
Other agents enforce this via `.cursorrules`, `.agents/rules/workflow.md`,
|
|
1004
1004
|
`.github/copilot-instructions.md`.
|
|
1005
1005
|
|
|
1006
|
-
**Pre-commit
|
|
1007
|
-
- `
|
|
1008
|
-
|
|
1009
|
-
|
|
1006
|
+
**Pre-commit hook (`.hooks/pre-commit`):**
|
|
1007
|
+
- Blocks commits to `main` / `master`. Nothing else runs locally, so
|
|
1008
|
+
`git commit` stays fast. Keeping commits to one logical unit (no
|
|
1009
|
+
unrelated files) is your discipline plus the `nudge-uncommitted`
|
|
1010
|
+
hooks, not something this hook enforces.
|
|
1011
|
+
|
|
1012
|
+
**CI gate (`.github/workflows/ci.yml`), on every PR and push to main:**
|
|
1013
|
+
- `webjs check` (conventions) must pass
|
|
1014
|
+
- `webjs test` (unit + integration), the browser layer, and the e2e
|
|
1015
|
+
layer must pass
|
|
1016
|
+
- Mark these as required status checks in the branch-protection rule for
|
|
1017
|
+
main so a PR can only merge when the gate is green. The gate lives in
|
|
1018
|
+
CI, where a local `--no-verify` cannot skip it.
|
|
1010
1019
|
|
|
1011
1020
|
---
|
|
1012
1021
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Production image for the {{APP_NAME}} webjs app.
|
|
2
|
+
#
|
|
3
|
+
# Works with a plain `docker build` / `docker compose up`, and is the same
|
|
4
|
+
# artifact the webdeploy hosting tool (ubicloud + uncloud) builds and ships.
|
|
5
|
+
#
|
|
6
|
+
# webjs serves .ts directly via Node's built-in type-stripping, so there is
|
|
7
|
+
# NO JavaScript build step. **Node 24+ is REQUIRED**: on older Node the runtime
|
|
8
|
+
# falls back to esbuild, whose class-declaration transform breaks webjs's SSR
|
|
9
|
+
# walker for multi-class component files. Do not lower this base image
|
|
10
|
+
# below 24 (the same version the CI workflow and the framework pin).
|
|
11
|
+
FROM node:24-alpine
|
|
12
|
+
|
|
13
|
+
# openssl + ca-certificates are required by Prisma's query engine at runtime.
|
|
14
|
+
RUN apk add --no-cache openssl ca-certificates
|
|
15
|
+
|
|
16
|
+
WORKDIR /app
|
|
17
|
+
|
|
18
|
+
# Install deps first so this layer is cached unless the manifests change.
|
|
19
|
+
# package-lock.json is optional (it's absent when the app was scaffolded with
|
|
20
|
+
# --no-install); the glob keeps the COPY working with or without it.
|
|
21
|
+
COPY package.json package-lock.json* ./
|
|
22
|
+
RUN npm install --no-audit --no-fund
|
|
23
|
+
|
|
24
|
+
# App source. node_modules and local state are excluded via .dockerignore.
|
|
25
|
+
COPY . .
|
|
26
|
+
|
|
27
|
+
# Generate the Prisma client at build time (every scaffold ships a
|
|
28
|
+
# prisma/schema.prisma). If you remove Prisma from the app, delete this line.
|
|
29
|
+
RUN npx prisma generate
|
|
30
|
+
|
|
31
|
+
ENV NODE_ENV=production
|
|
32
|
+
# webjs start reads $PORT (default 8080). compose / uncloud / Railway set it.
|
|
33
|
+
ENV PORT=8080
|
|
34
|
+
EXPOSE 8080
|
|
35
|
+
|
|
36
|
+
# Platform-neutral readiness gate. webjs answers /__webjs/ready with 503 until
|
|
37
|
+
# the instance is fully warm (analysis + first vendor attempt), then 200. This
|
|
38
|
+
# HEALTHCHECK is honoured by Docker, compose, and most Docker-based platforms,
|
|
39
|
+
# so the gate works the same everywhere instead of needing a per-platform file.
|
|
40
|
+
# The probe is dependency-free (Node 24's built-in fetch, no curl/wget). For
|
|
41
|
+
# platforms that read their own config, point the equivalent knob at the same
|
|
42
|
+
# path (Railway healthcheckPath, Render healthCheckPath, Fly [checks], k8s
|
|
43
|
+
# readinessProbe); see AGENTS.md "Health and readiness probes".
|
|
44
|
+
HEALTHCHECK --interval=15s --timeout=3s --start-period=40s --retries=5 \
|
|
45
|
+
CMD ["node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/__webjs/ready').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
|
|
46
|
+
|
|
47
|
+
# `npm start` runs `prestart: prisma migrate deploy` (idempotent, a no-op when
|
|
48
|
+
# there are no migrations yet) and then `webjs start`, which serves on $PORT.
|
|
49
|
+
CMD ["npm", "start"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# {{APP_NAME}} local parity with production, using the same Dockerfile, one service.
|
|
2
|
+
#
|
|
3
|
+
# docker compose up --build → http://localhost:8080
|
|
4
|
+
#
|
|
5
|
+
# In production the webdeploy tool (ubicloud + uncloud) provisions a managed
|
|
6
|
+
# Postgres and injects DATABASE_URL + AUTH_SECRET for you. Locally this uses
|
|
7
|
+
# the scaffold's SQLite file on a named volume so data survives `compose down`.
|
|
8
|
+
services:
|
|
9
|
+
app:
|
|
10
|
+
build: .
|
|
11
|
+
ports:
|
|
12
|
+
- "8080:8080"
|
|
13
|
+
environment:
|
|
14
|
+
PORT: 8080
|
|
15
|
+
# SQLite on a volume for local dev. For production, point DATABASE_URL at
|
|
16
|
+
# your managed Postgres and switch prisma/schema.prisma's provider to
|
|
17
|
+
# "postgresql".
|
|
18
|
+
DATABASE_URL: file:/data/dev.db
|
|
19
|
+
# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
20
|
+
AUTH_SECRET: ${AUTH_SECRET:-change-me-please-at-least-32-characters!}
|
|
21
|
+
# Uncomment to back cache / sessions / rate-limiting / pub-sub with Redis
|
|
22
|
+
# instead of the in-memory defaults.
|
|
23
|
+
# REDIS_URL: redis://redis:6379
|
|
24
|
+
volumes:
|
|
25
|
+
- app-data:/data
|
|
26
|
+
# Readiness gate: hold the service "starting" until /__webjs/ready returns
|
|
27
|
+
# 200 (fully warm). Same probe as the Dockerfile HEALTHCHECK; dependency-free.
|
|
28
|
+
healthcheck:
|
|
29
|
+
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/__webjs/ready').then(r=>process.exit(r.ok?0:1),()=>process.exit(1))"]
|
|
30
|
+
interval: 15s
|
|
31
|
+
timeout: 3s
|
|
32
|
+
start_period: 40s
|
|
33
|
+
retries: 5
|
|
34
|
+
|
|
35
|
+
volumes:
|
|
36
|
+
app-data:
|