diff options
| author | 2026-05-20 13:56:09 +0100 | |
|---|---|---|
| committer | 2026-05-20 13:56:09 +0100 | |
| commit | de5146c7976e1fb38e8d1f82c30544462d881100 (patch) | |
| tree | 2de6f2358d6b83b2f64b68fe105ed11d4ff0feea | |
| parent | 52e53ad7956f637af3bb87de79934bfda4b74a2e (diff) | |
| download | dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.tar.gz dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.tar.bz2 dotfiles-de5146c7976e1fb38e8d1f82c30544462d881100.zip | |
refactor(nix): promote remote-dev/ to nix/ with common/vm/host split
Restructures the Home-Manager profile to support both the Arch host and
the Ubuntu remote-dev VM from the same flake.
- remote-dev/ → nix/ (hard rename; .chezmoiignore updated)
- home.nix split into common.nix (shared), vm.nix (Mason runtime
carve-outs + podman stack), host.nix (gpg scdaemon delegation to
system pcscd)
- flake.nix exposes homeConfigurations.{vm,host} via a mkProfile
helper
- rj alias in dot_zshrc updated to ~/.local/share/dotfiles/nix
- bootstrap.sh / justfile updated to use #vm against the new path
The split is behaviour-preserving for the VM: vm.nix + common.nix
together carry the same package set as the previous home.nix.
host.nix is provisioned but not yet wired into bootstrap (phase p8).
Phase 1 of the nix-on-host migration plan.
| -rw-r--r-- | .chezmoiignore | 2 | ||||
| -rw-r--r-- | dot_config/zsh/dot_zshrc | 2 | ||||
| -rw-r--r-- | nix/README.md (renamed from remote-dev/README.md) | 111 | ||||
| -rwxr-xr-x | nix/bootstrap.sh (renamed from remote-dev/bootstrap.sh) | 6 | ||||
| -rw-r--r-- | nix/common.nix | 160 | ||||
| -rw-r--r-- | nix/flake.lock (renamed from remote-dev/flake.lock) | 0 | ||||
| -rw-r--r-- | nix/flake.nix (renamed from remote-dev/flake.nix) | 26 | ||||
| -rw-r--r-- | nix/host.nix | 21 | ||||
| -rw-r--r-- | nix/justfile (renamed from remote-dev/justfile) | 2 | ||||
| -rw-r--r-- | nix/vm.nix | 71 | ||||
| -rw-r--r-- | remote-dev/home.nix | 223 |
11 files changed, 325 insertions, 299 deletions
diff --git a/.chezmoiignore b/.chezmoiignore index acca5f0..34be6e8 100644 --- a/.chezmoiignore +++ b/.chezmoiignore @@ -6,7 +6,7 @@ systemd-units/ etc/ firefox/ thunderbird/ -remote-dev/ +nix/ justfile just-lib.sh selene.toml diff --git a/dot_config/zsh/dot_zshrc b/dot_config/zsh/dot_zshrc index 138982b..b227c8f 100644 --- a/dot_config/zsh/dot_zshrc +++ b/dot_config/zsh/dot_zshrc @@ -358,7 +358,7 @@ reload-env() { # Just alias j='just' alias dj='just --justfile ~/dotfiles/justfile --working-directory ~/dotfiles' -alias rj='just --justfile ~/.local/share/dotfiles/remote-dev/justfile --working-directory ~/.local/share/dotfiles/remote-dev' +alias rj='just --justfile ~/.local/share/dotfiles/nix/justfile --working-directory ~/.local/share/dotfiles/nix' # LLVM / Clang tooling alias ncmake='cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_FLAGS="$DEV_CFLAGS" -DCMAKE_CXX_FLAGS="$DEV_CFLAGS" -DCMAKE_INSTALL_PREFIX=build/install -DCMAKE_BUILD_TYPE=Debug -DBUILD_SHARED_LIBS=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build' diff --git a/remote-dev/README.md b/nix/README.md index c6519a5..2bf3383 100644 --- a/remote-dev/README.md +++ b/nix/README.md @@ -1,26 +1,31 @@ -# remote-dev +# nix -Headless dev environment for an Ubuntu 22.04 VM I SSH into. Deployed with -Nix + Home-Manager. Shares the host's neovim, zellij, and zsh configs from -the same repo — no duplication. +Home-Manager profiles for the Arch host (`host.nix`) and the Ubuntu +remote-dev VM (`vm.nix`), both layered on top of `common.nix`. Shares +the same nvim, zellij, and zsh configs from the same repo — no +duplication across machines. ## Bootstrap -On a fresh VM, as the dev user (must have sudo): +**Host (Arch)**: managed by the top-level `bootstrap.sh` in the repo +root (installs nix + runs `home-manager switch --flake .../nix#host` +as part of `just init`). + +**VM (Ubuntu)**: as the dev user (must have sudo): ```sh -curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/remote-dev/bootstrap.sh | sh +curl -fsSL https://raw.githubusercontent.com/sommerfelddev/dotfiles/master/nix/bootstrap.sh | sh ``` Then log out and back in. Run `nvim` once to let it fetch plugins from GitHub on first launch. -## What it does +## What the VM bootstrap does 1. Installs Nix (Determinate Systems multi-user installer). 2. Clones this repo to `~/.local/share/dotfiles`. -3. Runs `home-manager switch --flake .../remote-dev#vm`, which: - - Installs the CLI tool subset (see `home.nix`). +3. Runs `home-manager switch --flake .../nix#vm`, which: + - Installs the CLI tool subset (see `common.nix` + `vm.nix`). - Symlinks `~/.config/{nvim,zellij,zsh,direnv,ghostty,git}` and `~/.ssh/config` at the cloned working tree via `mkOutOfStoreSymlink`, so `git pull` is enough to pick up config @@ -30,7 +35,7 @@ GitHub on first launch. ## Updating after a dotfiles change -Run from `~/.local/share/dotfiles/remote-dev`: +Run from `~/.local/share/dotfiles/nix` (host or VM): ```sh just update # pull + home-manager switch (handles everything) @@ -46,36 +51,41 @@ just switch # rebuild home-manager from the current checkout > `just update` runs `pull` then `switch`. The home-manager invocation > uses `--impure --flake '.#vm' -b backup`; the single-quotes around the > flake ref matter because our zsh enables `extendedglob`, which would -> otherwise interpret `.#vm` as a glob pattern. +> otherwise interpret `.#vm` as a glob pattern. On the host, swap +> `#vm` → `#host`. ## Adding a tool -Edit `home.nix`, add to `home.packages`, then `just switch` (or `just update`). +Edit `common.nix` (shared) or the profile-specific file (`host.nix` / +`vm.nix`), add to `home.packages`, then `just switch` (or `just +update`). ## Single-shell policy (leaf tools only) -Nix on this VM carries **leaf CLI tools** plus **editor/AI-agent +The nix profile carries **leaf CLI tools** plus **editor/AI-agent runtimes**, and nothing else. Specifically forbidden in `home.packages` -because they would shadow Ubuntu's via `PATH` and silently break builds -against the system sysroot/libc/CI: `cc`, `c++`, `gcc`, `g++`, `clang`, -`clang++`, `ld`, `make`, `cmake`, `ninja`, `meson`, `pkg-config`, -`autoconf`, `automake`, `python`, `python3`, `pip`, `cargo`, `rustc`, -`go`. The system `python3` (`/usr/bin/python3`) stays the default +because they would shadow the system toolchain via `PATH` and silently +break builds against the system sysroot/libc/CI: `cc`, `c++`, `gcc`, +`g++`, `clang`, `clang++`, `ld`, `make`, `cmake`, `ninja`, `meson`, +`pkg-config`, `autoconf`, `automake`, `python`, `python3`, `pip`, +`cargo`, `rustc`, `go`. The system `python3` stays the default interpreter for project builds. -Explicit carve-outs (used only by Mason/editor/AI agents, never by the +Explicit carve-outs (used only by editor/AI agents, never by the project build): -- `nodejs` — `node`/`npm`/`npx` for npm-based LSPs. -- `uv` — `uv`/`uvx` for Python LSPs in isolated venvs. `uv` does NOT - install a `python3` in PATH; it manages its own interpreters under - `~/.local/share/uv/`. System `python3` is untouched. +- `nodejs` — `node`/`npm`/`npx` for npm-based LSPs and + copilot-language-server. +- `uv` — `uv`/`uvx` for ad-hoc Python tooling in isolated venvs. `uv` + does NOT install a `python3` in PATH; it manages its own + interpreters under `~/.local/share/uv/`. System `python3` is + untouched. - `clang-tools` — `clang-format`, `clang-tidy`, `clangd` only (no compiler driver). If a project needs a newer build toolchain, drop a `flake.nix` + `.envrc` in that project tree (direnv + nix-direnv is already wired -up). Don't add it to `home.nix`. +up). Don't add it to `common.nix`/`host.nix`/`vm.nix`. ## Commit signing on the VM (SSH-format, no GPG secrets) @@ -127,37 +137,18 @@ git log --show-signature -1 ## Caveats - **GPG / pass**: HM installs `gnupg` and `pass` but does _not_ import - any private key. Don't try; use SSH-format signing via the forwarded - agent instead (see above). -- **Disk usage**: Nix store + nvim plugins consumes ~3-5 GB. Check the - VM's root partition size first. + any private key. On the VM, use SSH-format signing via the forwarded + agent instead (see above). On the host, smartcard access via + `pcscd` is configured in `host.nix` (`~/.gnupg/scdaemon.conf`). +- **Disk usage**: Nix store + nvim plugins consumes ~3-5 GB. Check + partition size first on the VM. - **Network for first nvim launch**: `vim.pack.add` fetches plugins - from GitHub on first start. Mason will also fetch LSP servers using - `nodejs`/`uv` from this profile. -- **Mason pip installs need a managed `python3.11`**: a handful of Mason - packages (autotools-language-server, codespell, mdformat, - nginx-language-server, systemdlint, yamllint) install themselves into - per-pkg venvs via pip. Ubuntu 20.04's `/usr/bin/python3` is 3.8 — too - old. `bootstrap.sh` runs `uv python install 3.11` (uv is in the nix - profile) and symlinks the resulting binary to - `~/.local/bin/python3.11`. The versioned name leaves - `/usr/bin/python3` untouched. On an existing VM run - `uv python install 3.11 && ln -sf "$(uv python find 3.11)" ~/.local/bin/python3.11` - once, then `:MasonToolsInstall` (or `:MasonInstallAll`) in nvim. -- **`basedpyright` is provided by nix, not Mason**: its pypi distro - pulls `nodejs-wheel-binaries`, which ships only `manylinux_2_28` - Linux wheels. Neither Nix's python nor uv's standalone - (`manylinux2014`-tagged) accepts those, so pip falls back to - compiling Node 24 from source — which fails on Ubuntu 20.04's - gcc 9.4 (needs gcc ≥10 for `-std=gnu++20`). `home.nix` adds - `pkgs.basedpyright`; the matching AUR package (`basedpyright-bin`) - is in `meta/base.txt` for Arch. `mason-tool-installer` no longer tries - to install it (see `dot_config/nvim/lua/plugins/lsp.lua`). + from GitHub on first start. - **Ubuntu apt collisions**: Nix-installed binaries appear first in PATH. The leaf-tools policy above exists precisely to keep this shadowing contained to harmless tools. -## Podman (rootless) +## Podman (rootless, VM only) Nix can't manage setuid helpers, `/etc/subuid`/`/etc/subgid`, or kernel cmdline. Do this once on the VM as root: @@ -185,26 +176,26 @@ podman info | grep -E 'cgroupVersion|graphDriverName|networkBackend' # expected: graphDriverName: overlay, networkBackend: netavark # cgroupVersion: v1 is fine — only blocks --memory/--cpus flags. The # podman v5 deprecation warning is silenced by PODMAN_IGNORE_CGROUPSV1_WARNING, -# set in home.nix. +# set in vm.nix. podman run --rm docker.io/library/alpine echo hi ``` -The home-manager profile already installs `podman`, `crun`, `conmon`, +The VM home-manager profile installs `podman`, `crun`, `conmon`, `netavark`, `aardvark-dns`, `slirp4netns`, and `passt`, and writes sensible `~/.config/containers/{registries,storage,policy}.conf` files. ## How it's wired -`home.nix` uses `config.lib.file.mkOutOfStoreSymlink` so the symlinks -point at the **live working tree** at `~/.local/share/dotfiles/...`, not -at copies in `/nix/store`. This means: +`common.nix` uses `config.lib.file.mkOutOfStoreSymlink` so the symlinks +point at the **live working tree** at `~/.local/share/dotfiles/...`, +not at copies in `/nix/store`. This means: -- Editing `dot_config/nvim/init.lua` in the cloned repo takes effect on - the next `nvim` launch with no rebuild. +- Editing `dot_config/nvim/init.lua` in the cloned repo takes effect + on the next `nvim` launch with no rebuild. - `home-manager switch` only needs to re-run when adding/removing a package or changing what's symlinked. The zsh plugins (`zsh-syntax-highlighting`, etc.) live in -`$HOME/.nix-profile/share/`. The shared `dot_zshrc` probes Arch system -paths first, then falls back to the nix-profile path, so the same file -works on both host and VM unchanged. +`$HOME/.nix-profile/share/`. The shared `dot_zshrc` prefers the +nix-profile path on both host and VM, falling back to system paths for +un-bootstrapped states. diff --git a/remote-dev/bootstrap.sh b/nix/bootstrap.sh index 1b7dafe..9f5e144 100755 --- a/remote-dev/bootstrap.sh +++ b/nix/bootstrap.sh @@ -2,12 +2,12 @@ # Bootstrap a headless dev environment on a fresh Ubuntu 22.04 VM. # Idempotent: safe to re-run. # -# curl -fsSL https://raw.githubusercontent.com/<user>/dotfiles/master/remote-dev/bootstrap.sh | sh +# curl -fsSL https://raw.githubusercontent.com/<user>/dotfiles/master/nix/bootstrap.sh | sh # # Steps: # 1. Install Nix (Determinate Systems installer, multi-user). # 2. Clone (or fast-forward) the dotfiles repo to ~/.local/share/dotfiles. -# 3. Run `home-manager switch --flake .../remote-dev#vm`. +# 3. Run `home-manager switch --flake .../nix#vm`. # 4. Install python3.11 via `uv` (needed by Mason pip installs). # 5. Add Nix-store zsh to /etc/shells and chsh the user. # @@ -66,7 +66,7 @@ fi log "Running home-manager switch (this can take a while on first run)…" nix --extra-experimental-features 'nix-command flakes' \ run home-manager/master -- \ - switch --impure --flake "$DIR/remote-dev#vm" -b backup + switch --impure --flake "$DIR/nix#vm" -b backup # ── 4. Mason's python interpreter (via uv from the nix profile) ────────────── # Mason installs some LSPs/linters into per-package pip venvs. We need a diff --git a/nix/common.nix b/nix/common.nix new file mode 100644 index 0000000..b6a8493 --- /dev/null +++ b/nix/common.nix @@ -0,0 +1,160 @@ +{ config, pkgs, lib, dotfilesRoot, ... }: + +# Shared Home-Manager module: the leaf-CLI subset, editor/AI-agent +# runtimes, and the shared dotfiles symlinks used by **both** the Arch +# host and the Ubuntu remote-dev VM. Profile-specific extras live in +# `host.nix` and `vm.nix`. +# +# Policy: this profile carries leaf CLI tools plus editor/AI-agent +# runtimes (node, uv). It must NEVER carry anything the project build +# might invoke. Forbidden on PATH (would shadow the system's and break +# builds against the system sysroot/libc): cc, c++, gcc, g++, clang, +# clang++, ld, ld.lld, ar, nm, objcopy, make, cmake, ninja, meson, +# pkg-config, autoconf, automake, libtool, python, python3, pip, +# cargo, rustc, go. If a project needs a newer toolchain, put it in a +# project-local flake.nix + direnv `.envrc`, NOT here. +# +# Allowed runtimes (used only by editor/AI agents): node, npm, npx +# (via `nodejs`), uv, uvx (via `uv` — does NOT install a python3, +# manages its own interpreters under XDG). `clang-tools` is allowed +# because it ships only formatters/linters/clangd, no compiler driver. + +let + dotfiles = "${config.home.homeDirectory}/.local/share/dotfiles"; + link = path: config.lib.file.mkOutOfStoreSymlink "${dotfiles}/${path}"; +in +{ + home.stateVersion = "25.05"; + + # ── Packages ──────────────────────────────────────────────────────────────── + home.packages = with pkgs; [ + # Editor + multiplexer + neovim + zellij + tree-sitter + + # Search / move + ripgrep + fd + fzf + sd + choose + zoxide + just + + # Viewers + bat + lsd + glow + + # Git stack + git + gh + delta + mergiraf + + # JSON / YAML + jq + yq-go + + # System + htop + fastfetch + + # Net + curl + curlie + wget + dog + rsync + openssh + + # Docs + tldr + man-db + man-pages + + # Secrets + gnupg + pass + + # C/C++ source tooling (no compiler driver in PATH) + clang-tools + + # Editor/AI agent runtimes — NOT for project builds (see policy above) + nodejs_24 # copilot-language-server requires Node 24 (see ai.lua) + uv # for project tooling that asks for `uv`/`uvx`; brings no python + + # AI coding agents + claude-code + github-copilot-cli # NB: pkgs.copilot-cli is AWS Copilot, NOT this + + # Zsh and plugins (loaded from $HOME/.nix-profile/share/... by the + # shared zshrc; nix-profile path is preferred, system path is the + # fallback for un-bootstrapped states). + zsh + zsh-syntax-highlighting + zsh-autosuggestions + zsh-history-substring-search + ]; + + # ── direnv + nix-direnv ───────────────────────────────────────────────────── + programs.direnv = { + enable = true; + nix-direnv.enable = true; + enableZshIntegration = false; # zshrc already calls `eval "$(direnv hook zsh)"` + }; + + # ── Shared config symlinks ────────────────────────────────────────────────── + # Live symlinks back into the cloned working tree so `git pull` is enough + # to update configs — no `home-manager switch` required after every edit. + xdg.configFile = { + "nvim".source = link "dot_config/nvim"; + "zellij".source = link "dot_config/zellij"; + "zsh/.zshrc".source = link "dot_config/zsh/dot_zshrc"; + "zsh/.zprofile".source = link "dot_config/zsh/dot_zprofile"; + "ghostty".source = link "dot_config/ghostty"; # for terminfo refs only + "direnv/direnvrc".source = link "dot_config/direnv/direnvrc"; + "git/config".source = link "dot_config/git/config"; + "git/attributes".source = link "dot_config/git/attributes"; + "git/ignore".source = link "dot_config/git/ignore"; + # Git hooks: source filenames carry the chezmoi `executable_` attribute + # prefix which only chezmoi strips. In nix-managed setups we use raw + # symlinks, so map each hook to its stripped name explicitly. The + # executable bit comes from the working-tree file mode (git resolves + # the symlink). + "git/hooks/pre-push".source = link "dot_config/git/hooks/executable_pre-push"; + "git/hooks/pre-commit".source = link "dot_config/git/hooks/executable_pre-commit"; + "git/hooks/commit-msg".source = link "dot_config/git/hooks/executable_commit-msg"; + "git/hooks/post-commit".source = link "dot_config/git/hooks/executable_post-commit"; + "git/hooks/_dispatch.sh".source = link "dot_config/git/hooks/_dispatch.sh"; + }; + + # ~/.ssh/config from the dotfiles tree (read-only); keys + known_hosts + # stay machine-local. We can't symlink via home.file because + # mkOutOfStoreSymlink exposes the working-tree perms (0664 under a + # default umask 002) and OpenSSH refuses any group-writable ssh_config. + # Materialize a real 0600 file via activation instead. + home.activation.sshConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + run install -D -m 600 \ + "${dotfiles}/private_dot_ssh/config" "$HOME/.ssh/config" + ''; + + # ZDOTDIR redirect so login shells find ~/.config/zsh/.zprofile etc. + # Also source HM's session-vars — HM normally drops these into + # ~/.profile, but zsh login shells don't read .profile, and we don't + # use programs.zsh.enable. + home.file.".zshenv".text = '' + if [ -r "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" ]; then + . "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" + fi + export ZDOTDIR="$HOME/.config/zsh" + [[ -r "$ZDOTDIR/.zshenv" ]] && source "$ZDOTDIR/.zshenv" + ''; + + # ── XDG base dirs ────────────────────────────────────────────────────────── + xdg.enable = true; + + # ── Enable HM-managed activation messages ────────────────────────────────── + programs.home-manager.enable = true; +} diff --git a/remote-dev/flake.lock b/nix/flake.lock index 349d5fd..349d5fd 100644 --- a/remote-dev/flake.lock +++ b/nix/flake.lock diff --git a/remote-dev/flake.nix b/nix/flake.nix index 69ddafd..8896f2f 100644 --- a/remote-dev/flake.nix +++ b/nix/flake.nix @@ -1,5 +1,5 @@ { - description = "Headless dev environment for remote Ubuntu VMs."; + description = "Home-Manager profiles for the Arch host and the Ubuntu remote-dev VM."; inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; @@ -14,25 +14,31 @@ system = "x86_64-linux"; pkgs = import nixpkgs { inherit system; - # Whitelist specific unfree packages (claude-code, github-copilot-cli) - # instead of globally setting allowUnfree, so a typo elsewhere can't - # silently pull in additional unfree deps. + # Whitelist specific unfree packages (claude-code, + # github-copilot-cli) instead of globally setting allowUnfree, + # so a typo elsewhere can't silently pull in additional unfree + # deps. config.allowUnfreePredicate = pkg: builtins.elem (nixpkgs.lib.getName pkg) [ "claude-code" "github-copilot-cli" ]; }; - in - { - homeConfigurations.vm = home-manager.lib.homeManagerConfiguration { + + mkProfile = module: home-manager.lib.homeManagerConfiguration { inherit pkgs; - modules = [ ./home.nix ]; - # Path to the cloned dotfiles checkout — passed in so home.nix can - # symlink shared configs (nvim, zellij, zsh) from the same repo. + modules = [ module ]; + # Path to the cloned dotfiles checkout — passed in so the + # modules can symlink shared configs from the same repo. extraSpecialArgs = { dotfilesRoot = ../.; }; }; + in + { + homeConfigurations = { + vm = mkProfile ./vm.nix; + host = mkProfile ./host.nix; + }; }; } diff --git a/nix/host.nix b/nix/host.nix new file mode 100644 index 0000000..7d81ffe --- /dev/null +++ b/nix/host.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, dotfilesRoot, ... }: + +# Arch host Home-Manager profile. Layered on top of `common.nix`; adds +# only host-specific concerns that don't make sense on the VM. + +{ + imports = [ ./common.nix ]; + + home.username = builtins.getEnv "USER"; + home.homeDirectory = builtins.getEnv "HOME"; + + # ── Smartcard (Yubikey) ──────────────────────────────────────────────────── + # Nix's gnupg ships its own scdaemon. Delegate to the system pcscd + # service instead of letting nix's scdaemon open the USB device + # directly (which would race with pcscd). `pcsclite` provides the + # shared library at the path below and stays in `meta/base.txt`. + home.file.".gnupg/scdaemon.conf".text = '' + disable-ccid + pcsc-driver /usr/lib/libpcsclite.so.1 + ''; +} diff --git a/remote-dev/justfile b/nix/justfile index db254df..19e4a9b 100644 --- a/remote-dev/justfile +++ b/nix/justfile @@ -1,4 +1,4 @@ -# Recipes for the remote-dev VM. Run from ~/.local/share/dotfiles/remote-dev. +# Recipes for the remote-dev VM. Run from ~/.local/share/dotfiles/nix. # Show available recipes (default) default: diff --git a/nix/vm.nix b/nix/vm.nix new file mode 100644 index 0000000..d003b6e --- /dev/null +++ b/nix/vm.nix @@ -0,0 +1,71 @@ +{ config, pkgs, lib, dotfilesRoot, ... }: + +# VM-only Home-Manager profile (Ubuntu 22.04 remote-dev box). Adds +# Mason-related runtime carve-outs and the rootless podman stack on +# top of `common.nix`. + +{ + imports = [ ./common.nix ]; + + home.username = builtins.getEnv "USER"; + home.homeDirectory = builtins.getEnv "HOME"; + + home.sessionVariables = { + # Ubuntu 20.04-derived hosts still default to cgroups v1; podman 5 + # warns on every invocation. Flipping to v2 is a host-level reboot + # and only matters for --memory/--cpus, so silence the warning. + PODMAN_IGNORE_CGROUPSV1_WARNING = "1"; + }; + + home.packages = with pkgs; [ + # ── Mason-driven LSP carve-outs (removed by phase p6 once Mason is + # gone and LSPs come from common.nix directly). Kept here for + # now so the VM keeps working between phases. ─────────────────────── + jre # Mason's groovy-language-server (headless Java) + basedpyright # Mason's pypi distro can't install on Ubuntu 20.04 + # (manylinux_2_28 wheels, uv's python rejects) + # Rust toolchain for Mason packages whose only install source is + # `cargo install` (shellharden). The Arch host has these via pacman; + # on the VM Mason needs cargo+rustc on PATH or it bails with ENOENT. + cargo + rustc + + # ── Rootless podman ───────────────────────────────────────────────────── + # The nix `podman` is wrapped to find these helpers via /nix/store + # paths, so we don't need to write a containers.conf for + # `helper_binaries_dir`. + podman + crun # OCI runtime (lighter than runc; default for rootless) + conmon # container monitor process + netavark # default network stack on podman 4+ + aardvark-dns # DNS for netavark networks + slirp4netns # rootless user-mode networking + passt # pasta backend (slirp4netns successor; podman picks it up) + ]; + + # ── Rootless podman config ────────────────────────────────────────────────── + # Kept inline (not in the chezmoi tree) because Arch's system-wide + # /etc/containers defaults already work there; these files exist only + # to give nix's user-installed podman sane rootless defaults. + xdg.configFile."containers/registries.conf".text = '' + unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"] + short-name-mode = "permissive" + ''; + + xdg.configFile."containers/storage.conf".text = '' + [storage] + # runroot/graphroot default to $XDG_RUNTIME_DIR/containers and + # $XDG_DATA_HOME/containers/storage respectively for rootless — leave unset. + driver = "overlay" + + [storage.options.overlay] + # Kernel >=5.13 supports rootless overlay natively (VM is on 5.15), + # so mount_program is left unset → uses the kernel driver directly + # instead of fuse-overlayfs. + ''; + + xdg.configFile."containers/policy.json".text = builtins.toJSON { + default = [ { type = "insecureAcceptAnything"; } ]; + transports.docker-daemon."" = [ { type = "insecureAcceptAnything"; } ]; + }; +} diff --git a/remote-dev/home.nix b/remote-dev/home.nix deleted file mode 100644 index a94278b..0000000 --- a/remote-dev/home.nix +++ /dev/null @@ -1,223 +0,0 @@ -{ config, pkgs, lib, dotfilesRoot, ... }: - -let - # The dotfiles checkout is cloned to ~/.local/share/dotfiles by bootstrap.sh. - # We do NOT use `dotfilesRoot` as a Nix store path because that would copy - # the entire repo into the store on every rebuild. Instead, we symlink - # config dirs at runtime via `config.lib.file.mkOutOfStoreSymlink`, which - # points at the live working tree so edits take effect without a rebuild. - dotfiles = "${config.home.homeDirectory}/.local/share/dotfiles"; - link = path: config.lib.file.mkOutOfStoreSymlink "${dotfiles}/${path}"; -in -{ - home.username = builtins.getEnv "USER"; - home.homeDirectory = builtins.getEnv "HOME"; - home.stateVersion = "25.05"; - - home.sessionVariables = { - # Ubuntu 20.04 still defaults to cgroups v1; podman 5 warns on every - # invocation. Flipping to v2 is a host-level reboot (see README) and - # only matters if we need --memory/--cpus, so silence the warning. - PODMAN_IGNORE_CGROUPSV1_WARNING = "1"; - }; - - # ── Packages ──────────────────────────────────────────────────────────────── - # Policy: this profile carries leaf CLI tools plus editor/AI-agent - # runtimes (node, uv). It must NEVER carry anything the project build - # might invoke. Forbidden on PATH (would shadow Ubuntu's and break - # builds against the system sysroot/libc): cc, c++, gcc, g++, clang, - # clang++, ld, ld.lld, ar, nm, objcopy, make, cmake, ninja, meson, - # pkg-config, autoconf, automake, libtool, python, python3, pip, - # cargo, rustc, go. If a project needs a newer toolchain, put it in - # a project-local flake.nix + direnv `.envrc`, NOT here. - # - # Allowed runtimes (used only by Mason/editor/AI agents): node, npm, - # npx (via `nodejs`), uv, uvx (via `uv` — does NOT install a python3, - # manages its own interpreters under XDG). clang-tools is allowed - # because it ships only formatters/linters/clangd, no compiler driver. - home.packages = with pkgs; [ - # Editor + multiplexer - neovim - zellij - tree-sitter - - # Search / move - ripgrep - fd - fzf - sd - choose - zoxide - just - - # Viewers - bat - lsd - glow - - # Git stack - git - gh - delta - mergiraf - - # JSON / YAML - jq - yq-go - - # System - htop - fastfetch - - # Net - curl - curlie - wget - dog - rsync - openssh - - # Docs - tldr - man-db - man-pages - - # Secrets (user can bring their key separately) - gnupg - pass - - # C/C++ source tooling (no compiler driver in PATH) - clang-tools - - # Editor/AI agent runtimes — NOT for project builds (see policy above) - nodejs_24 # Mason npm LSPs + copilot-language-server (needs Node 24, see ai.lua) - uv # Mason python LSPs in isolated venvs; brings `uv`/`uvx` only - jre # for Mason's groovy-language-server (headless Java runtime) - basedpyright # see lsp.lua: Mason's pypi distro can't install on Ubuntu 20.04 - # (nodejs-wheel-binaries has only manylinux_2_28 wheels which - # uv's python rejects since it's manylinux2014; source build - # of Node 24 needs gcc >=10 and host gcc is 9.4) - - # NB: python3.11 for Mason is NOT installed here — see bootstrap.sh - # step 4. Nix's python disables manylinux wheel support by design - # (its libc is patched and doesn't satisfy any manylinux policy), so - # pip in a nix-python venv falls back to source builds for packages - # like `nodejs-wheel-binaries` (pulled in by basedpyright). That - # source build then fails on Ubuntu 20.04's gcc 9.4 (no C++20). - # Bootstrap uses `uv python install 3.11` to fetch a portable - # manylinux-aware CPython and symlinks it to ~/.local/bin/python3.11. - - # Rust toolchain for Mason packages whose only install source is - # `cargo install` (shellharden). The host has these via the Arch - # package manager; on the VM Mason needs cargo+rustc on PATH or it - # bails with ENOENT. - cargo - rustc - - # AI coding agents - claude-code - github-copilot-cli # NB: pkgs.copilot-cli is AWS Copilot, NOT this - - # Zsh and plugins (sourced from $HOME/.nix-profile/share/... by the shared zshrc) - zsh - zsh-syntax-highlighting - zsh-autosuggestions - zsh-history-substring-search - - # Rootless podman (see README "Podman" section for host prerequisites). - # The nix `podman` is wrapped to find these helpers via /nix/store paths, - # so we don't need to write a containers.conf for `helper_binaries_dir`. - podman - crun # OCI runtime (lighter than runc; default for rootless) - conmon # container monitor process - netavark # default network stack on podman 4+ - aardvark-dns # DNS for netavark networks - slirp4netns # rootless user-mode networking - passt # pasta backend (slirp4netns successor; podman picks it up) - ]; - - # ── direnv + nix-direnv ───────────────────────────────────────────────────── - programs.direnv = { - enable = true; - nix-direnv.enable = true; - enableZshIntegration = false; # zshrc already calls `eval "$(direnv hook zsh)"` - }; - - # ── Shared config symlinks ────────────────────────────────────────────────── - # Live symlinks back into the cloned working tree so `git pull` is enough - # to update configs — no `home-manager switch` required after every edit. - xdg.configFile = { - "nvim".source = link "dot_config/nvim"; - "zellij".source = link "dot_config/zellij"; - "zsh/.zshrc".source = link "dot_config/zsh/dot_zshrc"; - "zsh/.zprofile".source = link "dot_config/zsh/dot_zprofile"; - "ghostty".source = link "dot_config/ghostty"; # for terminfo refs only - "direnv/direnvrc".source = link "dot_config/direnv/direnvrc"; - "git/config".source = link "dot_config/git/config"; - "git/attributes".source = link "dot_config/git/attributes"; - "git/ignore".source = link "dot_config/git/ignore"; - # Git hooks: source filenames carry the chezmoi `executable_` attribute - # prefix which only chezmoi strips. On remote-dev we use raw symlinks, - # so map each hook to its stripped name explicitly. The executable bit - # comes from the working-tree file mode (git resolves the symlink). - "git/hooks/pre-push".source = link "dot_config/git/hooks/executable_pre-push"; - "git/hooks/pre-commit".source = link "dot_config/git/hooks/executable_pre-commit"; - "git/hooks/commit-msg".source = link "dot_config/git/hooks/executable_commit-msg"; - "git/hooks/post-commit".source = link "dot_config/git/hooks/executable_post-commit"; - "git/hooks/_dispatch.sh".source = link "dot_config/git/hooks/_dispatch.sh"; - }; - - # ── Rootless podman config ────────────────────────────────────────────────── - # Kept inline (not in the chezmoi tree) because Arch's system-wide - # /etc/containers defaults already work there; these files exist only - # to give nix's user-installed podman sane rootless defaults. - xdg.configFile."containers/registries.conf".text = '' - unqualified-search-registries = ["docker.io", "quay.io", "ghcr.io"] - short-name-mode = "permissive" - ''; - - xdg.configFile."containers/storage.conf".text = '' - [storage] - # runroot/graphroot default to $XDG_RUNTIME_DIR/containers and - # $XDG_DATA_HOME/containers/storage respectively for rootless — leave unset. - driver = "overlay" - - [storage.options.overlay] - # Kernel >=5.13 supports rootless overlay natively (VM is on 5.15), - # so mount_program is left unset → uses the kernel driver directly - # instead of fuse-overlayfs. - ''; - - xdg.configFile."containers/policy.json".text = builtins.toJSON { - default = [ { type = "insecureAcceptAnything"; } ]; - transports.docker-daemon."" = [ { type = "insecureAcceptAnything"; } ]; - }; - - # ~/.ssh/config from the dotfiles tree (read-only); keys + known_hosts - # stay machine-local on the VM. We can't symlink via home.file because - # mkOutOfStoreSymlink exposes the working-tree perms (0664 under Ubuntu's - # default umask 002) and OpenSSH refuses any group-writable ssh_config. - # Materialize a real 0600 file via activation instead. - home.activation.sshConfig = lib.hm.dag.entryAfter [ "writeBoundary" ] '' - run install -D -m 600 \ - "${dotfiles}/private_dot_ssh/config" "$HOME/.ssh/config" - ''; - - # ZDOTDIR redirect so login shells find ~/.config/zsh/.zprofile etc. - # Also source HM's session-vars (PODMAN_IGNORE_CGROUPSV1_WARNING, etc.) — - # HM normally drops these into ~/.profile, but zsh login shells don't read - # .profile, and we don't use programs.zsh.enable. - home.file.".zshenv".text = '' - if [ -r "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" ]; then - . "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" - fi - export ZDOTDIR="$HOME/.config/zsh" - [[ -r "$ZDOTDIR/.zshenv" ]] && source "$ZDOTDIR/.zshenv" - ''; - - # ── XDG base dirs (Ubuntu doesn't set these in /etc/profile.d by default) ── - xdg.enable = true; - - # ── Enable HM-managed activation messages ────────────────────────────────── - programs.home-manager.enable = true; -} |
