Skip to content

Replace Ansible with mise modules - #160

Draft
TechDufus wants to merge 45 commits into
mainfrom
mise-migration
Draft

TechDufus wants to merge 45 commits into
mainfrom
mise-migration

Conversation

@TechDufus

Copy link
Copy Markdown
Owner

Replaces the Ansible roles/ tree with mise modules (modules/*/mise*.toml) and a slimmed bin/dotfiles that runs mise bootstrap. Supported profiles: omarchy, cachyos, macos.

Opened as a draft for review.

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 TechDufus left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review notes (zsh-only scope; pacman + macOS only by design).

Fix before merge

  • Homebrew prefix is hardcoded to /opt/homebrew and breaks Intel Macs (/usr/local). Use $(brew --prefix):
    • bin/dotfiles:67 eval "$(/opt/homebrew/bin/brew shellenv)" fails under set -e when brew isn't there
    • modules/zsh/mise.macos.toml:5 login_shell = "/opt/homebrew/bin/zsh"
    • modules/clangd/mise.macos.toml:5 clangd 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.d backup behavior (bin/dotfiles:89-98), and version-manager cleanup.

Leftovers (suggestions)

  • modules/bash/bashrc:110 still loads the oh-my-bash goenv plugin.
  • modules/zsh/zsh/paths_vars.zsh:18-20 rbenv / Homebrew ruby PATH branch.
  • modules/zsh/p10k.zsh:53 nvm prompt 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.yml only 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.sh was deleted with nothing replacing that coverage.

Verify

  • Check min_version = "2026.10.6" (under [bootstrap.brew] in modules/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 zsh is 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.
gaps_in 2 / gaps_out 4 (Omarchy: 5 / 10). Cells re-snap the windows they
floated on every config reload, so gap and layouts.lua changes (and
Omarchy's no-gaps toggle) apply without reopening windows.
Only targets mise refuses (a real file or folder where a module wants
a link) are moved aside. Copy-mode targets such as fcitx5's
keyboard.conf are overwritten, and mise keeps no checkpoint of them or
of what --force-dotfiles replaces (`mise dot undo`: nothing to undo).
Clone Omarchy themes with [bootstrap.repos] into
~/.config/omarchy/themes/<name>, where Omarchy's theme menu finds
them; `dotfiles --update` fast-forwards them. Pulsar is the first.

bootstrap:omarchy-theme switches to OMARCHY_DEFAULT_THEME once per
value of it, so a theme picked later in Omarchy's menu stays until the
default changes. Outside a live Hyprland session it uses Omarchy's
headless mode, which stages the theme for the next login.

The live-session lookup moves into hypr-instance.sh, shared with
bootstrap.sh, so the stale-signature handling has one copy.
Catppuccin, a built-in theme, is now the default, and Pulsar is no
longer cloned. The theme support stays: the comment on
bootstrap:omarchy-theme shows how to add a theme repo again. An
existing Pulsar clone is left where it is.
Preserve Mac-specific app, SSH-agent, Claude and shell behavior.
Keep stable Raycast and existing sudo policy; retain Tern summon.
Stage Spoon updates with failure and interruption rollback.
Make setup previews non-mutating and conflict backups collision-safe.
Use native pnpm and remove Vimscript language-server management.

Verified with isolated native mise checks and focused regressions,
31 tool installs, 13 LSP lifecycles, real Spoon release convergence,
and synthetic dotfile apply, repeatability and rollback.

Preparation only: live main and host configuration remain unchanged.
No remote state was changed. Refs #159.
Keep only theme and TUI in the Mac profile. Herdr owns its generated
integration; it is no longer a migration prerequisite.

Verified isolated mise deployment and an unchanged repeat apply.
Live configuration and other profiles remain unchanged.
Prioritize mise runtimes in Darwin login shells while keeping Linux
wrapper precedence. Retire stale role links and manage Yazi directly.

Repair fresh Neovim plugin and Solargraph installs. Keep TypeScript 7
as the compiler and give language-server 6 its own compatible SDK.

Verified native login shells, simulated Linux PATH guards, actual
Yazi/Neovim runs, standalone TS diagnostics, a clean Solargraph install,
and no-change managed dotfile reapplication.

Refs #159.
Select supported Catppuccin Mocha options, remove near-black overrides,
and enable transparent ordinary floats while retaining text contrast.

Manage ApplePressAndHoldEnabled=false for Tern's macOS domain only.
Preserve global repeat timing, Linux settings, and terminal opacity.

Verified normal Neovim startup and native PTY rendering, applied native
preference type/value, no-change repeat, and managed dotfile state.
Physical key-repeat confirmation awaits a user-controlled Tern relaunch;
no running editor or chat was restarted.
Keep bootstrap in HOME unless explicitly redirected. Validate Linux
sudoers before installation and transfer Git includes without losing
signing policy, unrelated globals, or repository-local overrides.

Repair retained Helm and language-server dependencies, remove stale
sesh consumption and unconditional Ollama deletion, and respect
Omarchy's native update ownership. Align docs and validation gates.

Verified the full eight-suite macOS gate and real wrapper preview;
Linux sudoers, Git and Task scenarios; real Helm, tmux and mise
consumers; and the exact ShellCheck gate. Live Git migration preserves
effective values and is idempotent. No full fresh Omarchy installation.

Refs #159
Recognize ~/.local/share/omarchy consistently in the wrapper and
Taskfile while retaining explicit profile precedence and existing
system-marker and OS-ID detection. Add wrapper regression coverage
and document both supported installation roots.

Reproduced ordinary task update rejecting the home-only layout before
the fix. Verified real Linux GoTask dispatch afterward across 18
scenarios, the exact ShellCheck gate, and the full eight-suite macOS
gate. No actual system updater or package installation was run.

Refs #159
Clone the Ayaka theme (abhijeet-swami/omarchy-ayaka-theme) into
~/.config/omarchy/themes/ayaka and make it the default theme. The url
matches the existing clone's origin, .git suffix included.
btop.conf was deployed whole on macOS/CachyOS and not at all on
Omarchy, so Omarchy's default replaced every repo setting. mise only
merges JSON/TOML/YAML and btop rewrites its file wholesale, so
bootstrap:btop now sets just the keys in settings.conf and leaves the
rest to btop. The theme key comes from omarchy.conf (color_theme =
"current", Omarchy's theme link) or catppuccin.conf elsewhere.

shown_boxes gpu0 is dropped: without a GPU library btop removes the
box and saves that, so the key would flip back on every run.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant