feat: insta build — pre-push verification (plan + Dockerfile + static checks)

Merged
JW
3mo ago
+684-1

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 --out into a temp dir; the source tree stays clean). --explain includes 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_modules not 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 as flyctl-build.ts; capture-only runner with a 30s wedge guard; ensureNixpacks() mirrors ensureFlyctl() (brew/curl best-effort, degrades to Dockerfile-only when unavailable). Handles real nixpacks ≥1.x plan output (providers empty, names in NIXPACKS_METADATA)
  • src/commands/build.ts — pure, unit-tested report assembly (buildReport/computeVerdict/inferPort/renderReport); reuses dockerfileExposedPort from deploy
  • Registered beside deploy in index.ts; SKILL.md architecture table updated

Testing

  • npm run typecheck clean; npm test 333/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 — local docker build + run + port probe (skips cleanly without a daemon)
  • Mirror row in skills/insta/cli-reference.md goes 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 build with --explain, --port <n>, and --json. Exits 1 on a failed verdict. No login, no Docker, never pushes; rejects non-directory args. Validates --port to 1..65535 and rejects an explicit empty value.
  • Assembles a plan: builder (dockerfile or nixpacks), providers, install/build/start commands, port with rationale, and .env.example keys. --explain includes the Dockerfile content.
  • Integrates nixpacks without auto-install: quiet --version probe only; if unavailable, degrades to Dockerfile-only checks. Parses real nixpacks plan JSON (including NIXPACKS_METADATA) and generates a Dockerfile via nixpacks build --out into a temp dir.
  • Static checks with next actions: Dockerfile presence (critical), start command (CMD/ENTRYPOINT vs nixpacks start; missing is critical for nixpacks), port detection (flag wins, else EXPOSE), and build context size (ignores dockerignored node_modules; warns on large or truncated scans; truncation message counts entries).
  • Keeps --json output pure; Dockerfile content is omitted unless --explain. Adds unit tests for report assembly, JSON rendering, nixpacks glue, and context scanning.