From 0c5c222f58ba08be1c24b76dc74965f0fd48bab0 Mon Sep 17 00:00:00 2001 From: Carson Seese Date: Tue, 18 Aug 2026 19:56:38 +0000 Subject: [PATCH] Support Omarchy alongside Debian/Ubuntu/Arch on Linux Omarchy owns a large part of $HOME plus its own mise, package-update entrypoint, and login shell, and several of these dotfiles collided with it hard enough to break. Add an `isOmarchy` data flag (a /usr/share/omarchy directory or, on the dev channel, /etc/omarchy.conf) and defer to Omarchy wherever it already provides something. Fixes outright breakage: - install.sh no longer chsh's to fish there. SDDM, /etc/profile.d, and the uwsm session are bash scripts, and chsh'ing to fish breaks the graphical login. ~/.bashrc execs fish for interactive terminals instead, which is what omarchy-setup-fish does -- but chezmoi owns ~/.bashrc, so that command must not be run. - install.sh's bootstrap step and `update` both ran `pacman -Syu`, which Omarchy's 00-omarchy-update-guard.hook libalpm hook aborts. The former now uses omarchy-pkg-add; the latter hands the whole system-package step to `omarchy-update -y`, which also covers mise itself and mise's tools. It runs last because it owns the reboot prompt. - `mise self-update` is tolerated rather than fatal. Omarchy installs mise-bin, which ships mise's self-update marker and exits nonzero, so every `chezmoi apply` was failing on that script. Same fix covers Arch's mise and Homebrew. - `clean` trims the package cache with `paccache -rk2` instead of emptying it with `pacman -Sc`: Omarchy treats the cache as its only offline downgrade path and keeps two versions per package. Splits mise config ownership: - The tool config moves from ~/.config/mise/config.toml to a conf.d/10-dotfiles.toml drop-in. Omarchy provisions its lazy tool stubs with `mise use -g`, which writes config.toml, so chezmoi owning that file made the two clobber each other on every apply. mise structurally refuses to write into conf.d. Note the precedence trade-off: config.toml outranks conf.d. - A run_once script moves a pre-existing dotfiles-owned config.toml aside (config.toml outranks conf.d, so a leftover copy would keep winning). It recognises the old file by its minimum_release_age line so a `mise use -g` config survives untouched. - Tools Omarchy already ships are dropped there: starship, lazygit, lazydocker, fd, fzf, jq, ripgrep, zoxide, usage and herdr as pacman packages, gh and hunk as Omarchy's ~/.local/bin mise stubs. A duplicate herdr is the worst of these -- Omarchy has a migration that deletes mise copies because a stale client shadows /usr/bin/herdr with an older wire protocol. Shell and terminal integration: - ~/.bashrc sources Omarchy's default/bash/env-bootstrap above the interactivity guard (OMARCHY_PATH and the PATH entries non-interactive shells and the uwsm session need), then default/bash/rc, and skips our duplicate starship/mise/zoxide/mcfly activations and history settings. - install.sh installs omarchy-fish and ttf-firacode-nerd. omarchy-fish vendors fzf.fish, so the fundle copy is dropped to stop the two fighting over the same keybindings; conf.d/{mise,zoxide,starship}.fish now check for the function their activation defines so they don't spawn a redundant subprocess on every shell start. - The ghostty config becomes a template that follows Omarchy's theme system (config-file = ?".../current/theme/ghostty.conf") instead of pinning a theme, so the terminal stays in sync with the rest of the desktop. font-family stays unquoted on purpose: omarchy-font-set only rewrites a quoted one, which keeps chezmoi the source of truth. Renders are byte-identical to before on non-Omarchy hosts. Verified with chezmoi init --apply into a throwaway HOME both with and without a /usr/share/omarchy present, including the config.toml migration in both directions, `update`/`clean` branch selection against command stubs, the bash-to-fish handoff guards, and the fish activation guards under real fish. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01CXzpqB5fEr7ASNjLz7AjKT --- AGENTS.md | 62 ++++++++++++++++++- README.md | 8 ++- install.sh | 27 +++++++- root/.chezmoi.toml.tmpl | 11 ++++ .../run_after_mise-update.sh.tmpl | 10 ++- ...nce_before_00-migrate-mise-config.ps1.tmpl | 14 +++++ ...once_before_00-migrate-mise-config.sh.tmpl | 23 +++++++ ...un_onchange_after_01-mise-install.ps1.tmpl | 2 +- ...run_onchange_after_01-mise-install.sh.tmpl | 2 +- ...hange_after_02-install-completions.sh.tmpl | 2 +- root/dot_bashrc.tmpl | 38 ++++++++++-- root/dot_local/bin/executable_clean | 16 +++-- root/dot_local/bin/executable_update | 51 ++++++++++----- root/private_dot_config/fish/conf.d/mise.fish | 8 ++- .../fish/conf.d/starship.fish | 3 +- .../fish/conf.d/zoxide.fish | 3 +- root/private_dot_config/fish/config.fish.tmpl | 5 ++ root/private_dot_config/ghostty/config | 14 ----- root/private_dot_config/ghostty/config.tmpl | 26 ++++++++ .../10-dotfiles.toml.tmpl} | 38 +++++++++--- 20 files changed, 300 insertions(+), 63 deletions(-) create mode 100644 root/.chezmoiscripts/run_once_before_00-migrate-mise-config.ps1.tmpl create mode 100644 root/.chezmoiscripts/run_once_before_00-migrate-mise-config.sh.tmpl delete mode 100644 root/private_dot_config/ghostty/config create mode 100644 root/private_dot_config/ghostty/config.tmpl rename root/private_dot_config/mise/{config.toml.tmpl => conf.d/10-dotfiles.toml.tmpl} (54%) diff --git a/AGENTS.md b/AGENTS.md index bce6b1b..87b1d37 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,8 +19,8 @@ This is a personal dotfiles repository managed with [chezmoi](https://www.chezmo Shell-agnostic commands applied by chezmoi to `~/.local/bin` (source: `root/dot_local/bin/`), so one bash implementation serves fish, bash, and zsh. Not applied on Windows (ignored via `.chezmoiignore.tmpl`): -- `update` - Updates everything: system packages (apt or pacman on Linux, brew on macOS), mise itself, chezmoi, then `chezmoi update` (pull latest dotfiles + apply), then `mise upgrade` for the freshly pulled tool pins -- `clean` - Prunes what `update` leaves behind: orphaned system packages and caches (`apt-get autoremove --purge`/`clean`, `pacman -Rns` of `-Qtdq` orphans + `-Sc`, `brew autoremove`/`cleanup --prune=all`), mise tool versions no longer referenced by any config (`mise prune`), and mise's download cache +- `update` - Updates everything: system packages (apt or pacman on Linux, brew on macOS), mise itself, chezmoi, then `chezmoi update` (pull latest dotfiles + apply), then `mise upgrade` for the freshly pulled tool pins. On Omarchy the system-package and mise-self-update steps hand off to `omarchy-update -y`, which runs last (see [Omarchy](#omarchy)) +- `clean` - Prunes what `update` leaves behind: orphaned system packages and caches (`apt-get autoremove --purge`/`clean`, `pacman -Rns` of `-Qtdq` orphans + `-Sc`, `brew autoremove`/`cleanup --prune=all`), mise tool versions no longer referenced by any config (`mise prune`), and mise's download cache. On Omarchy the cache is trimmed with `paccache -rk2` rather than emptied - `set-work-email ` - Writes `~/work.email` and re-runs `chezmoi init --apply` so the work identity takes effect (Windows gets a PowerShell function equivalent in the profile) ## Architecture @@ -38,6 +38,7 @@ The dotfiles adapt based on environment variables: - `REMOTE_CONTAINERS_IPC` - Detects dev container environment - `CODESPACES` - Detects GitHub Codespaces environment - A `/workspaces` directory - Also treated as a dev container signal (matches install.sh; covers containers whose user isn't `vscode` and where `REMOTE_CONTAINERS_IPC` isn't set at install time) +- A `/usr/share/omarchy` directory or `/etc/omarchy.conf` file - Sets `isOmarchy` (see [Omarchy](#omarchy)); the latter covers Omarchy's dev channel, where `OMARCHY_PATH` is a git checkout instead - `DOTFILES_SOURCE_DIR` - Override source directory (defaults to current directory for install.sh) - `DOTFILES_WORK_EMAIL` - Work email fallback when `~/work.email` is absent (the file wins; Codespaces user secrets surface as env vars) - `DOTFILES_WORK_EMAIL_FILE` - Relocates the work-email file from `~/work.email` (used by render.sh; unset in normal use) @@ -76,13 +77,17 @@ On real hosts (WSL, VMs, bare metal), `install.sh` runs `scripts/generate-signin ### Tool Management -CLI tools are managed by [mise](https://mise.jdx.dev/) via `root/private_dot_config/mise/config.toml.tmpl`. Language SDKs (go, node, bun) are conditionally included only outside dev containers (`not .isDc`). Tools are installed automatically during `chezmoi apply` via the `run_onchange_after_01-mise-install.sh.tmpl` script. +CLI tools are managed by [mise](https://mise.jdx.dev/) via `root/private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl`. Language SDKs (go, node, bun) are conditionally included only outside dev containers (`not .isDc`), and tools Omarchy already provides are excluded on Omarchy (`not .isOmarchy`). Tools are installed automatically during `chezmoi apply` via the `run_onchange_after_01-mise-install.sh.tmpl` script. + +It is a **`conf.d` drop-in, not `~/.config/mise/config.toml`**, so that `mise use -g` keeps a file of its own to write to. mise structurally refuses to write into `conf.d`, so neither side clobbers the other. The trade-off is precedence: within a global config dir `conf.d` is the *lowest* precedence, so `config.toml` wins wherever both name the same tool (on Omarchy that includes `node`, which its installer pins with `mise use -g`). `run_once_before_00-migrate-mise-config.sh.tmpl` moves a pre-existing dotfiles-owned `config.toml` aside once per machine; it leaves a `mise use -g` one alone. ### Chezmoi Automation Scripts Post-install scripts in `root/.chezmoiscripts/` run automatically during `chezmoi apply`: +- `run_once_before_00-migrate-mise-config.sh.tmpl` - One-time move of a former dotfiles-owned `~/.config/mise/config.toml` to `config.toml.pre-conf.d.bak` (see Tool Management); recognises the old file by its `minimum_release_age` line so an Omarchy/`mise use -g` config survives - `run_onchange_after_01-mise-install.sh.tmpl` - Installs mise tools when config changes +- `run_after_mise-update.sh.tmpl` - Runs `mise self-update`. Tolerates failure: package-manager builds (Omarchy's `mise-bin`, Arch's `mise`, Homebrew) ship mise's self-update marker and exit nonzero, and updating mise there belongs to the packager - `run_after_install-claude-config.sh.tmpl` - Syncs Claude Code configuration - `run_onchange_after_02-install-completions.sh.tmpl` - Generates fish completions for every installed tool that supports it (gh, docker, mise, rg, fd, ast-grep, zellij, herdr, starship, pnpm); re-runs when the mise config changes so completions track tool versions. worktrunk uses dynamic completions instead (`conf.d/wt.fish` sources `COMPLETE=fish wt` at shell startup) - `run_onchange_after_04-claude-plugins.sh.tmpl` - Installs/enables Claude Code plugins when the plugin list changes (see Claude Plugin Management) @@ -118,6 +123,56 @@ picks the plugins up on the next apply. Hand-authored skills under `dot_claude/skills/` are unrelated — those are files applied directly to `~/.claude/skills/`. +### Omarchy + +[Omarchy](https://omarchy.org) is an opinionated Arch/Hyprland setup that owns a +large part of `$HOME`, so the templates defer to it rather than fight it. The +`isOmarchy` data flag (see Environment Detection) gates every branch; `install.sh` +mirrors it with `IS_OMARCHY`. Like `isDc` and the identity, it is resolved at +`chezmoi init` time — installing Omarchy afterwards needs a re-`init` +(`set-work-email` does one, or `chezmoi init --apply`). + +What changes on Omarchy, and why: + +| Area | Behavior | Reason | +| --- | --- | --- | +| **Login shell** (`install.sh`) | No `chsh`; `~/.bashrc` execs fish for interactive terminals instead | SDDM, `/etc/profile.d`, and the uwsm session are bash scripts. `chsh -s fish` breaks the graphical login. This is what `omarchy-setup-fish` does — but do **not** run that command, it overwrites the chezmoi-managed `~/.bashrc` | +| **`~/.bashrc`** | Sources Omarchy's `default/bash/env-bootstrap` above the interactivity guard, then `default/bash/rc`; our own starship/mise/zoxide/mcfly activations and history settings are skipped | `env-bootstrap` sets `OMARCHY_PATH` and the mise-shims/`~/.local/bin` PATH entries that non-interactive shells and the uwsm session need; `default/bash/rc` already activates those tools | +| **Bootstrap packages** (`install.sh`) | `omarchy-pkg-add` instead of `pacman -Syu`, plus `omarchy-fish` and `ttf-firacode-nerd` | A libalpm hook (`00-omarchy-update-guard.hook`) aborts direct system upgrades. `omarchy-fish` vendors Omarchy's fish config; Omarchy ships JetBrainsMono Nerd, not FiraCode | +| **fish** | `fundle plugin 'PatrickF1/fzf.fish'` is dropped; `conf.d/{mise,zoxide,starship}.fish` skip activation when something already did it | `omarchy-fish` vendors fzf.fish (so fundle's copy would be a second install fighting over the same keybindings) and activates mise/zoxide/starship from `/usr/share/fish/vendor_conf.d`. Re-activating those three is harmless but spawns a redundant subprocess on every shell start, so each checks for the function its activation defines (`mise`, `__zoxide_z`, `__starship_set_job_count`) — per-shell state, so a nested fish still activates. omarchy-fish also turns on `fish_vi_key_bindings`; override in `config.fish`, which runs after all `conf.d` files | +| **mise config** | Lives in `conf.d/10-dotfiles.toml`, and the tools Omarchy provides are omitted | See Tool Management. Omitted: `starship`, `lazygit`, `lazydocker`, `fd`, `fzf`, `jq`, `ripgrep`, `zoxide`, `usage`, `herdr` (pacman) and `gh`, `hunk` (Omarchy's lazy `~/.local/bin` mise stubs). A duplicate `herdr` is the worst of these — Omarchy has a migration that deletes mise copies because a stale client shadows `/usr/bin/herdr` with an older wire protocol | +| **`mise self-update`** | Tolerated failure, not an error | Omarchy installs `mise-bin`, which ships mise's self-update marker | +| **`update`** | System packages, `mise self-update`, and orphan pruning hand off to `omarchy-update -y`, which runs **last** | Omarchy's entrypoint pairs the upgrade with a Snapper snapshot, keyring refresh, migrations, AUR packages, and `mise up`. It runs last because it owns the reboot prompt, and a confirmed reboot would otherwise skip the dotfiles steps | +| **`clean`** | `paccache -rk2` instead of `pacman -Sc` | Omarchy treats the package cache as its only offline downgrade path and keeps two versions per package (`omarchy-update-pkg-prune`) | +| **`~/.config/ghostty/config`** | `config-file = ?"~/.local/state/omarchy/current/theme/ghostty.conf"` instead of a pinned `theme` | `omarchy-theme-set` themes every app together; a pinned theme leaves the terminal visibly out of sync. The theme file sets colors only, so the rest of the config still applies | + +Known overlaps left alone deliberately: + +- **`~/.config/starship.toml`, `~/.config/herdr/config.toml`** replace Omarchy's. + Omarchy documents `~/.config` as the user's, and only *seeds* the herdr config + when absent, so this is the intended direction. Omarchy's herdr config mirrors + its tmux keybindings; ours does not. +- **`~/.gitconfig`** is read *after* Omarchy's `~/.config/git/config`, so + anything we don't set is inherited from it — notably + `init.defaultBranch = master`, `column.ui`, `branch.sort`, `tag.sort`. +- **`~/.local/bin/claude`** is installed natively by `install.sh` on every + platform, but Omarchy also ships a lazy mise stub there, and + `omarchy-refresh-applications` (which some migrations run) restores it. Both + yield a working `claude`. `omarchy remove preinstalls` is Omarchy's own way to + drop its agent stubs for good. +- **`font-family` in the ghostty config is unquoted on purpose.** + `omarchy-font-set` only rewrites a *quoted* `font-family`, so leaving it bare + keeps chezmoi the source of truth for the terminal font (`omarchy font set` + will not change ghostty). +- **`paranoid = true`** in the mise settings applies to Omarchy's `mise use -g` + installs too. It blocks short-name community-plugin installs and requires + project configs to be re-trusted when their contents change. +- **`VISUAL=nvim`** (from `.chezmoitemplates/env-fish`) wins over Omarchy's + `EDITOR=omarchy-launch-editor --inline` in tools that prefer `VISUAL`. Omarchy's + own menu flows call `omarchy-launch-editor` directly, so they are unaffected. + `BROWSER` is deliberately never exported session-wide by Omarchy, and + omarchy-fish does not set it, so it is unset in fish sessions. + ### Environment Variables Simple key-value environment variables (e.g., `VISUAL`, `HOMEBREW_NO_AUTO_UPDATE`) are centralized in template partials under `root/.chezmoitemplates/`. Each partial renders the same variables in the syntax for its target shell: @@ -145,6 +200,7 @@ On Windows, `run_onchange_after_03-windows-env.ps1.tmpl` persists env vars from - `root/.chezmoi.toml.tmpl` - Main chezmoi configuration with environment variables - `root/dot_gitconfig.tmpl` - Git configuration with conditional work includes - `root/dot_gitconfig-work.tmpl` - Work-specific git configuration +- `root/private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl` - mise tool pins and settings (a conf.d drop-in; see Tool Management) - `root/private_dot_config/fish/config.fish.tmpl` - Fish shell configuration - `root/dot_bashrc.tmpl` - Bash configuration (Homebrew, starship, mise, zoxide, mcfly) - `root/dot_zshrc.tmpl` - Zsh configuration diff --git a/README.md b/README.md index bc4c204..7c46c9a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ My personal dotfiles, managed with [chezmoi](https://www.chezmoi.io/). ## Install -### Linux (Debian/Ubuntu/Arch) — dev containers, WSL, cloud VMs +### Linux (Debian/Ubuntu/Arch/Omarchy) — dev containers, WSL, cloud VMs Clone and run the bootstrap script. It installs mise, chezmoi, fish, and Claude Code, then applies the dotfiles: @@ -14,6 +14,12 @@ git clone https://github.com/CS-5/dotfiles.git ~/.local/share/chezmoi ~/.local/share/chezmoi/install.sh [--work-email
] ``` +On [Omarchy](https://omarchy.org) the same command adapts to what Omarchy +already owns: it leaves bash as the login shell (`~/.bashrc` execs fish +instead), installs `omarchy-fish`, keeps out of `~/.config/mise/config.toml`, +skips tools Omarchy already packages, and routes `update` through +`omarchy update`. See [AGENTS.md](AGENTS.md#omarchy) for the details. + The work identity comes from a single file, `~/work.email`, containing just your work email address; its domain selects the identity (e.g. `…@kirbtech.com`). No file means a personal machine. `--work-email` writes that file before applying; diff --git a/install.sh b/install.sh index 646ec7e..3d3508d 100755 --- a/install.sh +++ b/install.sh @@ -28,6 +28,15 @@ if [[ -n "${REMOTE_CONTAINERS_IPC:-}" || "${USER:-}" == "vscode" || "${CODESPACE IS_DC=true fi +# Omarchy already owns package updates, mise, and the login shell, so several +# steps below hand off to it instead of doing their own thing. Matches the +# isOmarchy detection in .chezmoi.toml.tmpl (/etc/omarchy.conf is the dev +# channel, where OMARCHY_PATH is a git checkout instead of /usr/share/omarchy). +IS_OMARCHY=false +if [[ -d /usr/share/omarchy || -f /etc/omarchy.conf ]]; then + IS_OMARCHY=true +fi + # Work identity is detected from ~/work.email at chezmoi render time. Pass # --work-email to write that file here (non-interactive provisioning); leave it # unset to keep any existing ~/work.email (no file => personal identity). @@ -73,6 +82,15 @@ BOOTSTRAP_PKGS=(curl git wget unzip gnupg fish neovim) if command -v apt-get >/dev/null 2>&1; then sudo apt-get update sudo apt-get install -y "${BOOTSTRAP_PKGS[@]}" +elif [[ "$IS_OMARCHY" == "true" ]]; then + # Not `pacman -Syu`: a libalpm hook aborts direct system upgrades, because + # `omarchy update` owns the snapshot and migrations that go with them. + # omarchy-pkg-add is Omarchy's own `pacman -S --needed`, and it verifies + # afterwards that every package actually landed. + # + # omarchy-fish puts Omarchy's fish config (and fzf.fish) in fish's vendor + # dirs; ttf-firacode-nerd is our terminal font, which Omarchy doesn't ship. + omarchy-pkg-add "${BOOTSTRAP_PKGS[@]}" omarchy-fish ttf-firacode-nerd elif command -v pacman >/dev/null 2>&1; then # -Syu, not -Sy: Arch doesn't support partial upgrades, so installing # against a synced-but-not-upgraded system can break shared libs. @@ -121,7 +139,14 @@ log_success "Dotfiles installed and applied" #### Shell #### show_progress "Setting up shell" fish -c "fundle install" -if [[ "${SHELL:-}" != *"fish"* ]]; then +if [[ "$IS_OMARCHY" == "true" ]]; then + # Omarchy keeps bash as the login shell on purpose: SDDM, /etc/profile.d, + # and the uwsm session are bash, and chsh'ing to fish breaks the graphical + # login. ~/.bashrc execs fish for interactive terminals instead, which is + # what omarchy-setup-fish does -- but chezmoi owns ~/.bashrc here, so that + # command must not be run (it would overwrite the applied file). + log_info "Omarchy: leaving bash as the login shell (~/.bashrc execs fish)" +elif [[ "${SHELL:-}" != *"fish"* ]]; then log_info "Changing default shell to fish" sudo chsh -s "$(which fish)" "${USER:-$(id -un)}" fi diff --git a/root/.chezmoi.toml.tmpl b/root/.chezmoi.toml.tmpl index 6559d03..d5bcdaf 100644 --- a/root/.chezmoi.toml.tmpl +++ b/root/.chezmoi.toml.tmpl @@ -5,6 +5,16 @@ sourceDir = {{ env "DOTFILES_SOURCE_DIR" | default "~/.local/share/chezmoi" | qu {{ $isDc := or $probablyDc $isCodespaces -}} {{ $binDir := ".local/bin" -}} +{{/* Omarchy (https://omarchy.org) is an opinionated Arch/Hyprland setup that + owns a large part of $HOME and /usr/share/omarchy. It ships its own mise + (the mise-bin package, whose self-update is disabled), writes + ~/.config/mise/config.toml through `mise use -g`, blocks direct + `pacman -Syu`, and keeps bash as the login shell on purpose. The templates + branch on this flag rather than fighting any of it. + /etc/omarchy.conf only exists on the dev channel, where OMARCHY_PATH points + at a git checkout instead of /usr/share/omarchy. */}} +{{ $isOmarchy := or (stat "/usr/share/omarchy" | not | not) (stat "/etc/omarchy.conf" | not | not) -}} + {{/* Work identity is driven by ~/work.email: a file whose sole contents are one work email address. When the file is absent, the DOTFILES_WORK_EMAIL env var is consulted as a fallback (Codespaces @@ -64,6 +74,7 @@ sourceDir = {{ env "DOTFILES_SOURCE_DIR" | default "~/.local/share/chezmoi" | qu isWindows = {{ eq .chezmoi.os "windows" }} isWsl = {{ env "WSL_DISTRO_NAME" | not | not }} isDc = {{ $isDc }} + isOmarchy = {{ $isOmarchy }} # Identity identity = {{ $identity | quote }} diff --git a/root/.chezmoiscripts/run_after_mise-update.sh.tmpl b/root/.chezmoiscripts/run_after_mise-update.sh.tmpl index bbae924..e206ad2 100644 --- a/root/.chezmoiscripts/run_after_mise-update.sh.tmpl +++ b/root/.chezmoiscripts/run_after_mise-update.sh.tmpl @@ -6,5 +6,11 @@ if ! command -v mise >/dev/null 2>&1; then exit 0 fi -mise self-update --yes --quiet >/dev/null -{{- end }} \ No newline at end of file +# Package-manager builds ship mise's self-update marker and exit nonzero +# ("mise is installed via a package manager, cannot update") — Omarchy's +# mise-bin, Arch's mise, Homebrew. Updating mise there belongs to the packager, +# so don't fail the whole apply over it. mise prints its own reason on stderr. +if ! mise self-update --yes --quiet >/dev/null; then + echo "mise self-update unavailable or failed, leaving mise to its packager" +fi +{{- end }} diff --git a/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.ps1.tmpl b/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.ps1.tmpl new file mode 100644 index 0000000..14a96d1 --- /dev/null +++ b/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.ps1.tmpl @@ -0,0 +1,14 @@ +{{ if eq .chezmoi.os "windows" -}} +# Windows half of run_once_before_00-migrate-mise-config.sh.tmpl: these dotfiles +# used to own ~/.config/mise/config.toml and now ship +# .config/mise/conf.d/10-dotfiles.toml instead. chezmoi does not delete a target +# whose source file went away, and config.toml outranks conf.d, so a leftover +# copy would keep winning. Only a config.toml that is recognisably the old +# dotfiles one is moved aside; anything `mise use -g` wrote must survive. +$config = Join-Path $HOME ".config" "mise" "config.toml" +if ((Test-Path -LiteralPath $config) -and + (Select-String -LiteralPath $config -Pattern '^minimum_release_age = ' -Quiet)) { + Move-Item -LiteralPath $config -Destination "$config.pre-conf.d.bak" -Force + Write-Host "Moved former dotfiles mise config to $config.pre-conf.d.bak" +} +{{- end }} diff --git a/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.sh.tmpl b/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.sh.tmpl new file mode 100644 index 0000000..6725321 --- /dev/null +++ b/root/.chezmoiscripts/run_once_before_00-migrate-mise-config.sh.tmpl @@ -0,0 +1,23 @@ +{{ if ne .chezmoi.os "windows" -}} +#!/bin/bash + +# One-time migration: these dotfiles used to own ~/.config/mise/config.toml and +# now ship .config/mise/conf.d/10-dotfiles.toml instead. chezmoi does not delete +# a target whose source file went away, and config.toml *outranks* conf.d, so a +# leftover copy would keep winning with whatever versions it was last applied +# with. +# +# Only a config.toml that is recognisably the old dotfiles one is moved aside: +# on Omarchy that file belongs to `mise use -g` (its lazy tool stubs write +# there), and it must survive untouched. Renamed rather than deleted — mise +# reads a fixed set of filenames, so the backup is inert but recoverable. + +set -eufo pipefail + +config="{{ .chezmoi.homeDir }}/.config/mise/config.toml" + +if [[ -f $config ]] && grep -q '^minimum_release_age = ' "$config"; then + mv -- "$config" "$config.pre-conf.d.bak" + echo "Moved former dotfiles mise config to $config.pre-conf.d.bak" +fi +{{- end }} diff --git a/root/.chezmoiscripts/run_onchange_after_01-mise-install.ps1.tmpl b/root/.chezmoiscripts/run_onchange_after_01-mise-install.ps1.tmpl index 9d4a2f2..c37f547 100644 --- a/root/.chezmoiscripts/run_onchange_after_01-mise-install.ps1.tmpl +++ b/root/.chezmoiscripts/run_onchange_after_01-mise-install.ps1.tmpl @@ -1,5 +1,5 @@ {{ if eq .chezmoi.os "windows" -}} -# mise config hash: {{ include "private_dot_config/mise/config.toml.tmpl" | sha256sum }} +# mise config hash: {{ include "private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl" | sha256sum }} if (Get-Command mise -ErrorAction SilentlyContinue) { mise install --yes --quiet diff --git a/root/.chezmoiscripts/run_onchange_after_01-mise-install.sh.tmpl b/root/.chezmoiscripts/run_onchange_after_01-mise-install.sh.tmpl index 3561252..60b4ecd 100644 --- a/root/.chezmoiscripts/run_onchange_after_01-mise-install.sh.tmpl +++ b/root/.chezmoiscripts/run_onchange_after_01-mise-install.sh.tmpl @@ -1,7 +1,7 @@ {{ if ne .chezmoi.os "windows" -}} #!/bin/bash -# mise config hash: {{ include "private_dot_config/mise/config.toml.tmpl" | sha256sum }} +# mise config hash: {{ include "private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl" | sha256sum }} if ! command -v mise >/dev/null 2>&1; then echo "mise not found, skipping tool install" diff --git a/root/.chezmoiscripts/run_onchange_after_02-install-completions.sh.tmpl b/root/.chezmoiscripts/run_onchange_after_02-install-completions.sh.tmpl index e463a06..7563c18 100644 --- a/root/.chezmoiscripts/run_onchange_after_02-install-completions.sh.tmpl +++ b/root/.chezmoiscripts/run_onchange_after_02-install-completions.sh.tmpl @@ -3,7 +3,7 @@ # Regenerate whenever any pinned tool version changes, not just when this # script changes — otherwise completions go stale as tools upgrade. -# mise config hash: {{ include "private_dot_config/mise/config.toml.tmpl" | sha256sum }} +# mise config hash: {{ include "private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl" | sha256sum }} # Freshly mise-installed tools aren't on this non-interactive script's PATH # during the first apply (01-mise-install runs just before this); the shims diff --git a/root/dot_bashrc.tmpl b/root/dot_bashrc.tmpl index e90bae4..4c177fd 100644 --- a/root/dot_bashrc.tmpl +++ b/root/dot_bashrc.tmpl @@ -1,4 +1,31 @@ -{{ if .isMac -}} +{{ if .isOmarchy -}} +# Omarchy owns the bash side of this machine, and bash stays the login shell on +# purpose: SDDM, /etc/profile.d, and the uwsm session are all bash. env-bootstrap +# (OMARCHY_PATH, plus the mise-shims and ~/.local/bin entries on PATH) has to run +# even for non-interactive shells, so it goes above the interactivity guard. +# Mirrors Omarchy's own /etc/skel/.bashrc. +[[ -r /usr/share/omarchy/default/bash/env-bootstrap ]] && + source /usr/share/omarchy/default/bash/env-bootstrap + +# If not running interactively, don't do anything else. +[[ $- != *i* ]] && return + +# Hand interactive terminals to fish the way omarchy-setup-fish does, rather +# than chsh'ing the login shell out from under the session. The guards keep +# `bash -c`, nested shells, and a fish parent from re-entering. +if command -v fish >/dev/null 2>&1 && + [[ $(ps --no-header --pid=$PPID --format=comm) != "fish" ]] && + [[ -z ${BASH_EXECUTION_STRING:-} && ${SHLVL} == 1 ]]; then + shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION='' + exec fish $LOGIN_OPTION +fi + +# Omarchy's aliases, functions, history settings, and tool activations +# (mise, starship, zoxide, fzf). Overrides for these belong below. +[[ -r "${OMARCHY_PATH:-/usr/share/omarchy}/default/bash/rc" ]] && + source "${OMARCHY_PATH:-/usr/share/omarchy}/default/bash/rc" +{{ else }} +{{- if .isMac -}} # Homebrew completion (macOS) if command -v brew >/dev/null 2>&1; then eval "$(brew shellenv)" @@ -7,10 +34,10 @@ if command -v brew >/dev/null 2>&1; then fi fi {{- end }} - +{{ end }} export PATH="{{ .binDirAbs }}:$PATH" {{ template "env-posix" . }} - +{{ if not .isOmarchy }} # Initialize starship prompt if command -v starship >/dev/null 2>&1; then eval "$(starship init bash)" @@ -28,13 +55,13 @@ fi if command -v mcfly >/dev/null 2>&1; then eval "$(mcfly init bash)" fi - +{{ end }} [[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path bash)" fzkill() { ps -eo pid,args,%mem,%cpu | fzf --multi --header-lines=1 | awk '{print $1}' | xargs kill "$@" } - +{{ if not .isOmarchy }} # History settings HISTFILE=~/.bash_history HISTSIZE=20000 @@ -42,3 +69,4 @@ HISTFILESIZE=20000 HISTCONTROL=ignoreboth:erasedups shopt -s histappend shopt -s cmdhist +{{- end }} diff --git a/root/dot_local/bin/executable_clean b/root/dot_local/bin/executable_clean index 43ae7ab..95beed4 100644 --- a/root/dot_local/bin/executable_clean +++ b/root/dot_local/bin/executable_clean @@ -1,8 +1,8 @@ #!/bin/bash # Prune old and unused packages left behind by `update`: orphaned system -# packages and package caches (apt/brew), and mise tool versions no longer -# referenced by any config, plus mise's download cache. +# packages and package caches (apt/pacman/brew), and mise tool versions no +# longer referenced by any config, plus mise's download cache. # # Managed by chezmoi; installed to ~/.local/bin so it works from any shell. @@ -30,8 +30,16 @@ Linux) # shellcheck disable=SC2086 # orphan names are word-split on purpose sudo pacman -Rns --noconfirm $orphans fi - # Drop cached packages for anything no longer installed. - sudo pacman -Sc --noconfirm + # Omarchy treats the package cache as its only offline downgrade path + # and trims it to two versions per package rather than emptying it + # (omarchy-update-pkg-prune); paccache trims to the same depth. + # Elsewhere, drop cached packages for anything no longer installed. + if command -v omarchy-update >/dev/null 2>&1 && + command -v paccache >/dev/null 2>&1; then + sudo paccache -rk2 + else + sudo pacman -Sc --noconfirm + fi fi ;; esac diff --git a/root/dot_local/bin/executable_update b/root/dot_local/bin/executable_update index 0864a66..8380b70 100644 --- a/root/dot_local/bin/executable_update +++ b/root/dot_local/bin/executable_update @@ -1,6 +1,6 @@ #!/bin/bash -# Update everything on this machine: system packages (apt/brew), mise, +# Update everything on this machine: system packages (apt/pacman/brew), mise, # chezmoi, the dotfiles themselves, and mise-managed tools. # # Managed by chezmoi; installed to ~/.local/bin so it works from any shell. @@ -9,27 +9,36 @@ set -eufo pipefail step() { printf '\n\033[1;34m==>\033[0m \033[1m%s\033[0m\n' "$1"; } +# Omarchy owns system updates end to end: a libalpm hook aborts any direct +# `pacman -Syu`, and its own entrypoint pairs the upgrade with a Snapper +# snapshot, keyring refresh, migrations, AUR packages, and mise. +is_omarchy() { command -v omarchy-update >/dev/null 2>&1; } + # Everything lives inside main so bash parses the whole script before any of # it runs: `chezmoi update` below re-applies the dotfiles, which can rewrite # this very file mid-execution. main() { - step "Updating system packages" - case "$(uname -s)" in - Darwin) - brew update && brew upgrade - ;; - Linux) - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update && sudo apt-get upgrade -y - elif command -v pacman >/dev/null 2>&1; then - sudo pacman -Syu --noconfirm - fi - ;; - esac + if ! is_omarchy; then + step "Updating system packages" + case "$(uname -s)" in + Darwin) + brew update && brew upgrade + ;; + Linux) + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get upgrade -y + elif command -v pacman >/dev/null 2>&1; then + sudo pacman -Syu --noconfirm + fi + ;; + esac - if command -v mise >/dev/null 2>&1; then - step "Updating mise" - mise self-update --yes + if command -v mise >/dev/null 2>&1; then + step "Updating mise" + # Package-manager builds (Arch, Homebrew) disable self-update and + # exit nonzero; the step above already upgraded those. + mise self-update --yes || echo "mise self-update unavailable" + fi fi # Upgrade the chezmoi binary before pulling the dotfiles: newer templates @@ -46,6 +55,14 @@ main() { mise upgrade fi + # Last on Omarchy, because it owns the reboot prompt: a confirmed reboot + # ends this script, and the dotfiles steps above must not be skipped by it. + # This also covers mise itself (the mise-bin package) and mise's tools. + if is_omarchy; then + step "Updating Omarchy and system packages" + omarchy-update -y + fi + step "Update complete" } diff --git a/root/private_dot_config/fish/conf.d/mise.fish b/root/private_dot_config/fish/conf.d/mise.fish index 0253d13..ea827ab 100644 --- a/root/private_dot_config/fish/conf.d/mise.fish +++ b/root/private_dot_config/fish/conf.d/mise.fish @@ -1,4 +1,10 @@ -if command -v mise >/dev/null 2>&1 +# Guarded on the activation, not just on `command -v mise`: something earlier in +# the conf.d pass may already have activated it (Omarchy's omarchy-fish does, +# from fish's vendor_conf.d). Re-activating is harmless — mise's script +# deactivates first and guards __MISE_ORIG_PATH — but it costs another +# subprocess on every shell start. `mise` becomes a function once activated, and +# functions are per-shell, so a nested fish still activates properly. +if command -v mise >/dev/null 2>&1; and not functions -q mise if status is-interactive mise activate fish | source else diff --git a/root/private_dot_config/fish/conf.d/starship.fish b/root/private_dot_config/fish/conf.d/starship.fish index dc504d7..b027576 100644 --- a/root/private_dot_config/fish/conf.d/starship.fish +++ b/root/private_dot_config/fish/conf.d/starship.fish @@ -1,3 +1,4 @@ -if status is-interactive +# See mise.fish for why this checks the activation, not just `status is-interactive`. +if status is-interactive; and not functions -q __starship_set_job_count starship init fish | source end diff --git a/root/private_dot_config/fish/conf.d/zoxide.fish b/root/private_dot_config/fish/conf.d/zoxide.fish index 3cabdf8..b1ceb83 100644 --- a/root/private_dot_config/fish/conf.d/zoxide.fish +++ b/root/private_dot_config/fish/conf.d/zoxide.fish @@ -1,3 +1,4 @@ -if command -v zoxide >/dev/null 2>&1 +# See mise.fish for why this checks the activation rather than just the binary. +if command -v zoxide >/dev/null 2>&1; and not functions -q __zoxide_z zoxide init fish | source end diff --git a/root/private_dot_config/fish/config.fish.tmpl b/root/private_dot_config/fish/config.fish.tmpl index f3ba608..5e6199d 100644 --- a/root/private_dot_config/fish/config.fish.tmpl +++ b/root/private_dot_config/fish/config.fish.tmpl @@ -23,7 +23,12 @@ set -g fish_color_valid_path --underline fish_add_path ~/.local/share/mise/shims fundle plugin 'edc/bass' +{{ if not .isOmarchy -}} +{{/* Omarchy's omarchy-fish package vendors fzf.fish (and calls + fzf_configure_bindings from its own conf.d), so a fundle copy would be a + second install of the same plugin fighting over the same keybindings. */ -}} fundle plugin 'PatrickF1/fzf.fish' +{{ end -}} fundle plugin 'jorgebucaran/autopair.fish' fundle init diff --git a/root/private_dot_config/ghostty/config b/root/private_dot_config/ghostty/config deleted file mode 100644 index 4cdf340..0000000 --- a/root/private_dot_config/ghostty/config +++ /dev/null @@ -1,14 +0,0 @@ -theme = Raycast Dark -cursor-invert-fg-bg = true - -font-family = FiraCode Nerd Font - -quick-terminal-screen = mouse - -keybind = global:cmd+grave_accent=toggle_quick_terminal -keybind = shift+enter=text:\n - -window-height = 40 -window-width = 140 - -shell-integration-features = sudo,title,ssh-terminfo,ssh-env diff --git a/root/private_dot_config/ghostty/config.tmpl b/root/private_dot_config/ghostty/config.tmpl new file mode 100644 index 0000000..7903aeb --- /dev/null +++ b/root/private_dot_config/ghostty/config.tmpl @@ -0,0 +1,26 @@ +{{ if .isOmarchy -}} +# Follow the Omarchy theme system instead of pinning a theme: omarchy-theme-set +# rewrites this file on every theme change, and the `?` prefix makes ghostty +# tolerate its absence before the first theme is applied. It sets colors only, +# so everything below still applies. +config-file = ?"~/.local/state/omarchy/current/theme/ghostty.conf" +{{- else -}} +theme = Raycast Dark +{{- end }} +cursor-invert-fg-bg = true + +{{ if .isOmarchy -}} +# Unquoted on purpose: omarchy-font-set only rewrites a quoted font-family, so +# leaving it bare keeps chezmoi the source of truth for the terminal font. +{{ end -}} +font-family = FiraCode Nerd Font + +quick-terminal-screen = mouse + +keybind = global:cmd+grave_accent=toggle_quick_terminal +keybind = shift+enter=text:\n + +window-height = 40 +window-width = 140 + +shell-integration-features = sudo,title,ssh-terminfo,ssh-env diff --git a/root/private_dot_config/mise/config.toml.tmpl b/root/private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl similarity index 54% rename from root/private_dot_config/mise/config.toml.tmpl rename to root/private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl index 8dc12cd..3b8877d 100644 --- a/root/private_dot_config/mise/config.toml.tmpl +++ b/root/private_dot_config/mise/conf.d/10-dotfiles.toml.tmpl @@ -1,27 +1,44 @@ +{{/* A conf.d drop-in rather than ~/.config/mise/config.toml, so that + `mise use -g` keeps a file of its own to write to — Omarchy provisions + its lazy tool stubs that way, and chezmoi owning config.toml made the + two overwrite each other on every apply. mise refuses to write into + conf.d, so this file is safe from that side too. + + Trade-off: within a global config dir conf.d is the *lowest* precedence, + so config.toml wins wherever both name the same tool. */ -}} [tools] +{{ if not .isOmarchy -}} +{{/* Omarchy ships all of these already: the first group as pacman packages + from its own mirror, gh and hunk as lazy mise stubs in ~/.local/bin + (omarchy-mise-install). A second mise copy either shadows the one + Omarchy provides — which Omarchy documents as breaking herdr, whose + client and daemon share a wire protocol — or is itself shadowed. */ -}} starship = "1.26.0" lazygit = "0.64.0" lazydocker = "0.25.2" -ast-grep = "0.45.1" -hyperfine = "1.20.0" -duf = "0.9.1" fd = "10.4.2" fzf = "0.74.2" jq = "1.8.2" ripgrep = "15.2.0" -lsd = "1.2.0" -gh = "2.97.0" zoxide = "0.10.0" +usage = "5.1.0" +gh = "2.97.0" +"github:modem-dev/hunk" = "0.18.0" +{{ end -}} +ast-grep = "0.45.1" +hyperfine = "1.20.0" +duf = "0.9.1" +lsd = "1.2.0" worktrunk = "0.72.0" cloudflared = "2026.7.3" aube = "1.38.1" -usage = "5.1.0" "github:Ataraxy-Labs/sem" = "0.21.0" "github:backnotprop/plannotator" = "0.26.7" -"github:modem-dev/hunk" = "0.18.0" {{ if ne .chezmoi.os "windows" -}} +{{ if not .isOmarchy -}} herdr = "0.8.0" +{{ end -}} zellij = "0.44.3" "github:zyedidia/eget" = "1.3.4" {{- end }} @@ -40,8 +57,10 @@ idiomatic_version_file_enable_tools = ["node", "go", "python", "bun"] # Tools must be published for at least N days before they will be installed minimum_release_age = "7d" -# Pin SHA256 + per-platform download URLs in mise.lock alongside this config. -# Survives upstream tampering and avoids GitHub rate limits on reinstall. +# Pin SHA256 + per-platform download URLs in a mise.lock next to each project +# config. Survives upstream tampering and avoids GitHub rate limits on +# reinstall. (Global configs are exempt: mise only writes a lockfile for one +# on an explicit `mise lock --global`.) lockfile = true # Re-verify provenance/checksums on every install (auto-enables @@ -57,4 +76,3 @@ node.verify = false # Install corepack shims after installing node node.corepack = true -