# 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.
