Skip to content

chore: add Nix dev shells for backend and frontend - #302

Open
FrameAutomata wants to merge 1 commit into
mainfrom
chore/nix-dev-shells
Open

chore: add Nix dev shells for backend and frontend#302
FrameAutomata wants to merge 1 commit into
mainfrom
chore/nix-dev-shells

Conversation

@FrameAutomata

@FrameAutomata FrameAutomata commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Best merged after #305, which is what lets this PR carry no backend configuration at all. Before #305 the backend panics twice without DB_TYPE and PORTS; after it, only JWT_SECRET is needed and the shells stay out of it entirely.

Why

Only cli/ had a flake. Backend and frontend work relied on ad-hoc nix-shell -p go and whatever node happened to be on PATH.

Shells

Shell Contents
default go + node + shared tooling
backend go + shared, no node
frontend node + shared, no Go toolchain
oxc backend + cargo/rustc for scripts/build-oxc-shim.sh

Split rather than one fat shell because the union closure is ~1.2GB — a frontend-only contributor would otherwise realise ~611MB of Go tooling they never invoke.

Versions are derived, not restated

The Go version is read out of backend/go.mod and the node major out of frontend/package.json, which gains the engines field it was missing:

goVersion = builtins.match ".*\ngo ([0-9]+)\\.([0-9]+)\\.[0-9]+.*" (readFile ./backend/go.mod);
nodeMajor = head (match "([0-9]+).*" frontendPackage.engines.node);

Bumping either manifest moves the dev shell automatically, so they cannot drift. engines.node is also the single declaration the node:22-alpine Dockerfiles can be checked against — a CI assertion for that is a reasonable follow-up, deliberately not included here.

No backend configuration in the shells

Earlier revisions of this PR exported DB_TYPE, PORTS and a literal JWT_SECRET. All three are gone:

  • a committed signing key is identical in every clone
  • exporting DB_TYPE silently shadowed backend/.env, since godotenv.Load calls loadFile(name, false) and never overwrites a set variable
  • it only ever helped Nix users, while Docker/CI/a fresh clone still panicked

#305 fixes it at the source instead. The flake now carries zero backend knowledge, so there is nothing left that can drift from the code.

Other details

The banner is gated on an interactive shell — an unconditional echo in a shellHook lands on stdout ahead of the command in x=$(nix develop --command ...). Versions interpolate at eval time rather than forking go/node on every shell entry and direnv reload.

.gitignore gains .direnv/, .envrc.local (which .envrc sources for local secrets), nix result symlinks anchored to the working directory, and the local databases as two globs whose basename must start with traceway — so a DB created from any directory is covered while tracked files like Dockerfile.duckdb are not.

Verification

All five shells evaluate with distinct derivation names and zero backend-env references. Frontend builds clean under node 22. nixfmt --check passes.

🤖 Generated with Claude Code

Only cli/ had a flake, so backend and frontend work relied on ad-hoc
`nix-shell -p go` and whatever node happened to be on PATH. Adds a root
flake with four shells:

  default   go + node + the shared tooling
  backend   go + shared, no node
  frontend  node + shared, no go toolchain
  oxc       backend + cargo/rustc for scripts/build-oxc-shim.sh

They are split rather than one fat shell because the union closure is
~1.2GB: a frontend-only contributor would otherwise realise ~611MB of Go
tooling they never invoke.

Toolchain versions are derived from the manifests instead of restated,
so they cannot drift: the Go version is read out of backend/go.mod and
the node major out of frontend/package.json, which gains the `engines`
field it was missing. That field is also the single declaration the
node:22-alpine Dockerfiles can be checked against.

The shells deliberately export no backend configuration. The backend
supplies its own startup defaults, so a dev shell that set DB_TYPE/PORTS
would only shadow backend/.env -- godotenv.Load never overwrites an
already-set variable -- and would drift from the code it duplicates.
JWT_SECRET has no default anywhere on purpose; the backend's own error
message is the single source of that instruction.

The banner is gated on an interactive shell and interpolates versions at
eval time: an unconditional echo lands on stdout ahead of the command in
`x=$(nix develop --command ...)`, and probing versions at runtime forks
go/node on every shell entry and direnv reload.

Also ignores .direnv/, .envrc.local (which .envrc sources for local
secrets), nix result symlinks anchored to the working directory, and the
local databases the backend writes -- as two globs whose basename must
start with `traceway`, so tracked files like Dockerfile.duckdb are
unaffected while a DB created from any directory is covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant