- Add flake.nix + flake.lock pinning nixpkgs-unstable - Add nix/shell.nix with PostgreSQL, Rust, Elixir LSP, pre-commit hooks - Add shell.nix flake-compat shim for non-flake nix-shell users - Bump Dockerfile to Elixir 1.20.2 / OTP 29.0.3 - Add nix.md with setup and troubleshooting docs - Gitignore Nix runtime state and generated pre-commit config Co-Authored-By: Claude <noreply@anthropic.com>
155 lines
5 KiB
Markdown
155 lines
5 KiB
Markdown
# Nix + direnv Setup
|
|
|
|
This project uses [Nix flakes](https://nixos.wiki/wiki/Flakes) to provide a reproducible dev environment that activates automatically when you `cd` into the project directory. No more "works on my machine" — every dev gets the same Elixir, Erlang, Rust, and PostgreSQL.
|
|
|
|
## How It Works
|
|
|
|
1. **Nix flakes** (`flake.nix`) declare every tool and library the project needs, pinned via `flake.lock`.
|
|
2. **direnv** watches for `.envrc` and loads/unloads the environment as you enter/leave the directory.
|
|
3. **nix-direnv** bridges the two, so `use flake` in `.envrc` triggers `nix develop`.
|
|
|
|
The result: `cd` into the repo and you're ready to run `mix phx.server`. `cd` out and your shell is clean. No global installs, no version conflicts, no manual setup.
|
|
|
|
## File Layout
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `flake.nix` | Top-level flake: pins nixpkgs, Erlang/Elixir versions, defines dev shell |
|
|
| `flake.lock` | Locked hashes for every dependency — reproducible across machines |
|
|
| `nix/shell.nix` | Dev shell definition: packages, env vars, PostgreSQL service scripts, pre-commit hooks |
|
|
| `shell.nix` | Flake-compat shim so `nix-shell` works without `--extra-experimental-features flakes` |
|
|
| `.envrc` | Contains `use flake` (gitignored — create it yourself on each machine) |
|
|
|
|
## One-Time Setup
|
|
|
|
### 1. Install Nix (multi-user)
|
|
|
|
```bash
|
|
sh <(curl -L https://nixos.org/nix/install)
|
|
```
|
|
|
|
Follow the prompts. The multi-user install (`--daemon`) is recommended on macOS. When it finishes, open a new terminal so the Nix daemon is running.
|
|
|
|
Enable flakes. Add this to `~/.config/nix/nix.conf` (create the file if needed):
|
|
```
|
|
experimental-features = nix-command flakes
|
|
```
|
|
|
|
Verify:
|
|
```bash
|
|
nix --version
|
|
nix flake --help
|
|
```
|
|
|
|
### 2. Install direnv
|
|
|
|
```bash
|
|
brew install direnv
|
|
```
|
|
|
|
Add the direnv hook to your shell's rc file. Running `brew info direnv` will print the exact line — pick the one for your shell:
|
|
|
|
**fish** (`~/.config/fish/config.fish`):
|
|
```fish
|
|
direnv hook fish | source
|
|
```
|
|
|
|
**zsh** (`~/.zshrc`):
|
|
```zsh
|
|
eval "$(direnv hook zsh)"
|
|
```
|
|
|
|
### 3. Install nix-direnv
|
|
|
|
```bash
|
|
brew install nix-direnv
|
|
```
|
|
|
|
Then configure direnv to load it. Create (or append to) `~/.config/direnv/direnvrc`:
|
|
```bash
|
|
mkdir -p ~/.config/direnv
|
|
cat > ~/.config/direnv/direnvrc << 'EOF'
|
|
source "$(brew --prefix nix-direnv)/share/nix-direnv/direnvrc"
|
|
EOF
|
|
```
|
|
|
|
If you installed nix-direnv via `nix profile` instead of Homebrew, the source line is:
|
|
```
|
|
source $HOME/.nix-profile/share/nix-direnv/direnvrc
|
|
```
|
|
|
|
### 4. Create `.envrc`
|
|
|
|
```bash
|
|
echo 'use flake' > .envrc
|
|
```
|
|
|
|
This is gitignored — each dev creates their own. You can add personal overrides (e.g., extra env vars) here.
|
|
|
|
### 5. Restart your shell
|
|
|
|
Open a new terminal tab or `exec $SHELL` so the direnv hook takes effect.
|
|
|
|
### 6. First time entering the project directory
|
|
|
|
```bash
|
|
cd ~/dev/prop
|
|
direnv allow # one-time approval — thereafter it loads automatically
|
|
```
|
|
|
|
The first build may take several minutes while Nix downloads and compiles packages (Erlang, Elixir, Rust, PostgreSQL, etc.). Subsequent entries are instant — direnv caches the shell.
|
|
|
|
## Daily Use
|
|
|
|
Once set up, just `cd` into the repo. The shell activates automatically:
|
|
|
|
- **PostgreSQL** starts on port 5432 with `prop_dev` and `prop_test` databases created
|
|
- **`start-services`** / **`stop-services`** commands are available to manually control PostgreSQL
|
|
- **Mix/Hex** are sandboxed to `.nix-mix` / `.nix-hex` (doesn't pollute `~/.mix`)
|
|
- **Pre-commit hooks** (mix format, credo, nixfmt, cargo fmt, cargo clippy) install automatically
|
|
- **Services stop** automatically when you `cd` out of the directory
|
|
|
|
## Troubleshooting
|
|
|
|
**`direnv: error .envrc is blocked`**
|
|
Run `direnv allow` in the project directory.
|
|
|
|
**`nix develop` fails with "experimental Nix feature 'nix-command' is disabled"**
|
|
Add `experimental-features = nix-command flakes` to `~/.config/nix/nix.conf`.
|
|
|
|
**Empty shell, no Elixir available**
|
|
Run `nix develop --command elixir --version` to see build output. Check for syntax errors in `flake.nix` or `nix/shell.nix`:
|
|
```bash
|
|
nix flake check
|
|
```
|
|
|
|
**Flake can't see new or modified files**
|
|
Nix flakes only see files tracked by git. If you added a new nix file, stage it first:
|
|
```bash
|
|
git add -N <file>
|
|
```
|
|
|
|
**Nix build fails with a hash mismatch in `shell.nix` (flake-compat)**
|
|
The `flake-compat` tarball hash in `shell.nix` is stale. Update it to the actual hash shown in the error message.
|
|
|
|
**direnv not triggering on `cd`**
|
|
Check your shell's rc file has the hook line from step 2. Run `direnv status` in the project directory.
|
|
|
|
**I added a new package to `nix/shell.nix` — how do I reload?**
|
|
direnv watches `flake.nix` and `flake.lock` for changes and rebuilds automatically. If it doesn't pick up the change, run `direnv reload`.
|
|
|
|
## Updating Dependencies
|
|
|
|
The flake pins everything via `flake.lock`. To update all inputs to their latest versions:
|
|
|
|
```bash
|
|
nix flake update
|
|
```
|
|
|
|
To update just nixpkgs (e.g., to get a newer Erlang/Elixir):
|
|
|
|
```bash
|
|
nix flake lock --update-input nixpkgs
|
|
```
|
|
|
|
Commit the updated `flake.lock` so everyone gets the same versions.
|