diff --git a/README.md b/README.md index 993e134..7fa206f 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,185 @@ # omarchy-setup -Every customization this laptop has on top of a stock Omarchy install, in one -place, so a second machine can be brought to the same state. +Omarchy customizations on top of a stock install, as **profiles**: one per +machine, each built from shared **layers** and generic **roles**. -Captured from Omarchy **4.0.4-1** on **2026-10-10**. +| Profile | Machine | +|---|---| +| `hp-laptop` | The HP (`omarchy-laptop`): Intel, Anker dock, LG displays. Where this repo started. | +| `macbook-air-m2` | The Air (`omarchy-air`): Asahi Linux on Arch Linux ARM, aarch64. | +| `coder-desktop` | The streamed Coder desktops, built by covert-cloud's image pipeline. | + +| Role | Adds | +|---|---| +| `developer` | gh, delta, uv, just, direnv, shfmt/shellcheck, httpie, mise, VS Code | +| `devops` | kubectl, helm, k9s, kubectx, stern, kustomize, talosctl, cilium, tofu, terragrunt, packer, ansible, go-task, yq, sops/age, dig/mtr/nmap/tcpdump | +| `ai-agents` | Claude Code, OpenCode, Codex, Gemini CLI, Copilot CLI | ```bash -git clone ~/omarchy-setup +git clone https://git.nic-oconnor.com/public/omarchy-setup.git ~/omarchy-setup cd ~/omarchy-setup -./install.sh --dry-run # see what it would change -./install.sh # do it +./install.sh --profiles # what exists +./install.sh --profile hp-laptop --show # its layers, and which layer owns each file +./install.sh --dry-run # see what it would change (profile picked by hostname) +./install.sh # do it +./install.sh --roles devops,ai-agents # add roles on top of the profile ``` `install.sh` is idempotent and backs up anything it overwrites as -`.bak.`. `capture.sh` runs the other way, pulling live changes back -into the repo so this stays the source of truth. +`.bak.`. It records the profile and roles it applied in +`~/.config/omarchy-setup/profile`, so later runs (and `capture.sh`) need no +flags. `capture.sh` runs the other way, pulling live changes back into the +layer that owns each file, so the repo stays the source of truth. -## What is in here +## How profiles are built -| Path | What | +Everything lives under `profiles/`. A layer is a directory with a `profile` +file (bash: `description`, `include=(...)`, and for machines `hostnames`, +`arch`, `handoff_peer_ips`) and any of the same pieces this repo always had: + +| Path in a layer | Installed to | |---|---| -| `config/` | Mirrors `~/.config`. Only files that differ from Omarchy's shipped defaults. | -| `themes/` | The three hand-built themes, wallpapers included. | -| `plugins.txt` | The 10 Omarchy shell plugins, each pinned to a commit. | -| `plugin-patches/` | Local plugin work that exists on no remote (see below). | -| `bin/` | `omarchy-handoff` and `fix-stuck-displays`. | -| `handoff/` | The lan-mouse source patch that fixes pointer feel, plus example `config.toml`s. Not a `~/.config` mirror. | -| `systemd/user/` | `lan-mouse.service`, `voxtype.service`. | -| `system/` | `/etc` drop-ins: the ARP sysctl and `hid_apple` fnmode. | -| `packages.txt` | Packages beyond the Omarchy baseline, each with a reason. | +| `config/` | `~/.config`, mirrored | +| `themes/` | `~/.config/omarchy/themes` | +| `plugins.txt`, `plugin-patches/` | `~/.config/omarchy/plugins`, pinned; local work replayed | +| `bin/` | `~/.local/bin` | +| `systemd/user/` | user units, plus `.service.d/*.conf` drop-ins | +| `system/` | `/etc`, mirrored (sudo) | +| `handoff/` | the lan-mouse patch and example configs (`install.sh handoff`) | +| `packages.txt` | `omarchy pkg add` / `omarchy pkg aur add` | +| `setup.sh` | run last, for state outside `~/.config` (markers, gsettings, a first-run theme) | -## The customizations, and why +A profile resolves to an ordered list of layers: its `include`s first (depth +first, each once), then any `--roles`, then the profile itself. **For a path +several layers provide, the last one wins**, so a machine overrides the roles +and shared layers beneath it. `./install.sh --show` prints the result. -**Displays** (`config/hypr/monitors.lua`) are the fiddliest part. The file is the -single source of truth for monitor rules on purpose — overlapping rules spread -across several files is what previously made external displays unrecoverable. -Three outputs are named by EDID description rather than connector, so they match -whichever port they land on: the built-in Chimei panel at 1.25x, the LG IPS236 on -HDMI at 1x, and the LG TV through the Anker dock. The TV is deliberately left on -`preferred` (3840x2160@30) — the dock negotiates DP alt-mode with only two lanes, -so forcing 4K60 produced a "monitor has no mode" loop. `GDK_SCALE=1` with a 1.25 -monitor scale; the stock pairing is 2 and `auto`. +| Layer | Used by | What | +|---|---|---| +| `base` | all | `hyprland.lua` with the `hypr/layers/` loader; `bindings.lua` kept at stock | +| `personal` | both laptops | branding, default agent (`claude`), herdr, opencode, voxtype | +| `personal-desktop` | HP | the three hand-built themes, the shell plugins, bar layout, Claude/Herdr bindings | +| `features/handoff` | both laptops | `omarchy-handoff` + patched lan-mouse, its unit and binding | +| `roles/*` | per profile, or `--roles` | packages, and any bindings the role needs | +| `machines/*` | — | displays, input, hardware packages, `/etc` drop-ins, per-machine overrides | -`hyprland.lua` sources `hyprmoncfg-monitors.lua` last so the `hyprmoncfg` daemon's -generated layout wins over anything above it. +**Hyprland settings never share a file.** `base`'s `hyprland.lua` loads every +`~/.config/hypr/layers/*.lua` in name order (Omarchy's own `require_all`, with +reload), after `monitors`/`input`/`bindings`/`looknfeel`/`autostart`. Each +layer ships its own `NN-.lua` there — roles 20s, `personal-desktop` 40, +features 50, machines 80 — and `bindings.lua` stays stock so nothing is bound +twice. `install.sh` warns about a file in `hypr/layers/` that no layer of the +current profile provides (a role you dropped). -`bin/fix-stuck-displays` recovers the related kernel-side failure: after -suspend/resume i915 can leave an unplugged connector "disconnected" but still -"enabled" on a pipe Hyprland has forgotten, which blocks every other output from -modesetting. It forces the connector on, disables it through Hyprland to release -the pipe, then returns it to auto-detect. It needs passwordless sudo for -`tee /sys/class/drm/*/status`. +**Packages** lines are `name [arch...] [cmd=a,b] # why`, in `[repo]`, `[aur]`, +`[optional]` or `[ignore]` sections. An arch limits a line to `x86_64` or +`aarch64` (Arch Linux ARM lacks `opencode`, `openai-codex`, `mise` and +`shellcheck`). `cmd=` skips the package when one of those commands is already +on PATH — on the laptops, `dotfiles`' `bin/` puts most devops and agent CLIs in +`~/.local/bin`, and the Arch package would only duplicate them; on a Coder +desktop, which has no `dotfiles`, the package is the tool. `[ignore]` is for +packages a machine has on purpose but this repo does not manage, so +`capture.sh packages` stays quiet about them. -**Keyboard/mouse sharing** (`bin/omarchy-handoff`, `systemd/user/lan-mouse.service`) -drives `lan-mouse` to share input with the other Omarchy laptop, bound to -`SUPER+SHIFT+K` (`SUPER+K` is already Keybindings). The script's header comment -is the real documentation — it records the non-obvious facts that cost hours: -`capture_backend` must be `layer-shell` (the portal backend is a silent no-op on -Hyprland 0.56.2), `[authorized_fingerprints]` is keyed by fingerprint and the -reversed form fails silently, and the log variable is `LAN_MOUSE_LOG_LEVEL`, not -`RUST_LOG`. It runs as a user unit rather than from `autostart.lua` because -`o.launch_on_start` sends both streams to `/dev/null`, hiding every pairing -failure. +Roles name the `dotfiles` modules they pair with (`dotfiles=(...)`), and +`install.sh` prints them when `~/.dotfiles` is absent. It never runs +`dotfiles` itself: that needs a 1Password token, which a Coder desktop does +not have. + +## Coder desktops + +The image is built by `covert-cloud` +(`platform/workspaces/omarchy-desktop/image/`). Its `provision/user.sh` clones +this repo at a **pinned commit** into `~/.local/share/omarchy-setup` and runs +`install.sh --profile coder-desktop`, which bakes in `base`, `developer` and +the `coder-desktop` layer: + +- one fixed `Virtual-1` monitor at 1920x1080, GDK scale 1 — **must match** + `selkies.service`'s `--manual-width/--manual-height`; the build fails if the + line is missing; +- no window animations, and GTK reduced motion: the VM has no GPU, so every + frame is drawn on the CPU; +- Omarchy's stay-awake mode: the desktop user has no password, so a lock + screen could never be unlocked; the build checks for the marker; +- Retro 82 as the default theme, set once only, so a theme the developer picks + survives restarts. (The image itself installs the Covert Cloud themes.) + +The plumbing stays in `covert-cloud`: Selkies, the Coder agent, the Super key +stand-in, the start page, autologin, and the lockdown (no SSH, no file +transfer, locked password). Nothing from `personal*` or `handoff` goes on a +desktop that other people use. + +**Roles per workspace.** The Coder template has a **Roles** parameter +(multi-select: `devops`, `ai-agents`). At every workspace start it runs +`install.sh --profile coder-desktop --roles --no-clobber`: +idempotent, so an unchanged choice costs a few seconds, and `--no-clobber` +keeps any file the developer has edited. Deselecting a role stops managing +it but does not uninstall its packages; its `hypr/layers/` file is left in place +and reported. + +**Changing what the desktops get** is a commit here plus a pin bump there: +`OMARCHY_SETUP_REF` in `covert-cloud`'s `provision/user.sh` (rebuild the image) +and in the template's `main.tf` (template push; reaches running desktops at +their next start). + +## The MacBook Air M2 + +Captured as it was on 2026-10-16 (Omarchy 4.0.3rc4), read-only over SSH: no +hand-built themes, no shell plugins, stock bar and terminal fonts, so it +includes `personal` but not `personal-desktop`. Add that to its `include=` to +give it the HP's desktop. What is its own: + +- `monitors.lua`: monitor scale 1.33333, GDK scale 1, no named outputs. +- `input.lua`: stock gestures (no three-finger workspace swipe), and the + measured note on why `sensitivity`/`accel_profile` rules are inert for the + lan-mouse virtual pointer. +- `/etc/modprobe.d`: `hid_apple fnmode=1` (the built-in keyboard; the HP uses 2 + for an external Apple keyboard) and `appledrm show_notch=1`. +- `lan-mouse.service.d/pointer-scale.conf`: `LAN_MOUSE_POINTER_SCALE=0.7`, for + the Air's 140 logical ppi against the LG's ~96. +- Its own `herdr/config.toml` (no agent-row customization yet), `opencode.json` + (`"theme": "system"`) and `voxtype/config.toml`. +- The Asahi platform packages, listed so `capture.sh` notices drift. + +Left out: `~/.config/opencode/opencode.gateway.json` (holds the LiteLLM key; +`dotfiles`' `mcp-config` writes it), `~/.config/lan-mouse/`, and the +post-update hooks and `omarchy-asahi-mic.service`, which Omarchy installs +itself. + +## The HP laptop: its customizations, and why + +**Displays** (`profiles/machines/hp-laptop/config/hypr/monitors.lua`) are the +fiddliest part. The file is the single source of truth for monitor rules on +purpose — overlapping rules spread across several files is what previously made +external displays unrecoverable. Three outputs are named by EDID description +rather than connector, so they match whichever port they land on: the built-in +Chimei panel at 1.25x, the LG IPS236 on HDMI at 1x, and the LG TV through the +Anker dock. The TV is deliberately left on `preferred` (3840x2160@30) — the dock +negotiates DP alt-mode with only two lanes, so forcing 4K60 produced a "monitor +has no mode" loop. `GDK_SCALE=1` with a catch-all monitor scale of 1 (the +built-in panel and the TV set 1.25 in their own rules); the stock pairing is 2 +and `auto`. + +`hyprland.lua` sources `hyprmoncfg-monitors.lua` last so the `hyprmoncfg` +daemon's generated layout wins over anything above it. + +`profiles/machines/hp-laptop/bin/fix-stuck-displays` recovers the related +kernel-side failure: after suspend/resume i915 can leave an unplugged connector +"disconnected" but still "enabled" on a pipe Hyprland has forgotten, which +blocks every other output from modesetting. It forces the connector on, disables +it through Hyprland to release the pipe, then returns it to auto-detect. It +needs passwordless sudo for `tee /sys/class/drm/*/status`. + +**Keyboard/mouse sharing** (`profiles/features/handoff/`: `bin/omarchy-handoff`, +`systemd/user/lan-mouse.service`) drives `lan-mouse` to share input with the +other Omarchy laptop (the Air), bound to `SUPER+SHIFT+K` (`SUPER+K` is already +Keybindings). The script's header comment is the real documentation — it records +the non-obvious facts that cost hours: `capture_backend` must be `layer-shell` +(the portal backend is a silent no-op on Hyprland 0.56.2), +`[authorized_fingerprints]` is keyed by fingerprint and the reversed form fails +silently, and the log variable is `LAN_MOUSE_LOG_LEVEL`, not `RUST_LOG`. It runs +as a user unit rather than from `autostart.lua` because `o.launch_on_start` +sends both streams to `/dev/null`, hiding every pairing failure. Three further facts were only established after the above was first written, and they are what actually made it work: @@ -72,7 +190,8 @@ they are what actually made it work: digest is the *correct* digest, valid TOML, non-empty, passes every guard, and never matches: the DTLS handshake dies `bad_certificate`, indistinguishable from a wrong key or a dropped packet. This was the root cause of a full day of - dead crossings. `omarchy-handoff fixcase` repairs it; `doctor` now fails on it. + dead crossings. `omarchy-handoff fixcase` repairs it; `doctor` now fails on + it. - **Authorization is receiver-side only, not mutual.** `connect.rs` sets `insecure_skip_verify`, so the *connecting* side never checks the peer's certificate — only the listener consults `authorized_fingerprints`. Each @@ -80,65 +199,73 @@ they are what actually made it work: needs both lists, and a sender cannot tell whether it is talking to the real peer. Input goes to whatever answers on that IP:port. - **`ufw` is default-deny inbound**, and a dropped inbound 4242 presents - *identically* to a bad certificate. `install.sh --handoff` opens it for the - peer's addresses. Check it first: `journalctl -k -g 'UFW BLOCK'`. + *identically* to a bad certificate. `install.sh handoff` opens it for the + addresses in the profile's `handoff_peer_ips`. Check it first: `journalctl -k + -g 'UFW BLOCK'`. -`handoff/patch/` carries a two-line source patch so the daemon forwards the -accelerated deltas, which is the only way to make the sender's `sensitivity` and -`accel_profile` affect remote pointer feel at all; it also adds -`LAN_MOUSE_POINTER_SCALE` for the residual difference in logical pixel density -between the two screens. `install.sh` builds it per-machine — the two laptops are -different architectures — and `systemd/user/lan-mouse.service` runs the result. +`profiles/features/handoff/handoff/patch/` carries a two-line source patch so +the daemon forwards the accelerated deltas, which is the only way to make the +sender's `sensitivity` and `accel_profile` affect remote pointer feel at all; it +also adds `LAN_MOUSE_POINTER_SCALE` for the residual difference in logical pixel +density between the two screens. `install.sh` builds it per-machine — the two +laptops are different architectures — and `lan-mouse.service` runs the result. +The Air's `LAN_MOUSE_POINTER_SCALE=0.7` is a systemd drop-in in its own layer +(`lan-mouse.service.d/pointer-scale.conf`), so the shared unit stays at 1.0. -`system/sysctl.d/99-omarchy-handoff-arp.conf` goes with it: Wi-Fi and the dock's -ethernet both sit on `192.168.1.0/24`, and with the default ARP settings the -subnet's mapping collapses onto whichever NIC replied last. +`profiles/machines/hp-laptop/system/sysctl.d/99-omarchy-handoff-arp.conf` goes +with it: Wi-Fi and the dock's ethernet both sit on `192.168.1.0/24`, and with +the default ARP settings the subnet's mapping collapses onto whichever NIC +replied last. -**Bar and shell** (`config/omarchy/shell.json`) adds `omarchy.active-window` to -the left, five plugin widgets, and `indie.herdr-hud` as a non-bar plugin. Idle is -stretched a long way past stock — screensaver at 20 minutes, lock at 30, against -2.5 and 5. +**Bar and shell** (`profiles/personal-desktop/config/omarchy/shell.json`) adds +`omarchy.active-window` to the left, five plugin widgets, and `indie.herdr-hud` +as a non-bar plugin. Idle is stretched a long way past stock — screensaver at 20 +minutes, lock at 30, against 2.5 and 5. -**Keybindings** (`config/hypr/bindings.lua`) rebinds `SUPER+SHIFT+A` from the -stock ChatGPT webapp to Claude, and adds four Herdr HUD bindings plus Handoff. +**Keybindings** live in two layer files. +`profiles/personal-desktop/config/hypr/layers/40-personal-desktop.lua` rebinds +`SUPER+SHIFT+A` from the stock ChatGPT webapp to Claude and adds four Herdr HUD +bindings; the handoff feature's `50-handoff.lua` adds Handoff. -**Input** (`config/hypr/input.lua`) enables the three-finger horizontal -workspace gesture that ships commented out, and flattens pointer acceleration for -`unknown-device*`. +**Input** (`profiles/machines/hp-laptop/config/hypr/input.lua`) enables the +three-finger horizontal workspace gesture that ships commented out, and flattens +pointer acceleration for `unknown-device*`. That second rule is **a no-op, and the reasoning originally given for it was wrong.** It was added believing lan-mouse forwards deltas the sending compositor -had already accelerated, which the receiver then amplified a second time. Upstream -does the opposite: it forwards the *unaccelerated* deltas and discards the -accelerated pair in the same event (`handoff/patch/REBUILD.md`). And the rule -cannot act either way — `hl.device({name="unknown-device"})` does match the -virtual pointer (provable by toggling `scroll_factor`), but `sensitivity` and -`accel_profile` are libinput pointer-acceleration settings and a -`wlr-virtual-pointer` is not a libinput device, so they have nothing to act on. -Measured 2026-10-10 on both machines with `lan-mouse test-emulation` + `hyprctl -cursorpos`: travel was 398x198px at `sensitivity = 0` and identical at `-0.9`, -exactly 1:1 with the raw deltas. The lines are kept because they are harmless and -the `unknown-device` naming note is worth preserving; the real fix is -`handoff/patch/`. Do not add sensitivity or accel_profile rules here expecting -them to change remote pointer feel. +had already accelerated, which the receiver then amplified a second time. +Upstream does the opposite: it forwards the *unaccelerated* deltas and discards +the accelerated pair in the same event +(`profiles/features/handoff/handoff/patch/REBUILD.md`). And the rule cannot act +either way — `hl.device({name="unknown-device"})` does match the virtual pointer +(provable by toggling `scroll_factor`), but `sensitivity` and `accel_profile` +are libinput pointer-acceleration settings and a `wlr-virtual-pointer` is not a +libinput device, so they have nothing to act on. Measured 2026-10-10 on both +machines with `lan-mouse test-emulation` + `hyprctl cursorpos`: travel was +398x198px at `sensitivity = 0` and identical at `-0.9`, exactly 1:1 with the raw +deltas. The lines are kept because they are harmless and the `unknown-device` +naming note is worth preserving; the real fix is `handoff/patch/`. Do not add +sensitivity or accel_profile rules here expecting them to change remote pointer +feel. **Terminals** are all dropped to a smaller font (9 -> 8) except ghostty, which went the other way to 12. `shell.toml` sets the bar font base size to 10. -**Branding** (`config/omarchy/branding/`) replaces the about and screensaver -ASCII art. +**Branding** (`profiles/personal/config/omarchy/branding/`, shared with the Air) +replaces the about and screensaver ASCII art. **`hid_apple` fnmode=2** makes the F-keys act as F-keys on an Apple keyboard. It only takes effect after `sudo mkinitcpio -P` or a module reload. ## Local plugin work -`indie.herdr-hud` is the one piece that exists on no remote. Ten commits live on -its `local` branch — touchpad scroll feel, agent ordering, context-size display — -and they stay off the upstream PR deliberately. `plugin-patches/` carries them as -a git bundle plus a patch for the uncommitted working tree, and `install.sh` -replays both after cloning. `io.github.tallsam.navbar-cat` has a one-file working -tree change captured the same way. +`indie.herdr-hud` is the one piece that exists on no remote. Its `local` branch +carries commits — touchpad scroll feel, agent ordering, context-size display — +and they stay off the upstream PR deliberately. +`profiles/personal-desktop/plugin-patches/` carries them as a git bundle plus a +patch for the uncommitted working tree, and `install.sh` replays both after +cloning. Two untracked scripts in the same plugin, `bin/herdr-agents` and +`bin/herdr-web`, ride along as `indie.herdr-hud--bin--`. `capture.sh plugins` re-bundles and re-diffs these, so local work keeps flowing back into the repo. @@ -148,42 +275,62 @@ back into the repo. - **Secrets.** `~/.config/lan-mouse/` holds a private key and per-machine fingerprints; `~/.config/novnc/auth.env`, SSH keys and the git credential helpers are likewise machine-local. Start from - `handoff/config/config.toml.example-{air,laptop}` (which carry the - `capture_backend`, edge and lowercase-fingerprint rules), then pair with - `omarchy-handoff pair ` and `omarchy-handoff doctor`. - A fresh install regenerates the certificate, so this machine's fingerprint - changes and the *peer* has to re-pair. Back up `lan-mouse.pem` beforehand if - you would rather keep it stable — somewhere private, not here. + `profiles/features/handoff/handoff/config/config.toml.example-{air,laptop}` + (which carry the `capture_backend`, edge and lowercase-fingerprint rules), + then pair with `omarchy-handoff pair ` and + `omarchy-handoff doctor`. A fresh install regenerates the certificate, so this + machine's fingerprint changes and the *peer* has to re-pair. Back up + `lan-mouse.pem` beforehand if you would rather keep it stable — somewhere + private, not here. - **Shell, git and dev tooling.** That is already its own repo, `dotfiles` - (`git.nic-oconnor.com/public/dotfiles`), which handles zsh, mise, 1Password and - the `~/.local/bin` tool shims. The only overlap avoided here is + (`git.nic-oconnor.com/public/dotfiles`), which handles zsh, mise, 1Password + and the `~/.local/bin` tool shims. The only overlap avoided here is `~/.config/git/config`, whose customizations are identity and credential helpers — `dotfiles` territory. -- **The remote-access stack.** `wayvnc`, `novnc`, `sunshine`, `freerdp`, `remmina` - and `omarchy-remote-desktop` are installed on this laptop but scoped out. - `kokd.remote-desktop` is still in `plugins.txt` since it is a bar plugin, but - it is not placed in `shell.json`. -- **Work tooling.** The keycloak helpers (`kcadm`, `kclogin`, `wren-keycloak`) and - `paperclipai.service`. +- **The remote-access stack.** `wayvnc`, `novnc`, `sunshine`, `freerdp`, + `remmina` and `omarchy-remote-desktop` are installed on this laptop but scoped + out. `kokd.remote-desktop` is still in `plugins.txt` since it is a bar plugin, + but it is not placed in `shell.json`. +- **Two plugins with no remote yet.** `niccoconnor.mantel` and + `niccoconnor.keyhints` are installed on the HP and placed in `shell.json`, but + neither has an `origin`, so `plugins.txt` cannot pin them (`capture.sh` says + so). Until they are pushed, another machine gets a bar with those two slots + empty. +- **Work tooling.** The keycloak helpers (`kcadm`, `kclogin`, `wren-keycloak`) + and `paperclipai.service`. - **aether-managed themes.** `pink-gt3rs`, `porsche-cayman` and `porsche-rain` carry a `.aether-managed` marker and are reinstallable with `aether`, so their ~18MB of wallpapers and previews stay out. - **`btop.conf`.** Its only diff against stock is v1.4.7 default drift, not a real customization. - **Backup files.** Fifteen `.bak.*` and `.prefix.*` files are sitting in - `~/.config/hypr` and `~/.config/omarchy` from earlier editing sessions. They are - not tracked and are safe to delete. + `~/.config/hypr` and `~/.config/omarchy` from earlier editing sessions. They + are not tracked and are safe to delete. -## Adding a file +## Adding things -`capture.sh` only refreshes what the repo already tracks, so start it by hand: +**A file.** `capture.sh` only refreshes what the repo already tracks, so start +it by hand, in the layer it belongs to: ```bash -cp ~/.config/foo/bar.conf config/foo/bar.conf # path mirrors ~/.config -git add config/foo/bar.conf +cp ~/.config/foo/bar.conf profiles/machines/hp-laptop/config/foo/bar.conf +git add profiles/machines/hp-laptop/config/foo/bar.conf ``` -`install.sh config` and `capture.sh config` both walk `config/` recursively, so -nothing else needs changing. Check first that the file actually differs from -stock — `diff /usr/share/omarchy/config/foo/bar.conf ~/.config/foo/bar.conf` — -or you will be tracking a file Omarchy already ships. +Check first that it actually differs from stock — +`diff /usr/share/omarchy/config/foo/bar.conf ~/.config/foo/bar.conf` — or you +will be tracking a file Omarchy already ships. A Hyprland setting goes in that +layer's `config/hypr/layers/NN-.lua`, not in `bindings.lua` or +`looknfeel.lua`. + +**A role.** `mkdir profiles/roles/`, give it a `profile` (`description=`, +optionally `dotfiles=(...)`) and a `packages.txt`, plus +`config/hypr/layers/2N-.lua` if it needs bindings. Use `cmd=` for any +package `dotfiles` might already provide, and an arch for anything Arch Linux +ARM lacks. To offer it on Coder desktops, add it to the template's Roles +parameter in `covert-cloud`. + +**A machine.** `mkdir profiles/machines/` with a `profile` naming +`hostnames=`, `arch=` and `include=(base ...)`, then its `monitors.lua`, +`input.lua`, `packages.txt` and `/etc` drop-ins. Run `./install.sh --profile + --dry-run` on it, then `./capture.sh` after any hand-tuning. diff --git a/capture.sh b/capture.sh index 86e8b70..1454cbe 100755 --- a/capture.sh +++ b/capture.sh @@ -2,11 +2,14 @@ # Pull the live machine's state back into this repo - the reverse of install.sh. # # Run it after customizing something by hand, then review `git diff` and commit. -# It only refreshes paths the repo already tracks; it never invents new ones, so -# adding a newly customized file is a deliberate act (see README, "Adding a file"). +# Each live file goes back into the layer that OWNS it for this machine's +# profile (the last layer providing that path; `./install.sh --show` lists +# them). It only refreshes paths the repo already tracks, so adding a newly +# customized file is a deliberate act (see README, "Adding a file"). # -# ./capture.sh # refresh everything -# ./capture.sh config # only the named steps +# ./capture.sh # this machine's profile, every step +# ./capture.sh --profile hp-laptop # name it explicitly +# ./capture.sh config # only the named steps # ./capture.sh --list # # Steps: config themes plugins bin services system packages @@ -16,15 +19,19 @@ set -uo pipefail REPO="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" STEPS=(config themes plugins bin services system packages) -c_ch=$'\033[33m'; c_skip=$'\033[2m'; c_warn=$'\033[33m'; c_hd=$'\033[1m'; c_off=$'\033[0m' -[[ -t 1 ]] || { c_ch=; c_skip=; c_warn=; c_hd=; c_off=; } +c_ch=$'\033[33m'; c_skip=$'\033[2m'; c_warn=$'\033[33m'; c_err=$'\033[31m'; c_hd=$'\033[1m'; c_off=$'\033[0m' +[[ -t 1 ]] || { c_ch=; c_skip=; c_warn=; c_err=; c_hd=; c_off=; } say() { printf '%s\n' "$*"; } head_() { printf '\n%s==> %s%s\n' "$c_hd" "$*" "$c_off"; } chg() { printf ' %sM %s%s\n' "$c_ch" "$*" "$c_off"; } skip() { printf ' %s= %s%s\n' "$c_skip" "$*" "$c_off"; } warn() { printf ' %s! %s%s\n' "$c_warn" "$*" "$c_off"; } +err() { printf ' %sx %s%s\n' "$c_err" "$*" "$c_off"; } gone() { printf ' %s? %s%s\n' "$c_warn" "$*" "$c_off"; } +# shellcheck source=lib/layers.sh +source "$REPO/lib/layers.sh" + # pull_back