diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6b19e96 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{nix,yaml,yml,json,md,sh,toml}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true + +# Markdown uses trailing whitespace for hard line breaks. +[*.md] +trim_trailing_whitespace = false diff --git a/.renovaterc.json b/.renovaterc.json index 5024f5f..5a18122 100644 --- a/.renovaterc.json +++ b/.renovaterc.json @@ -1,10 +1,6 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended", - ":dependencyDashboard", - ":semanticCommits" - ], + "extends": ["config:recommended", ":dependencyDashboard", ":semanticCommits"], "nix": { "enabled": true }, diff --git a/README.md b/README.md index 5616204..7ef1ec5 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ single flake. Defined in the host table in [`flake.nix`](./flake.nix): -| Configuration | System | Machine | -| ------------------- | --------------- | ---------------------------------------- | -| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | -| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) | +| Configuration | System | Machine | +| --------------------- | ---------------- | --------------------------------------------------------------------------- | +| `lyrathorpe-mbp` | `aarch64-linux` | MacBook Pro (Apple Silicon, Asahi) | +| `lyrathorpe-t400` | `x86_64-linux` | ThinkPad T400 — [install notes](./system/machine/T400/README.md) | | `lyrathorpe-macpro31` | `x86_64-linux` | Mac Pro 3,1, desktop — [install notes](./system/machine/MacPro31/README.md) | -| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | -| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | +| `emmathorpe-edaas` | `x86_64-linux` | Work WSL box (NixOS-WSL) | +| `lyrathorpe-mac` | `aarch64-darwin` | macOS (nix-darwin) | Shared layers: `lyrathorpe/home` (home-manager: shell, git, editor), `system/modules/common-nixos.nix` (all NixOS hosts), and @@ -48,14 +48,29 @@ WSL work box) keep plain TTY login. The target account needs a password ## MacBook (Asahi) firmware The MBP host references `system/modules/firmware/` for Apple peripheral -firmware (Wi-Fi/Bluetooth). Those blobs are **not** redistributable, so the -directory is gitignored and a clean checkout will not build `lyrathorpe-mbp` -until it is populated out-of-band. +firmware (Wi-Fi/Bluetooth). These blobs are **committed** (tracked) even though +`.gitignore` lists the directory: the flake is `git+file`, so it only sees +tracked files — untracking them breaks `lyrathorpe-mbp` evaluation (and the CI +host-eval) because the config can't find the firmware. They are not +redistributable; the repo is private. -Copy the firmware extracted during the Asahi install (from -`/etc/nixos/firmware` on the freshly-installed machine, or re-extract per the +To refresh them, copy the firmware extracted during the Asahi install (from +`/etc/nixos/firmware`, or re-extract per the [Asahi NixOS docs](https://github.com/tpwrules/nixos-apple-silicon)) into -`system/modules/firmware/` before rebuilding that host. +`system/modules/firmware/` and commit with `git add -f`. + +## Development + +A dev shell and a formatting/lint gate are wired through the flake: + +- `nix develop` — shell with `deadnix`, `statix`, `treefmt`, and the git + `pre-commit` hooks (installed automatically on first entry). +- `nix fmt` — formats the tree via `treefmt` (nixfmt + shfmt + prettier; + generated files and `flake.lock` are excluded). +- `nix flake check` — runs formatting, `deadnix`, `statix`, the pre-commit + hooks, and evaluates every host. `.editorconfig` carries the base style; + `statix.toml` disables the two house-style lints (`repeated_keys`, + `empty_pattern`). ## CI diff --git a/flake.lock b/flake.lock index 822ec07..73f2769 100644 --- a/flake.lock +++ b/flake.lock @@ -40,6 +40,22 @@ } }, "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { "locked": { "lastModified": 1761640442, "narHash": "sha256-AtrEP6Jmdvrqiv4x2xa5mrtaIp3OEe8uBYCDZDS+hu8=", @@ -54,7 +70,7 @@ "type": "github" } }, - "flake-compat_2": { + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1767039857, @@ -90,6 +106,49 @@ "type": "github" } }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1778507602, + "narHash": "sha256-kTwur1wV+01SdqskVMSo6JMEpg71ps3HpbFY2GsflKs=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "61ab0e80d9c7ab14c256b5b453d8b3fb0189ba0a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -172,7 +231,7 @@ }, "nixos-apple-silicon": { "inputs": { - "flake-compat": "flake-compat", + "flake-compat": "flake-compat_2", "nixpkgs": [ "nixpkgs" ] @@ -193,7 +252,7 @@ }, "nixos-wsl": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "nixpkgs": [ "nixpkgs" ] @@ -248,6 +307,7 @@ "inputs": { "firefox-addons": "firefox-addons", "flake-parts": "flake-parts", + "git-hooks": "git-hooks", "home-manager": "home-manager", "nix-darwin": "nix-darwin", "nix-homebrew": "nix-homebrew", @@ -255,7 +315,28 @@ "nixos-apple-silicon": "nixos-apple-silicon", "nixos-wsl": "nixos-wsl", "nixpkgs": "nixpkgs", - "nixpkgs-unstable": "nixpkgs-unstable" + "nixpkgs-unstable": "nixpkgs-unstable", + "treefmt-nix": "treefmt-nix" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780220602, + "narHash": "sha256-eynAfOmbmxJnkp7YewvCEbShNnnYJ9gLLqkzsYtBPeM=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "db947814a175b7ca6ded66e21383d938df01c227", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" } } }, diff --git a/flake.nix b/flake.nix index c11803e..ed4a090 100644 --- a/flake.nix +++ b/flake.nix @@ -66,11 +66,13 @@ # claude-code tracks nixpkgs-unstable regardless of the pinned nixpkgs. overlays = [ (_final: prev: { - claude-code = + inherit (import nixpkgs-unstable { inherit (prev.stdenv.hostPlatform) system; config.allowUnfree = true; - }).claude-code; + }) + claude-code + ; }) ]; @@ -309,20 +311,28 @@ programs.shfmt.enable = true; programs.prettier.enable = true; # Generated hardware-configuration.nix files are not hand-edited. - settings.global.excludes = [ "*/hardware-configuration.nix" ]; + settings.global.excludes = [ + "*/hardware-configuration.nix" # generated by nixos-generate-config + "flake.lock" # generated by `nix flake lock` + ]; }; # Pre-commit hooks: format + lint gate run on commit. The same hooks # are exposed as a flake check (pre-commit.check.enable defaults true). - pre-commit.settings.hooks = { - nixfmt-rfc-style.enable = true; - deadnix = { - enable = true; - # Unused module args ({config,lib,pkgs,...}) are normal; only flag - # genuinely dead bindings. - settings.noLambdaPatternNames = true; + pre-commit.settings = { + # Generated by nixos-generate-config; don't lint/reformat (treefmt + # excludes them too). + excludes = [ "hardware-configuration\\.nix$" ]; + hooks = { + nixfmt-rfc-style.enable = true; + deadnix = { + enable = true; + # Unused module args ({config,lib,pkgs,...}) are normal; only + # flag genuinely dead bindings. + settings.noLambdaPatternNames = true; + }; + statix.enable = true; # reads statix.toml (repeated_keys/empty_pattern disabled) }; - statix.enable = true; # reads .statix.toml (repeated_keys/empty_pattern disabled) }; # treefmt-nix exposes its own `checks.treefmt`; alias it to @@ -336,7 +346,7 @@ deadnix --fail --no-lambda-pattern-names ${./.} && touch $out ''; checks.statix = pkgs.runCommandLocal "check-statix" { nativeBuildInputs = [ pkgs.statix ]; } '' - statix check ${./.} && touch $out + statix check -c ${./.} ${./.} && touch $out ''; # `nix develop` shell with the tooling needed to hack on this flake. diff --git a/lyrathorpe/home/KEYBINDINGS.md b/lyrathorpe/home/KEYBINDINGS.md index 2382edc..b768d01 100644 --- a/lyrathorpe/home/KEYBINDINGS.md +++ b/lyrathorpe/home/KEYBINDINGS.md @@ -4,12 +4,12 @@ Every keyboard shortcut configured across this desktop, and where it is defined. Everything here is managed declaratively through Nix — edit the listed file and rebuild, never the generated dotfiles. -| Area | Defined in | -| --- | --- | +| Area | Defined in | +| ----------------- | --------------------------------------------------------------------------------------------------------------------- | | Sway (compositor) | [`sway.nix`](./sway.nix) `config.keybindings` + `config.modes`, plus the home-manager Sway module's built-in defaults | -| tmux | [`shell.nix`](./shell.nix) `programs.tmux` | -| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` | -| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) | +| tmux | [`shell.nix`](./shell.nix) `programs.tmux` | +| zsh line editor | [`shell.nix`](./shell.nix) `programs.zsh.historySubstringSearch` | +| foot (terminal) | foot package defaults — only colours are themed (in `sway.nix`) | **Conventions** @@ -26,49 +26,49 @@ rebuild, never the generated dotfiles. ### Applications & session -| Shortcut | Action | -| --- | --- | -| `Super`+`Return` | Open a terminal (foot) | -| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) | -| `Super`+`d` | App launcher (same as above; module default) | -| `Super`+`e` | File manager (nemo) | -| `Super`+`c` | Clipboard history picker (clipman → fuzzel) | -| `Super`+`l` | Lock screen (swaylock) | -| `Super`+`Shift`+`q` | Close the focused window | -| `Super`+`Shift`+`c` | Reload the Sway config | -| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) | +| Shortcut | Action | +| ------------------- | ------------------------------------------------------- | +| `Super`+`Return` | Open a terminal (foot) | +| `Super`+`Space` | App launcher (sway-launcher-desktop in a floating foot) | +| `Super`+`d` | App launcher (same as above; module default) | +| `Super`+`e` | File manager (nemo) | +| `Super`+`c` | Clipboard history picker (clipman → fuzzel) | +| `Super`+`l` | Lock screen (swaylock) | +| `Super`+`Shift`+`q` | Close the focused window | +| `Super`+`Shift`+`c` | Reload the Sway config | +| `Super`+`Shift`+`e` | Exit Sway (asks for confirmation) | ### Focus -| Shortcut | Action | -| --- | --- | -| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction | -| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) | -| `Super`+`a` | Focus the parent container | -| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating | +| Shortcut | Action | +| ----------------------- | ---------------------------------------- | +| `Super`+`←`/`↓`/`↑`/`→` | Move focus by direction | +| `Super`+`h`/`j`/`k` | Move focus left / down / up (vim-style) | +| `Super`+`a` | Focus the parent container | +| `Super`+`Alt`+`Space` | Toggle focus between tiling and floating | > Note: vim focus-right would be `Super`+`l`, but that is bound to **lock** here; > use `Super`+`→`. ### Moving windows -| Shortcut | Action | -| --- | --- | -| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction | +| Shortcut | Action | +| ------------------------------- | ---------------------------------------- | +| `Super`+`Shift`+`←`/`↓`/`↑`/`→` | Move the window by direction | | `Super`+`Shift`+`h`/`j`/`k`/`l` | Move the window left / down / up / right | -| `Super`+`Shift`+`Space` | Toggle the window floating | +| `Super`+`Shift`+`Space` | Toggle the window floating | Mouse (with `Super` held): left-drag moves a window, right-drag resizes it. ### Layout -| Shortcut | Action | -| --- | --- | -| `Super`+`b` | Split horizontally | -| `Super`+`v` | Split vertically | -| `Super`+`s` | Stacking layout | -| `Super`+`w` | Tabbed layout | -| `Super`+`f` | Toggle fullscreen | +| Shortcut | Action | +| ----------- | -------------------------------------------------------------------------------------- | +| `Super`+`b` | Split horizontally | +| `Super`+`v` | Split vertically | +| `Super`+`s` | Stacking layout | +| `Super`+`w` | Tabbed layout | +| `Super`+`f` | Toggle fullscreen | | `Super`+`y` | **Layout submenu**: `s` stacking · `w` tabbed · `e` toggle split · `Return`/`Esc` exit | > The layout submenu's `e` (toggle split) is the home for that action since @@ -76,49 +76,49 @@ Mouse (with `Super` held): left-drag moves a window, right-drag resizes it. ### Workspaces -| Shortcut | Action | -| --- | --- | -| `Super`+`1`…`0` | Switch to workspace 1…10 | +| Shortcut | Action | +| ----------------------- | --------------------------------- | +| `Super`+`1`…`0` | Switch to workspace 1…10 | | `Super`+`Shift`+`1`…`0` | Move the window to workspace 1…10 | -| `Super`+`z` | Previous workspace | -| `Super`+`x` | Next workspace | +| `Super`+`z` | Previous workspace | +| `Super`+`x` | Next workspace | ### Scratchpad -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| ------------------- | --------------------------------- | | `Super`+`Shift`+`-` | Move the window to the scratchpad | -| `Super`+`-` | Show / cycle the scratchpad | +| `Super`+`-` | Show / cycle the scratchpad | ### Modes (submenus) -| Shortcut | Action | -| --- | --- | -| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit | -| `Super`+`y` | **Layout mode** (see Layout above) | +| Shortcut | Action | +| ------------------- | ------------------------------------------------------------------------------------------------------------ | +| `Super`+`r` | **Resize mode**: arrow keys resize; `Return`/`Esc` exit | +| `Super`+`y` | **Layout mode** (see Layout above) | | `Super`+`Shift`+`x` | **Power menu**: `l` lock · `e` log out · `s` sleep · `r` reboot · `Shift`+`s` shutdown · `Return`/`Esc` exit | ### Screenshots -| Shortcut | Action | -| --- | --- | -| `Print` | Select a region → swappy (annotate/save) | -| `Shift`+`Print` | Focused window → swappy | +| Shortcut | Action | +| --------------- | ---------------------------------------- | +| `Print` | Select a region → swappy (annotate/save) | +| `Shift`+`Print` | Focused window → swappy | ### Audio & media -| Shortcut | Action | -| --- | --- | -| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) | -| `XF86AudioMute` | Toggle output mute | -| `XF86AudioMicMute` | Toggle microphone mute | -| `XF86AudioPlay` | Play/pause (playerctl) | -| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track | +| Shortcut | Action | +| ----------------------------------------------- | ---------------------- | +| `XF86AudioRaiseVolume` / `XF86AudioLowerVolume` | Volume ±5% (wpctl) | +| `XF86AudioMute` | Toggle output mute | +| `XF86AudioMicMute` | Toggle microphone mute | +| `XF86AudioPlay` | Play/pause (playerctl) | +| `XF86AudioNext` / `XF86AudioPrev` | Next / previous track | ### Brightness — laptops only -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| ----------------------------------------------- | ----------------------------- | | `XF86MonBrightnessUp` / `XF86MonBrightnessDown` | Backlight ±5% (brightnessctl) | Present only on portable hosts (T400, MBP); desktops have no internal backlight. @@ -129,19 +129,19 @@ Present only on portable hosts (T400, MBP); desktops have no internal backlight. Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys. -| Shortcut | Action | -| --- | --- | -| `Ctrl`+`b` then `v` | Split into left/right panes | -| `Ctrl`+`b` then `s` | Split into top/bottom panes | -| `Ctrl`+`h`/`j`/`k`/`l` | Move between panes — and into/out of vim splits — seamlessly (vim-tmux-navigator, no prefix) | -| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) | -| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) | -| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane | -| `Ctrl`+`b` then `c` | New window | -| `Ctrl`+`b` then `n` / `p` | Next / previous window | -| `Ctrl`+`b` then `d` | Detach | -| `Ctrl`+`b` then `Ctrl`+`s` / `Ctrl`+`r` | Save / restore the session (resurrect; continuum also auto-saves and restores on start) | -| Mouse | Enabled — click to focus, drag borders, scroll, select | +| Shortcut | Action | +| --------------------------------------- | -------------------------------------------------------------------------------------------- | +| `Ctrl`+`b` then `v` | Split into left/right panes | +| `Ctrl`+`b` then `s` | Split into top/bottom panes | +| `Ctrl`+`h`/`j`/`k`/`l` | Move between panes — and into/out of vim splits — seamlessly (vim-tmux-navigator, no prefix) | +| `Alt`+`←`/`→`/`↑`/`↓` | Switch pane by direction (no prefix needed) | +| `Ctrl`+`b` then `[` | Enter copy mode (then vi motions; `Space`/`Enter` to select/copy) | +| `Ctrl`+`b` then `z` | Zoom / unzoom the focused pane | +| `Ctrl`+`b` then `c` | New window | +| `Ctrl`+`b` then `n` / `p` | Next / previous window | +| `Ctrl`+`b` then `d` | Detach | +| `Ctrl`+`b` then `Ctrl`+`s` / `Ctrl`+`r` | Save / restore the session (resurrect; continuum also auto-saves and restores on start) | +| Mouse | Enabled — click to focus, drag borders, scroll, select | > The stock split keys `%` and `"` are unbound; use `v` / `s` above. `Ctrl`+`b` > then `s` is therefore a split, not the session tree. @@ -155,22 +155,22 @@ Prefix is **`Ctrl`+`b`** (default). Copy mode uses **vi** keys. Only colours are themed; these are foot's default key bindings. -| Shortcut | Action | -| --- | --- | -| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) | -| `Shift`+`Insert` | Paste primary selection | -| `Ctrl`+`Shift`+`r` | Search scrollback | -| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset | -| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) | -| `Ctrl`+`Shift`+`n` | Spawn a new terminal | -| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward | +| Shortcut | Action | +| --------------------------------------- | ----------------------------- | +| `Ctrl`+`Shift`+`c` / `Ctrl`+`Shift`+`v` | Copy / paste (clipboard) | +| `Shift`+`Insert` | Paste primary selection | +| `Ctrl`+`Shift`+`r` | Search scrollback | +| `Ctrl`+`+` / `Ctrl`+`-` / `Ctrl`+`0` | Font larger / smaller / reset | +| `Ctrl`+`Shift`+`u` | URL mode (jump to/open links) | +| `Ctrl`+`Shift`+`n` | Spawn a new terminal | +| `Shift`+`PageUp` / `Shift`+`PageDown` | Scroll back / forward | --- ## zsh -| Shortcut | Action | -| --- | --- | +| Shortcut | Action | +| --------- | -------------------------------------------------------------------------------------------------- | | `↑` / `↓` | History **substring** search — type a fragment first, then the arrows cycle matching past commands | Bound for both CSI and SS3 cursor sequences, so it works in foot, iTerm2 and diff --git a/lyrathorpe/home/work.nix b/lyrathorpe/home/work.nix index 854ee9c..5314e51 100644 --- a/lyrathorpe/home/work.nix +++ b/lyrathorpe/home/work.nix @@ -1,3 +1,5 @@ +# Home-manager module for the work (EDaaS/WSL) profile: corporate git signing, +# work toolchain packages and tmux tweaks. Imported only by the work host. { pkgs, lib, ... }: {