The fluffy-chainsaw CLI
Two loops cover almost everything:
# local dev
fluffy-chainsaw local -out .env # start deps (Postgres, emulators, buckets) + write .env
node --env-file=.env src/index.ts
fluffy-chainsaw local-prune # tear the containers down
# deploy
fluffy-chainsaw deploy -project <id> -region <region> # build -> scan -> provision [-> migrate] [-> firebase]
Everything else is the deploy loop broken into inspectable steps.
Commands
| Command | What it does | When you run it |
|---|---|---|
fluffy-chainsaw local |
Scans code + yaml, starts containerized substitutes (Postgres, Pub/Sub emulator, Firebase Auth/RTDB emulators, filesystem buckets) plus every runner in dev mode: the repo is mounted at /repo and the runner runs node --watch, so code edits restart it in-place — the image rebuild only matters for deploy. Runner images must set WORKDIR under /repo mirroring the repo layout and an exec-form CMD ["node", "<entry>"]. Writes every descriptor to .env. --deps-only starts deps without runners. |
Every local dev session. |
fluffy-chainsaw local-prune |
Removes the local containers/volumes for this project. | Cleanup. |
fluffy-chainsaw deploy |
One shot: build → scan -target pulumi-go (refreshes the program) → provision → migrate (when a database declares migrations) → firebase (only when the app uses auth/Hosting). --stack <name> picks the deployment stack: per-stack values resolve to that stack's branch, and with a per-stack deploy.project the target project follows automatically. A stack marked deploy.pinned skips build and deploys the digests in releases/<stack>.images.json. |
The normal deploy. |
fluffy-chainsaw release |
Promote the source stack's built images into the target stack's registry by digest (--to prod, --from defaults to the default stack), verify the copy, and write releases/<stack>.images.json — commit that file; it is what a pinned stack deploys. Prod never rebuilds: a rebuild would ship an artifact nobody tested. |
Cutting a release to a pinned stack. |
fluffy-chainsaw scan (alias emit) |
Scan markers + merge fluffy-chainsaw.yaml → the desired state / provisioning program. -target pulumi-go generates a standalone Pulumi Go program; -target json emits tfvars for a Terraform root you own. |
To inspect what will be provisioned, or when driving the steps manually. |
fluffy-chainsaw eject |
Write the generated Pulumi program into a directory you keep. | When you want to own/inspect/version the program. |
fluffy-chainsaw build |
Run each runner's make target; capture the image ref (the target's last stdout line). No-op if there are no runners. | Before provisioning, or via deploy. |
fluffy-chainsaw provision |
pulumi up on the generated program (-stack to pick the stack), owning every prerequisite deterministically: generates the program if missing, runs go mod tidy, picks the state backend (PULUMI_BACKEND_URL › gs://fluffy-chainsaw-state-<project>, created on first use › file://~/.fluffy-chainsaw/state), defaults the passphrase to the documented dummy, creates the stack, seeds gcp:project/gcp:region/fluffy-chainsaw:deployerPrincipal (explicitly-set config always wins), and retries the apply while freshly-enabled APIs propagate. |
Apply infrastructure, or via deploy. |
fluffy-chainsaw migrate |
Run each migrated database's migrations.run command as that database's migrator identity, then grant DML to its runners. --local targets the fluffy-chainsaw local postgres. See migrations.md. |
After adding a migration, or via deploy/local. |
fluffy-chainsaw wire |
Read stack outputs (-from pulumi or -from terraform), slice descriptors to one runner's uses, write KEY=VALUE (--runner <name> -out .env). |
Reproduce a deployed runner's env for local runs, CI, or debugging. A deployed runner already has this env on its service. |
fluffy-chainsaw adopt (alias import) |
Import resources you own that already exist in the project into state, skipping existing: true externals. |
First deploy into a project where your resources already live — run it between scan and provision so they're reconciled, not collided with. |
fluffy-chainsaw firebase |
Generate (or merge into) firebase.json the Hosting rewrites that put the frontend and the /auth/** + /api/** runner on one origin. Merges — it won't clobber your other rewrites. Writes the app's own hosting.site when declared (default: the project's default site). |
Apps using auth/Hosting; run via deploy. |
fluffy-chainsaw workspace deploy |
Deploy every child in the workspace manifest (fluffy-chainsaw.workspace.yaml: a defaults: block plus children: [{dir: <app dir>, select: <name>}, ...]) in order, each exactly like cd <child> && fluffy-chainsaw deploy. Before any deploy, a fail-closed pre-pass rejects two children owning the same physical resource (E_WORKSPACE_DOUBLE_OWNERSHIP). -only child1,child2 deploys a subset (the ownership check still covers the whole manifest). |
One repo, several independently-deployable apps. |
Common flags
-dir <path>— the app directory (default.);fluffy-chainsaw.yamlis found at<dir>/fluffy-chainsaw.yamlunless-configoverrides it.--select <name>— target the infra root atinfra/<name>(shorthand for the per-command-out/-dirplumbing) when one repo holds several infra roots.--stack <name>— the deployment stack, on every command that reads config (deploy,scan,build,migrate,firebase,adopt). Omitted, it falls back todeploy.stackfromfluffy-chainsaw.yaml, thendev— the same chain everywhere, so two commands never disagree about the active stack. With a per-stackdeploy.project,--projectalone selects the stack bound to that project, and a contradictory--stack/--projectpair is refused.fluffy-chainsaw localalways uses the default stack.scan -target pulumi-go -out <dir>creates aninfra/subpackage inside<dir>plus scaffoldmain.go,go.mod, andPulumi.yamlwhen absent. Point-outat the Pulumi project root, not at a directory already calledinfra/— otherwise you getinfra/infra/.
Run fluffy-chainsaw <command> -h for the full flag list of any command.
The manual deploy pipeline
When you want to see every step:
fluffy-chainsaw scan -target pulumi-go # what must exist
fluffy-chainsaw build # images for every runner
fluffy-chainsaw adopt # only if resources already exist in the project
fluffy-chainsaw provision # pulumi up
fluffy-chainsaw wire --runner orders-api -out .env # reproduce a runner's env if needed