5.0 KiB
Ansible-arch ↔ machine reconciliation
Date: 2026-07-28 Status: Approved
Context
The ansible-arch repo had an in-flight uncommitted diff (hyprland.conf tweaks,
new packages across several roles, new dotfiles stow packages) plus untracked
files. Separately, ansible.cfg sets become = True globally, which caused
the dotfiles role's stow task to run as root — the hypr, zellij, and
starship.toml symlinks in ~/.config ended up owned by root instead of
the invoking user. A full audit of installed packages (pacman -Qeq) against
what the roles declare also turned up drift in both directions.
Resolution order: prefer encoding drift into ansible roles and re-running the playbook over hand-editing the machine directly. Direct machine changes are only for things ansible can't (or already will, as a side effect) fix.
Scope
One reconciliation pass, no new tooling:
- Fix the
becomedefault so privilege escalation is opt-in per task, not global. - Commit the existing in-flight diff and untracked files as-is — they're already the intended state.
- Fold audited package/service drift into the appropriate roles.
No repeatable drift-check tool is being added (explicitly out of scope — one-time fix, matches existing YAGNI preference).
1. Privilege escalation fix
- Remove
become = Truefromansible.cfg[privilege_escalation](default becomesFalse). - Add explicit
become: trueto every task that needs root:- all
pacman:module tasks (every role) systemd:service-enable tasks (hyprland role)user:group-membership task (hyprland role, docker group)chsh/getenttasks and the sudoers-setup step (common role)
- all
- No change needed for
kewlfft.aur.aurtasks or theparumakepkg shell task — they never had explicitbecomeset, so they now correctly inheritbecome: false. (They were arguably broken before: makepkg refuses to run as root. This went unnoticed because the packages were already installed, so the tasks were no-ops.) - No change needed for dotfiles-role stow tasks or any
blockinfile/.zshrc/.profiletasks — same reasoning, they now correctly run unprivileged. - Net effect on
~/.configsymlink ownership: no manualchown/rmstep is needed. The dotfiles role's "Remove existing config files before stowing" task unconditionally removes thehypr/zellij/starship.toml/kanshitargets before everystowrun, so simply re-runningsite.ymlafter this fix deletes the root-owned symlinks and recreates them owned by the invoking user.
2. Formalize the in-flight diff
Commit as-is:
dotfiles/hypr/.config/hypr/hyprland.conf(keyboard layout, mouse/scroll behavior, hyprlock keybind, monitor scaling, dwindle pseudotile changes)roles/ai,roles/common,roles/devops,roles/dotfiles,roles/hyprland,roles/multimediapackage/task additions already in the working tree
git add untracked files that are already part of an existing stow package:
dotfiles/hypr/.config/hypr/hyprlock.confdotfiles/hypr/.config/hypr/monitors.shdotfiles/kanshi/dotfiles/starship/
3. Package/service audit — role placement
Cross-referenced pacman -Qeq (explicitly installed) against every
roles/*/tasks/*.yml package list.
Add to ctf role:
gitleaksnuclei-bin(AUR)picocompython-pyserial
Add to common role:
nanovimsshpasssmartmontoolscargo-audit
Explicitly out of scope (base install / firmware / bootloader —
provisioning concerns this repo doesn't own):
base, linux, linux-firmware, efibootmgr, lvm2, sudo,
btrfs-progs, amd-ucode, intel-media-driver, libva-intel-driver,
vulkan-intel, sof-firmware, xorg-server, xorg-xinit,
zram-generator, snapper.
Flagged, no role change: dunst — installed but superseded by swaync
(already declared in the hyprland role). Left as-is; not added to any role,
not uninstalled.
No action needed (false positives from the audit):
paru, paru-debug (installed by the common role's own bootstrap task, not
a plain pacman list item), ttf-joypixels (already declared, just via a
single-value name: key the audit grep didn't match).
Minor cleanup: remove the duplicate docker package entry from the
cs_student role — devops already declares it.
Dead file found during planning: roles/common/handlers/ contains both
main.yml and main.yaml. Ansible's role loader searches handler file
extensions in a fixed order and stops at the first match, so main.yml
(which already has become: true) is the only one ever loaded — main.yaml
is dead and orphaned. Delete it.
Verification
ansible-playbook site.yml --check --diffruns clean (no unexpected changes) after thebecomefix and role edits.ansible-playbook site.ymlrun confirms~/.config/hypr,~/.config/zellij,~/.config/starship.toml,~/.config/kanshiare owned by the invoking user, not root.git statusclean after committing.