Repository navigation
Replace Ansible with mise modules - #160
Merged
Merged
Conversation
Port every kept role to a mise conf.d module under modules/<name>/, loaded through ~/.config/mise/conf.d -> ~/.dotfiles/modules and applied with `mise bootstrap`: [bootstrap.packages], [dotfiles] links/copies/merges/blocks, [tools], [bootstrap.files], and bootstrap:<module> tasks. bin/dotfiles is now a small wrapper that installs git+mise, picks the omarchy/cachyos/macos profile, and runs `mise bootstrap --yes`. Removes the Ansible playbook, group_vars, per-role docs, tests that grep YAML, Ubuntu/Fedora/WSL paths, and the dropped roles. CI dry-runs both Linux profiles in an Arch container plus macOS, and runs the module tests.
bootstrap:omarchy caches success via outputs = { auto = true }. Agent and
SSH shells lack HYPRLAND_INSTANCE_SIGNATURE, so a run from there printed
"not in Hyprland" and marked the sources handled without reloading or
validating. Discover the running instance with `hyprctl instances` instead.
herdr/tmux panes that outlive a Hyprland restart keep a dead HYPRLAND_INSTANCE_SIGNATURE, so `hyprctl reload` failed and stopped the later bootstrap tasks. Use the variable only when `hyprctl instances` lists it, else the running instance; name the instance when reload fails.
svelte-language-server needs svelte ^4.2.19, which resolves to 4.2.20. mise refuses it as a trust downgrade: 4.2.20 was published without provenance after 5.0.0 had it. Same svelte-admin publisher and an upstream svelte@4.2.20 tag; 4.2.19 was unattested too. Exclude only that version so a later unattested release still fails.
mise loads a module folder's mise.toml as project config whenever it runs there (bootstrap:devtools, or anyone working in the folder), so node = "lts" overrode Omarchy's global 26.8.2 pin and the task run installed node@24. Declare node only for CachyOS and macOS, which have no Omarchy pin.
The old jj role re-rendered user.email from 1Password on every run. The port only filled an empty value, so a machine that once got the noreply fallback kept it while git used the real address. Set jj to the same email as git (1Password, else the last synced value) when it differs; use noreply only when jj has no email at all.
Deploy modes had been carried over from the Ansible roles as-is. Most copies dated from each role's first version with no recorded reason, and a copy silently overwrites live edits on every run. - Symlink files the app only reads: zsh (per-file links for ~/.config/zsh, so deleted sources lose their links), ghostty, kitty, lsd, lfk, gh-dash, worktrunk, Taskfile, commit_template, omp AGENTS.md, Omarchy keyboard.lua (now in hypr/), and on CachyOS/macOS tmux, neofetch, the btop theme, starship, bat, k9s, bash, bordersrc. - herdr config.toml becomes a merge: its settings screen writes keys in place, which every run reverted. - ~/.npmrc becomes three line edits: the copy deleted npm login tokens on every run. - Copies that remain carry a comment with the reason (app replaces the file, rewrites it on exit, or ignores symlinks), and AGENTS.md states the rules plus a mise hazard: switching a directory symlink to symlink-each with --force-dotfiles writes through the old link and replaces repo files with self-referencing links.
bun 1.4.3 made `bun --check` a full type check, which fails in CI where neither @types/node nor the omp package is installed. On bun 1.4.0 the step only ever caught parse errors, so keep that with `bun build --no-bundle`; the behavioural checks below are unchanged.
Centre the logo with a CI status badge, turn the reference-only and run-twice notes into GitHub callouts, and replace the bootstrap prose with a Mermaid diagram of what `dotfiles` does.
A failed package install stops the whole mise run before dotfiles, tools and tasks. Verified in a clean Arch container: with 1Password in [bootstrap.packages] and unavailable, `mise bootstrap` exited 1 at the packages step and applied nothing else. Install the app and CLI from bootstrap:1password instead (pacman when a repo ships them, as Omarchy does; paru or yay otherwise; the Homebrew casks on macOS, leaving a hand-installed app alone). Every failure only warns. Same container, three cases (no repo, no AUR helper, a failing AUR build): exit 0 with dotfiles and all tasks applied.
TechDufus
commented
Oct 14, 2026
TechDufus
left a comment
Owner
Author
There was a problem hiding this comment.
Review notes (zsh-only scope; pacman + macOS only by design).
Fix before merge
- Homebrew prefix is hardcoded to
/opt/homebrewand breaks Intel Macs (/usr/local). Use$(brew --prefix):bin/dotfiles:67eval "$(/opt/homebrew/bin/brew shellenv)"fails underset -ewhen brew isn't theremodules/zsh/mise.macos.toml:5login_shell = "/opt/homebrew/bin/zsh"modules/clangd/mise.macos.toml:5clangd source/opt/homebrew/opt/llvm/bin/clangd
Docs
- README: note that only omarchy/cachyos/macos are supported now (
bin/dotfiles:36-59) and that Ubuntu/Fedora/etc. Ansible support is recoverable from history. - Migration notes for existing Ansible-managed machines: old links/copies, the
conf.dbackup behavior (bin/dotfiles:89-98), and version-manager cleanup.
Leftovers (suggestions)
modules/bash/bashrc:110still loads the oh-my-bashgoenvplugin.modules/zsh/zsh/paths_vars.zsh:18-20rbenv / Homebrew ruby PATH branch.modules/zsh/p10k.zsh:53nvmprompt segment; consider removing it or swapping in a mise/asdf segment.- Nothing removes
~/.nvm,~/.pyenv,~/.asdf; consider a one-time cleanup task or a README note so they can't shadow mise.
Tests / CI
.github/workflows/mise.ymlonly dry-runs. Consider a job that applies for real and then runs a second time to prove idempotency, plus a zsh smoke test (e.g.zsh -ic 'command -v node').bin/tests/test_dotfiles_requirements.shwas deleted with nothing replacing that coverage.
Verify
- Check
min_version = "2026.10.6"(under[bootstrap.brew]inmodules/base/mise.macos.toml) and the mise features this relies on (mise bootstrap,[dotfiles],[bootstrap.packages],system_packages.managers) against the mise docs.
Optional
mise activate zshis only needed if you want per-folder[env]; shims (paths_vars.zsh:14) are fine otherwise.
mise's brew manager skips every entry on Intel Macs and Homebrew's installer refuses them, so stop early with "unsupported machine (Intel Mac)". Run brew shellenv only when /opt/homebrew/bin/brew exists. test:base runs the real script in a sandbox (env -i, temp HOME and XDG dirs, stub mise/uname/brew) and covers profile detection, the conf.d backup and link, miserc rewrites and the pull rules. DOTFILES_SYSROOT lets it fake /etc/os-release and /usr/share/omarchy. check now runs from home. Inside modules/base, mise also loads that file as a project config whose tasks run from ~, so the nested `mise run 'test:*'` could not find test:base's script.
Dry-runs missed both run-stopping bugs of the migration: the npm trust-policy failure in the tools step and a failed package install. The apply job runs the dotfiles and tools steps for real in an Arch container (cachyos profile) three times. The second run settles permissions on files the first run's merges create; the third must change nothing. Then zsh must find the mise shims, interactive or not. Packages, services and system files stay dry-run only: a stock container has no systemd, AUR helper, multilib or Omarchy repo. go tools need gcc for cgo, and registry lookups get 120s instead of the 20s default. About 3 minutes, in parallel with the arch job.
Other systems stop with "unsupported machine"; the Ansible version, which also covered Ubuntu, Fedora and WSL, is tagged ansible-final. macOS means Apple Silicon, and an existing conf.d is moved aside.
mise manages every runtime now; no goenv or rbenv is installed. The oh-my-bash ansible plugin stays: it only adds aliases for the Ansible CLI, which is still in use.
Replace the 1,669-line wizard config with a hand-written one that looks like Tern's "Tern" preset in other terminals: rounded chips for the directory (…/ plus the last three parts, the last in bold) and the git status, the clock on the right, ❯ on its own line, and earlier prompts collapsed to ❯. Colors are Catppuccin Mocha; each chip's background is its text color at about 14% over the base, as sampled from Tern's preset. Kept: the git counters (⇣⇡ *~+!?), the status, duration, background job, direnv, virtualenv, kubecontext, terraform, aws and context segments with their show-on-command rules, transient prompt and quiet instant prompt. Dropped: os_icon with its CachyOS override, and the dead nvm segment. Inside Tern its own preset still draws the prompt; Tern's "Your p10k config" option would draw these chips with flat edges.
A mise older than the module-folder support loads no module, never reads min_version, and `mise bootstrap` exits 0 having done nothing. The first macOS CI run did exactly that with mise 2026.9.1. Read min_version from modules/base/mise.toml and compare it with `mise --version` field by field before linking anything. On macOS try `brew upgrade mise` once; on Linux stop with the upgrade hint. Never run pacman -Sy here: that is a partial upgrade, and Omarchy blocks it.
The macos job passed while checking nothing. The runner image sets HOMEBREW_NO_AUTO_UPDATE=1, so brew poured a stale mise 2026.9.1, which ignores the module folders. Run brew update first, and in every job check that the base module's check task exists right after linking conf.d. Also move actions/checkout and actions/setup-node to v7 (Node 24); v4 ran on the deprecated Node 20 runtime.
In a terminal, dotfiles now runs mise bootstrap one stage at a time (packages, system, dotfiles, shell, tools, modules) behind a Catppuccin summary. Each stage shows a spinner with the live log line, then its result: counts from mise's JSON when jq is present, fail-soft module warnings, or a box with the log tail when it fails. mise's full output goes to ~/.local/state/dotfiles/last-run.log. It asks for sudo up front, applies the dotfiles a second time when the first pass created files that permission entries need, and runs the login-shell stage on the terminal when chsh will prompt. Piped output, --plain, -v, NO_COLOR and flags the UI does not handle keep the old behaviour: one `mise bootstrap --yes` with mise's output.
color_theme only styles mise's interactive prompts, such as the task picker in `mise run`; it matches the dotfiles summary and the prompt.
A fresh Omarchy ships its own ghostty, kitty and nvim configs, and omp leaves its own files too. mise refuses to replace a real file or directory with a link, so the first run stopped with "refusing to overwrite existing files" until it was forced. Before bootstrapping, ask mise which targets it would refuse (`mise dot apply --dry-run` lists them) and move each one to <path>.pre-dotfiles.<epoch>, as conf.d already is and as the Ansible version kept backups. A dry run only reports them and previews with --force-dotfiles, which writes nothing then. Runs that leave out the dotfiles part move nothing.
The repo config merges into Omarchy's herdr config, which sets prefix = "ctrl+space". The Ansible copy never set a prefix, so herdr's ctrl+b default applied; pin it explicitly. herdr 0.9.1 rejects a prefix array, so ctrl+space cannot be kept alongside.
Mirrors the Hammerspoon GrokBot binding (summon = 'G'). Matches the grok-bot window class and launches /usr/bin/grok-bot.
Omarchy's ~/.local/bin wrappers for codex, claude, omp and opencode
run `mise use -g`. A second global declaration in conf.d made omp and
codex fail ("declared in multiple global config files") and made
claude/opencode pin versions into the repo's mise.toml. Declare these
tools only on macOS and CachyOS.
Tern manages the omp version; update manually with `omp update` outside Tern.
Every cell now floats: windows sharing a cell overlap (focused on top, inset by Hyprland gaps/border) and nothing reflows on open or close. Summon and Super+U raise the window they place. - Hyper+P / Hyper+; / Hyper+' pick, cycle, reset the screen layout - lilHyper+O / Hyper+O move the window to the next/previous screen - Super+L toggles a workspace between cells and full screen only - Super+U and screen layout choices persist in $XDG_STATE_HOME/dotfiles/cells.lua across logouts Verified live on eDP-1: cell geometry, stacking, Super+U persistence, Super+L round trip, reset back to the Fullscreen default.
Ctrl+] / Ctrl+[ move to the next / previous block, and Ctrl+Shift+] / Ctrl+Shift+[ to the next / previous tab across sessions. A second merge entry owns only these chords in Tern's settings.json, so other bindings stay per machine. macOS keeps Tern's defaults for now.
Standard Dev puts the browser left and terminals right; both need the same workspace for the cells to show side by side.
Ctrl+Shift+[ misbehaved on Omarchy, so next / previous tab across sessions moves to Ctrl+Alt+] / Ctrl+Alt+[. Ctrl+Shift+] / [ fall back to Tern's default next / previous block. Ctrl+Alt+[ / ] were only editor fold / unfold in Tern and are unbound in Hyprland.
btop reads AMD GPU stats from librocm_smi64.so and drops a gpu0 box it cannot read. Install rocm-smi-lib on the Omarchy profile and own shown_boxes with gpu0 there; the integrated Renoir Radeon shows up.
Keep Discord personal-only on macOS and Linux without changing shared apps or OS profiles. Persist purpose locally through config-only setters. Verify native package selection, preview safety and idempotence. Fix the macOS Zsh fixture's temporary-path alias without weakening cache guards.
Manage surface_chat = "Console" in the shared settings merge and stop bootstrap.sh from deleting surface_chat; it now only drops the legacy opacity override. Console differs from Tern's Reader default, so Tern keeps it when it saves. Verified: tests/test_bootstrap.sh; isolated-HOME mise dot apply plus bootstrap leaves surface_chat=Console and removes opacity.
Super+O / Super+Shift+O now move the focused window to the next / previous screen into its remembered cell, as Command+O did under Hammerspoon and Meta+O under Plasma. They replace Omarchy's pop-out and Obsidian bindings. - Super+U cell choices are remembered per screen and layout; the older layout-only choices are dropped on load. - Floats cells does not own keep their relative box across screens; fullscreen windows stay fullscreen and return to their cell after. - screens entries can match by description; the HP P224 defaults to Fullscreen. - hypr/monitors.lua sets up the 4K + 1080p desk: arrangement, login cursor on the 4K, and workspaces 1-3 / 4-5 bound per screen only while both panels are connected (EDID check), so laptops keep starting on workspace 1. Verified: all test:* suites pass; hyprctl reload reports no config errors; live round trips on both screens (cell, Super+U memory, native float, fullscreen).
The desk's external keyboard sends F13 (keycode 191) from its Caps Lock key, so the CapsLock-only leader never fired on it. F13 is the key Karabiner produced on macOS and keyd also took on CachyOS. Verified: test:omarchy summon suite; live, Caps Lock then b on the external keyboard summoned the browser.
Super+O asks for a window's place on the other screen, as Command+O always snapped there under Hammerspoon, so a window tiled with Super+T (or by a float toggle) goes back to its cell, like Super+U. README documents this and the F13 leader. Verified: new cells case fails without the change; live, a tiled window moved to the HP and back landed in its cell.
Clone iamanro/omarchy-omp-usage and robzolkos/omarchy-github into ~/.config/omarchy/plugins via bootstrap.repos, like the themes. bootstrap:omarchy-plugins validates them, puts them in the bar and takes the stock omarchy.agents widget out through the running shell, once per plugin list, so later bar edits stay. Outside a session or on any failure it skips without recording, and retries next run. Verified: test:* suites pass; live run cloned both, enabled them after omarchy.tray, removed omarchy.agents; a rerun was a no-op.
The plugins run unsandboxed inside omarchy-shell, so dotfiles --update must not fast-forward them to unreviewed code. Pin each repo to the commit whose source was reviewed; upgrading is omarchy plugin update (diff + validation) followed by bumping ref. Verified: repos status shows both current at the pinned SHAs; repos update --dry-run fetches only the unpinned theme.
Pinning meant hand-bumping commit SHAs, and every dotfiles run reverted omarchy plugin update. Track upstream like the themes: dotfiles --update fast-forwards both plugins. This reverts the pinning from 6356e21; the files now match eb62669. Verified: repos status current, both checkouts on main, and repos update --dry-run fetches and fast-forwards both plugins.
omarchy update never touches third-party plugins, and unpinned repos only moved on dotfiles --update. Track each plugin's main branch so every dotfiles run fast-forwards it to the newest commit. Verified: repos status shows both on main and current; repos apply --dry-run reports nothing to change.
Tracking main made every dotfiles run fetch the plugins; offline, mise bootstrap repos apply exits 1 and aborts the whole bootstrap, which breaks the fail-soft rule. Unpin the repos again and link a post-update hook that runs omarchy plugin update --yes, so every omarchy update brings git-managed plugins to latest (validated, rolled back on failure). dotfiles --update still fast-forwards them. Verified: offline repos apply now exits 0; with omp-usage rewound one commit, the linked hook fast-forwarded it back to main and both plugins stayed in the bar; test:omarchy passes.
Omarchy dims windows to 0.985 / 0.96 and unfocused browsers to 0.985, so video showed the wallpaper through. A catch-all rule loaded after Omarchy's window rules sets 1 / 1; apps that want translucency set it themselves (Ghostty background-opacity). Verified: hyprctl reload with empty configerrors; getprop reports opacity 1 and opacity_inactive 1 for every open window (Brave was 1 / 0.985, the rest 0.985 / 0.96).
Standard Dev, 4K and HD layouts get a last full_screen cell, like Hammerspoon's empty full-screen slot, so Super+U can make one app fill the screen (per screen and layout) without switching layouts. Verified: test:omarchy suites; live, a window assigned to the cell filled the 4K's usable area below the bar and reset back.
Linux deployed no k9s config, so k9s ran on its generated defaults and the stock skin. Link config, plugins and aliases on Linux too. On Omarchy, k9s.yaml.tpl renders with each theme change and a theme-set hook copies it to skins/current.yaml; reactive k9s sessions retint live. macOS and CachyOS link catppuccin_mocha there. Turn the header and logo back on, drop the config keys the v0.51 schema rejects, use a real shellPod image, and make catppuccin crumbs and dialog buttons readable.
Headless theme switches skip theme-set hooks, and the task's sources cache kept it from rerunning after the first-run theme switch, so a fresh or SSH bootstrap left skins/current.yaml absent. Run bootstrap:k9s every time after bootstrap:omarchy-theme; refresh the theme only when the rendered skin is missing or older than the template, and copy it only when it differs.
Hyprland 0.56 swaps a workspace's tiled layout on config reloads and layout switches and drops its fullscreen state without unsetting the app's xdg fullscreen flag. Brave then re-entered browser fullscreen (hidden tabs) on every focus change. Cells now records each window's fullscreen mode and restores it after props refresh. Verified live: fullscreen Brave survived hyprctl reload at 2/2, F11 exit cleared the record, and focus changes kept the tabs.
Adds a regression case for the reload fullscreen re-sync: a mode Hyprland dropped silently is restored (fullscreen and maximized), a window the app took out of fullscreen is not. README and comment now claim only the verified trigger (config reloads); layout switches are source-backed, not exercised.
The omp-cloud plugin reads its connection from omp settings. The token is read from 1Password (HomeLab/omp-cloud) at first use, so no secret lives in this file. The herdr integrations stay local.
omp's /cloud prefers OMP_CLOUD_TOKEN over the op read in omp/config.yml, so a shell with a refreshed secret cache hands off without a 1Password prompt. The read joins wave one and adds no op call.
Clone piyush97/omaswitch with the other shell plugins and enable it. plugins.sh now enables any plugin kind (OMARCHY_PLUGINS), not only bar widgets; an overlay is enabled through shell.json plugins[]. Alt+Tab opens OmaSwitch while the plugin is installed and enabled, checked on each press, and otherwise keeps cells' window cycling, so disabling or removing the plugin needs no keybinding change.
The HomeLab/OpenMonitor 1Password item no longer exists. Its reads failed, and the all-or-nothing recipe aborted every refresh, leaving no secret cache. Remove the OTEL key, endpoint, and header exports along with the OTLP protocol defaults that only served them.
bootstrap:omp adds the TechDufus/omp-cloud marketplace and installs omp-cloud@omp-cloud when omp does not already have it, either from a marketplace or as a linked checkout (which shares node_modules/omp-cloud). The marketplace is added first because an unconfigured name@marketplace is treated as an npm spec. Upgrades stay manual: omp plugin upgrade.
Switch cloud.url from the localhost default to omp-cloud.home.techdufus.com:8080 so sessions on any machine reach the shared service. Verified by delegating and messaging cloud thread vavgqez05fnt from this session through that endpoint.
Restructure the README after high-star rice and terminal-app repos (Hyprland, HyDE, end-4, codex, mise, atuin): dark/light wordmark header, Catppuccin for-the-badge shields, kbd nav, framed Omarchy hero, gallery, collapsed operational notes, star history, license. All existing text is kept. .github/assets/render/render.py regenerates every image: the real bin/dotfiles UI against a stub mise and sudo in a sandbox HOME, captured from a private tmux server and rasterized with rsvg-convert.
Agent instruction files listed live config (bindings, package names, file paths, plugin inventories) that drifts with every change and already disagreed with the README. Keep the principles and boundaries; the config and README are the source of truth for specifics.
TechDufus
marked this pull request as ready for review
October 23, 2026 01:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the Ansible
roles/tree with mise modules (modules/*/mise*.toml) and a slimmedbin/dotfilesthat runsmise bootstrap. Supported profiles: omarchy, cachyos, macos.Opened as a draft for review.