feat: insta build — pre-push verification (plan + Dockerfile + static checks)
What
insta build [dir] [--explain] [--port <n>] [--json] — verify a source directory would build before insta deploy pushes it to a remote Fly build. Entirely local and offline: no login, no project link, nothing deployed, no Docker daemon.
Output (human, or --json for agents):
- detection plan — builder (
dockerfile|nixpacks), providers, install/build/start commands, port with the reason it was chosen (--port flag/Dockerfile EXPOSE n/ not detected), env keys from.env.example - the Dockerfile that would be used — the user's, or nixpacks-generated (
nixpacks build --outinto a temp dir; the source tree stays clean).--explainincludes its content - static checks, each with a
nextAction: Dockerfile present, start command (CMD/ENTRYPOINT or nixpacks start), port detected (the #1 deploy mistake per deploy.ts), build context (node_modulesnot dockerignored / >100MB) - verdict:
deployable|needs-attention|failed(exit 1)
Why
Today the first signal that a deploy can't work arrives after the remote build — deploy.ts hard-fails without a Dockerfile and a port mismatch surfaces as a dead app. Agents especially need a cheap, structured pre-push probe (insta build --explain --json).
How
src/nixpacks.ts— nixpacks glue, same injectable-runner pattern asflyctl-build.ts; capture-only runner with a 30s wedge guard;ensureNixpacks()mirrorsensureFlyctl()(brew/curl best-effort, degrades to Dockerfile-only when unavailable). Handles real nixpacks ≥1.x plan output (providersempty, names inNIXPACKS_METADATA)src/commands/build.ts— pure, unit-tested report assembly (buildReport/computeVerdict/inferPort/renderReport); reusesdockerfileExposedPortfrom deploy- Registered beside
deployinindex.ts; SKILL.md architecture table updated
Testing
npm run typecheckclean;npm test333/333 (22 new tests, TDD)- Live smoke with nixpacks 1.41.0: node app w/o Dockerfile → nixpacks plan + generated Dockerfile, verdict deployable; Dockerfile w/o CMD → needs-attention; undetectable dir → failed, exit 1
Follow-ups (not in this PR)
- Phase 2:
--check— localdocker build+ run + port probe (skips cleanly without a daemon) - Mirror row in
skills/insta/cli-reference.mdgoes in a parallel insta-skills PR
🤖 Generated with Claude Code
Summary by cubic
Adds insta build [dir] to verify a source directory will build before insta deploy. Previously failures surfaced after a remote Fly build; now you get a local, offline plan, optional generated Dockerfile, static checks, and a verdict that can block deploys.
- Registers
buildwith--explain,--port <n>, and--json. Exits 1 on a failed verdict. No login, no Docker, never pushes; rejects non-directory args. Validates--portto 1..65535 and rejects an explicit empty value. - Assembles a plan: builder (
dockerfileornixpacks), providers, install/build/start commands, port with rationale, and.env.examplekeys.--explainincludes the Dockerfile content. - Integrates
nixpackswithout auto-install: quiet--versionprobe only; if unavailable, degrades to Dockerfile-only checks. Parses realnixpacks planJSON (includingNIXPACKS_METADATA) and generates a Dockerfile vianixpacks build --outinto a temp dir. - Static checks with next actions: Dockerfile presence (critical), start command (CMD/ENTRYPOINT vs
nixpacksstart; missing is critical fornixpacks), port detection (flag wins, else EXPOSE), and build context size (ignores dockerignorednode_modules; warns on large or truncated scans; truncation message counts entries). - Keeps
--jsonoutput pure; Dockerfile content is omitted unless--explain. Adds unit tests for report assembly, JSON rendering,nixpacksglue, and context scanning.