一键导入
docker-test
Build and smoke-test the Docker images with docker compose. Use when touching a Dockerfile, the bundle build, or compose config.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Build and smoke-test the Docker images with docker compose. Use when touching a Dockerfile, the bundle build, or compose config.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Add a new shared workspace package under packages/*. Use when creating a new @packages/<name>, whether a bundled library other workspaces import or a build-only script package that runs during a build.
Orient in this repo: which file to edit for a change, how a change ripples across the stack, and how to search the code. Use at the start of a task in an unfamiliar area, or before a cross-cutting change.
Prefer Bun-native APIs, else Node built-ins with the node: prefix. Use when importing a Node built-in (fs, path, child_process, crypto, os, util), reading or writing files, spawning a process, or choosing between a Bun and a Node API.
Icebox a raised-but-undecided concern instead of forcing a plan-or-dismiss call: record it with no verdict so the context survives. Use when a review, PR, audit, or eval surfaces something real-maybe that should not be scheduled or closed yet, or when the user says to icebox or park an item.
Add a typed Hono API endpoint or WebSocket route: router, OpenAPI docs, validation envelope, and RPC client wiring. Use when adding or modifying routes in api/hono.
Start, restart, and verify the ZeroStarter dev stack. `bun run dev` serves portless named `.localhost` URLs (branch-prefixed in a worktree); resolve them with `bunx portless get`. Use when asked to run the app, when the API returns NOT_FOUND for routes that exist in source, or before browser testing.
| name | docker-test |
| description | Build and smoke-test the Docker images with docker compose. Use when touching a Dockerfile, the bundle build, or compose config. |
Full stack, same Dockerfiles prod ships:
docker compose build --no-cache && docker compose up
Scope to one service while iterating:
docker compose build --no-cache api && docker compose up -d api
curl -sf --retry 30 --retry-delay 1 --retry-connrefused http://localhost:4000/api/health
docker compose logs -f api
docker compose down
Where it comes from. The build reads .env as a BuildKit secret (compose wires secrets: dotenv from ./.env; a plain build passes --secret id=dotenv,src=.env) and validates it in full; up loads it via env_file: .env. The secret mounts only during the build RUN, never lands in a layer. Missing .env fails fast: compose reports "secret file not found", docker build "secret not provided". Invalid runtime env crashes the container loudly at boot.
Smoke build vs deploy image. A clean checkout has no real .env; give a smoke build a dummy from .env.example with the required blanks filled:
sed -e 's|^BETTER_AUTH_SECRET=$|BETTER_AUTH_SECRET=dummy|' \
-e 's|^GITHUB_CLIENT_ID=$|GITHUB_CLIENT_ID=dummy|' \
-e 's|^GITHUB_CLIENT_SECRET=$|GITHUB_CLIENT_SECRET=dummy|' \
-e 's|^GOOGLE_CLIENT_ID=$|GOOGLE_CLIENT_ID=dummy|' \
-e 's|^GOOGLE_CLIENT_SECRET=$|GOOGLE_CLIENT_SECRET=dummy|' \
-e 's|^POSTGRES_URL=$|POSTGRES_URL=postgres://dummy:dummy@localhost:5432/dummy|' \
.env.example > .env
Empty optionals (e.g. NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN=) pass via emptyStringAsUndefined. A smoke build is disposable: Next inlines NEXT_PUBLIC_* into the bundle at build, so a dummy-built web image carries dummy URLs forever. Deploy images build with the real .env.
--no-cache after any .env edit. Secret contents are not part of BuildKit's cache key, so a plain rebuild reuses the cached build RUN and silently ships stale baked values (web's NEXT_PUBLIC_*).
Skip SKIP_ENV_VALIDATION. Build and runtime both load a real .env, so both validate real values. The flag no longer bypasses validation anyway: it only substitutes shape-valid dummies for missing required vars while zod defaults and transforms still run (HONO_PORT defaults to 4000, HONO_TRUSTED_ORIGINS parses to an array). Reserve it for CI, which genuinely lacks a .env. The web deploy uses the narrower SKIP_ENV_VALIDATION_SERVER, which dummies only server secrets while still validating the NEXT_PUBLIC_* it inlines.
Database. Without a reachable POSTGRES_URL, DB-backed routes (e.g. /api/v1/user) return 500 while /api/health stays green; full e2e needs a real database URL.
Direct docker run --env-file does not strip inline comments: HONO_RATE_LIMIT=60 # note arrives as "60 # note", coerces to NaN, and validation rejects it. Compose's parser strips them; for docker run, sanitize first: sed 's/ #.*//' .env > .env.docker.
Ports 4000 and 3000 collide with a running dev stack; for side-by-side testing bump the compose mappings (e.g. 14000:4000) in a scratch checkout.
The api runner ships only bundle/, so the bundle must resolve every import with no node_modules. When one is missing, Bun auto-installs it from npm at runtime, so a container that "works" online may be downloading packages on every cold start. Prove it offline:
sed 's/ #.*//' .env > .env.docker
docker run -d --name t-offline --network=none --env-file .env.docker <image>
docker exec t-offline sh -c 'for i in $(seq 1 30); do wget -qO- http://localhost:4000/api/health && exit 0; sleep 1; done; exit 1'
docker logs t-offline # on failure: "Cannot find package 'X'" = unresolved import
docker rm -f t-offline
Forensics on an online container: docker diff <name> | grep .bun/install/cache; entries there mean auto-install fired (history: --external hono in the bundle build fetched hono from npm at cold start).
The web build prunes the unused libc stack from standalone (libc auto-detected at build; alpine base = musl). Store-layout or dep-rename drift regresses it back into bloat silently, so assert after any web image build:
docker run --rm --entrypoint sh zerostarter-web -c \
'find /app/node_modules \( -name "*linux-*-gnu*" -o -name "*sharp-linux-*" -o -name "*libvips-linux-*" \) ! -type l | grep . && echo "FAIL: glibc stack shipped" && exit 1; echo OK'
Expect OK. ! -type l skips dangling glibc-named symlinks (expected leftovers); only real files count. Any hit means the excludes in web/next/next.config.ts stopped matching: fix the patterns, never delete the assertion.
open -a Docker, then poll docker info until ready..dockerignore excludes real .env* from the build context (only .env.example enters), so the context itself carries no secret.