Compare commits
8 Commits
bda23fd5eb
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42e853a3c0 | ||
|
|
560631fd1d | ||
|
|
224166f9ea | ||
|
|
2235d933d5 | ||
|
|
6815c29a75 | ||
|
|
3f4a550f87 | ||
|
|
753bdfd403 | ||
|
|
434edc04a2 |
@@ -9,7 +9,6 @@ gathering = smart
|
||||
retry_files_enabled = False
|
||||
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
|
||||
451
docs/superpowers/plans/2026-07-28-ansible-reconciliation.md
Normal file
451
docs/superpowers/plans/2026-07-28-ansible-reconciliation.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# Ansible-arch ↔ Machine Reconciliation Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Bring the `ansible-arch` repo and the machine it manages back into alignment: commit the in-flight working-tree changes, fix the `become=True` global-default bug that caused root-owned dotfiles symlinks, and codify already-installed-but-undeclared packages into the right roles.
|
||||
|
||||
**Architecture:** No new files or abstractions. This is a reconciliation pass over existing `roles/*/tasks/*.yml` files and `ansible.cfg`. Every privileged task in the repo already carries an explicit `become: true` — the audit confirmed the *only* structural bug is the global default in `ansible.cfg`, so that fix is a one-line change, not a per-task sweep. Verification is `ansible-playbook --syntax-check` / `--check --diff` where the modules support check mode, and one real `ansible-playbook site.yml` run at the end (safe: pacman tasks are no-ops for already-installed packages, dotfiles-role symlink recreation is idempotent by design).
|
||||
|
||||
**Tech Stack:** Ansible, Arch Linux (pacman, AUR via `kewlfft.aur` + `paru`), GNU Stow.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Commit the in-flight working-tree diff
|
||||
|
||||
**Files:**
|
||||
- Modify (already modified in working tree, no further edits needed): `dotfiles/hypr/.config/hypr/hyprland.conf`, `roles/ai/tasks/main.yaml`, `roles/common/defaults/main.yml`, `roles/common/tasks/main.yml`, `roles/devops/tasks/main.yml`, `roles/dotfiles/defaults/main.yml`, `roles/dotfiles/tasks/main.yml`, `roles/hyprland/tasks/main.yml`, `roles/multimedia/tasks/main.yml`
|
||||
- Add (untracked): `dotfiles/hypr/.config/hypr/hyprlock.conf`, `dotfiles/hypr/.config/hypr/monitors.sh`, `dotfiles/kanshi/`, `dotfiles/starship/`
|
||||
|
||||
This working-tree diff already reflects the intended current state (verified during brainstorming — it's the hyprland.conf tweaks, new dotfiles stow packages, and new role packages that led to this reconciliation). Nothing to write here, just commit it.
|
||||
|
||||
- [ ] **Step 1: Review the diff one more time**
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff --stat
|
||||
```
|
||||
|
||||
Expected: the same 9 modified files and 4 untracked paths listed in the design spec's "Context" section.
|
||||
|
||||
- [ ] **Step 2: Stage and commit**
|
||||
|
||||
```bash
|
||||
git add dotfiles/hypr/.config/hypr/hyprland.conf \
|
||||
dotfiles/hypr/.config/hypr/hyprlock.conf \
|
||||
dotfiles/hypr/.config/hypr/monitors.sh \
|
||||
dotfiles/kanshi/ dotfiles/starship/ \
|
||||
roles/ai/tasks/main.yaml \
|
||||
roles/common/defaults/main.yml \
|
||||
roles/common/tasks/main.yml \
|
||||
roles/devops/tasks/main.yml \
|
||||
roles/dotfiles/defaults/main.yml \
|
||||
roles/dotfiles/tasks/main.yml \
|
||||
roles/hyprland/tasks/main.yml \
|
||||
roles/multimedia/tasks/main.yml
|
||||
git commit -m "feat: formalize in-flight hyprland/dotfiles/role changes"
|
||||
git status
|
||||
```
|
||||
|
||||
Expected: `git status` shows "nothing to commit, working tree clean" (modulo files touched by later tasks).
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Fix the `become=True` global default
|
||||
|
||||
**Files:**
|
||||
- Modify: `ansible.cfg:11-15`
|
||||
|
||||
**Context:** `ansible.cfg` currently has:
|
||||
|
||||
```ini
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
```
|
||||
|
||||
Every task across every role that actually needs root (`pacman:`, `systemd:`, `user:`, `getent:`, `chsh`) already has an explicit `become: true` set on it — confirmed by reading every `roles/*/tasks/*.yml` file. Every task that must run unprivileged (AUR installs via `kewlfft.aur.aur`, the `paru` makepkg bootstrap, `oh-my-zsh` install, dotfiles `stow`/`file` tasks, `blockinfile` edits to `.zshrc`/`.profile`) already correctly omits `become`. The global `become = True` default was silently overriding all of those correctly-unprivileged tasks to root — that's why `~/.config/hypr`, `~/.config/zellij`, and `~/.config/starship.toml` symlinks are owned by `root`.
|
||||
|
||||
So the fix is exactly one line.
|
||||
|
||||
- [ ] **Step 1: Remove the global become default**
|
||||
|
||||
Edit `ansible.cfg`, changing:
|
||||
|
||||
```ini
|
||||
[privilege_escalation]
|
||||
become = True
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```ini
|
||||
[privilege_escalation]
|
||||
become_method = sudo
|
||||
become_user = root
|
||||
become_ask_pass = False
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax-check the playbook**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --syntax-check
|
||||
```
|
||||
|
||||
Expected: `playbook: site.yml` with no errors.
|
||||
|
||||
- [ ] **Step 3: Dry-run the parts that support check mode**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --check --diff
|
||||
```
|
||||
|
||||
Expected: pacman/systemd/user tasks report no changes (packages already installed, services already enabled). The `stow`/`command` tasks in the dotfiles role will show as `skipped` (partial check-mode support) rather than simulate the symlink recreation — that's expected and covered by the real run in Task 6.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add ansible.cfg
|
||||
git commit -m "fix: stop forcing become=True globally, rely on per-task become"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add missing packages to the `ctf` role
|
||||
|
||||
**Files:**
|
||||
- Modify: `roles/ctf/tasks/main.yml`
|
||||
|
||||
Current content:
|
||||
|
||||
```yaml
|
||||
# tasks file for ctf
|
||||
- name: Install CTF tools (official repos)
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- wireshark-qt
|
||||
- nmap
|
||||
- openbsd-netcat
|
||||
- binwalk
|
||||
- gobuster
|
||||
state: present
|
||||
```
|
||||
|
||||
`gitleaks`, `picocom`, and `python-pyserial` are official-repo packages. `nuclei-bin` is AUR-only.
|
||||
|
||||
- [ ] **Step 1: Add the official-repo packages**
|
||||
|
||||
Edit `roles/ctf/tasks/main.yml` to:
|
||||
|
||||
```yaml
|
||||
# tasks file for ctf
|
||||
- name: Install CTF tools (official repos)
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- wireshark-qt
|
||||
- nmap
|
||||
- openbsd-netcat
|
||||
- binwalk
|
||||
- gobuster
|
||||
- gitleaks
|
||||
- picocom
|
||||
- python-pyserial
|
||||
state: present
|
||||
|
||||
- name: Install CTF AUR packages
|
||||
kewlfft.aur.aur:
|
||||
name: "{{ item }}"
|
||||
use: "{{ aur_helper | default('paru') }}"
|
||||
state: present
|
||||
loop:
|
||||
- nuclei-bin
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax-check**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --syntax-check
|
||||
```
|
||||
|
||||
Expected: no errors.
|
||||
|
||||
- [ ] **Step 3: Dry-run just this role**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --check --diff --tags ctf
|
||||
```
|
||||
|
||||
Expected: `pacman` task reports `ok` (all packages already installed, since the audit confirmed they're present via `pacman -Qeq`). The AUR task will show as `skipped` in check mode (module doesn't support check mode) — that's fine, it's covered by the Task 6 real run.
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add roles/ctf/tasks/main.yml
|
||||
git commit -m "feat(ctf): add gitleaks, nuclei-bin, picocom, python-pyserial"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Add missing packages to the `common` role
|
||||
|
||||
**Files:**
|
||||
- Modify: `roles/common/tasks/main.yml:19-53`
|
||||
|
||||
Current "Install base development tools" task (lines 19-53):
|
||||
|
||||
```yaml
|
||||
- name: Install base development tools
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- base-devel
|
||||
- git
|
||||
- curl
|
||||
- wget
|
||||
- rsync
|
||||
- unzip
|
||||
- zip
|
||||
- less
|
||||
- man-db
|
||||
- texinfo
|
||||
- which
|
||||
- zsh
|
||||
- stow
|
||||
- ansible
|
||||
- zoxide
|
||||
- atuin
|
||||
- broot
|
||||
- bottom
|
||||
- hyperfine
|
||||
- tokei
|
||||
- git-delta
|
||||
- yazi
|
||||
- ueberzugpp
|
||||
- tealdeer
|
||||
- ledger
|
||||
- btop
|
||||
- htop
|
||||
- bind
|
||||
- socat
|
||||
- iperf3
|
||||
state: present
|
||||
```
|
||||
|
||||
Add `nano`, `vim`, `sshpass`, `smartmontools` here. `cargo-audit` isn't a pacman package (it's installed via `cargo install`), so it goes in the separate "Install Rust toolchain and Python uv" task as a follow-up `cargo` step.
|
||||
|
||||
- [ ] **Step 1: Add nano, vim, sshpass, smartmontools**
|
||||
|
||||
Edit the `name:` list in "Install base development tools" (`roles/common/tasks/main.yml:22-51`), adding after `iperf3`:
|
||||
|
||||
```yaml
|
||||
- iperf3
|
||||
- nano
|
||||
- vim
|
||||
- sshpass
|
||||
- smartmontools
|
||||
state: present
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add cargo-audit via cargo install**
|
||||
|
||||
Current "Install Rust toolchain and Python uv" task (`roles/common/tasks/main.yml:55-68`):
|
||||
|
||||
```yaml
|
||||
- name: Install Rust toolchain and Python uv
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- rust
|
||||
- eza
|
||||
- bat
|
||||
- ripgrep
|
||||
- fd
|
||||
- starship
|
||||
- cargo
|
||||
- cargo-binstall
|
||||
- uv
|
||||
state: present
|
||||
```
|
||||
|
||||
Add a new task immediately after it:
|
||||
|
||||
```yaml
|
||||
- name: Install cargo-audit
|
||||
command: cargo install cargo-audit --locked
|
||||
args:
|
||||
creates: "{{ archsetup_user_home }}/.cargo/bin/cargo-audit"
|
||||
```
|
||||
|
||||
No `become` — `cargo install` must run as the invoking user, installing into `~/.cargo/bin`.
|
||||
|
||||
- [ ] **Step 3: Syntax-check**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --syntax-check
|
||||
```
|
||||
|
||||
Expected: no errors.
|
||||
|
||||
- [ ] **Step 4: Dry-run**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --check --diff --tags common
|
||||
```
|
||||
|
||||
Expected: pacman task `ok`, no changes (all four packages already installed per the audit). The `cargo-audit` command task will show as `skipped` in check mode — verified for real in Task 6 (`cargo-audit` is already on the machine, per the audit, so it'll be a no-op there too via the `creates` guard).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add roles/common/tasks/main.yml
|
||||
git commit -m "feat(common): add nano, vim, sshpass, smartmontools, cargo-audit"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Remove duplicate `docker` package from `cs_student` role
|
||||
|
||||
**Files:**
|
||||
- Modify: `roles/cs_student/tasks/main.yml:1-17`
|
||||
|
||||
`devops` role (`roles/devops/tasks/main.yml:6`) already declares `docker`. Current `cs_student` task:
|
||||
|
||||
```yaml
|
||||
# tasks file for cs_student
|
||||
- name: Install CS student tools
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- neovim
|
||||
- emacs
|
||||
- python
|
||||
- nodejs
|
||||
- npm
|
||||
- texlive-bin
|
||||
- texlive-basic
|
||||
- jupyter-nbconvert
|
||||
- docker
|
||||
- code
|
||||
- github-cli
|
||||
state: present
|
||||
```
|
||||
|
||||
- [ ] **Step 1: Remove the `docker` line**
|
||||
|
||||
Edit to:
|
||||
|
||||
```yaml
|
||||
# tasks file for cs_student
|
||||
- name: Install CS student tools
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- neovim
|
||||
- emacs
|
||||
- python
|
||||
- nodejs
|
||||
- npm
|
||||
- texlive-bin
|
||||
- texlive-basic
|
||||
- jupyter-nbconvert
|
||||
- code
|
||||
- github-cli
|
||||
state: present
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Syntax-check**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --syntax-check
|
||||
```
|
||||
|
||||
Expected: no errors.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add roles/cs_student/tasks/main.yml
|
||||
git commit -m "refactor(cs_student): drop duplicate docker package, already in devops role"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Remove the dead duplicate handler file
|
||||
|
||||
**Files:**
|
||||
- Delete: `roles/common/handlers/main.yaml`
|
||||
|
||||
**Context:** `roles/common/handlers/` contains both `main.yml` and `main.yaml`. Ansible's role loader (`ansible/playbook/role/__init__.py:_load_role_yaml`, confirmed by reading the installed Ansible source) searches extensions in the fixed order `['.yml', '.yaml', '.json']` and stops at the first match for handlers (`allow_dir=False`). That means `main.yml` — which already has `become: true` and is the one referenced correctly — is the only file Ansible ever loads. `main.yaml` is silently dead: it's never parsed, and its font-cache handler task (which lacks `become: true` and would break under the Task 2 fix if it were ever loaded) is orphaned.
|
||||
|
||||
Since `main.yml` is confirmed live and already correct, `main.yaml` is pure dead weight causing confusion about which handler is authoritative — remove it.
|
||||
|
||||
- [ ] **Step 1: Confirm main.yml is the active file**
|
||||
|
||||
```bash
|
||||
cat roles/common/handlers/main.yml
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```yaml
|
||||
- name: Update font cache
|
||||
become: true
|
||||
command: fc-cache -fv
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Delete the dead duplicate**
|
||||
|
||||
```bash
|
||||
git rm roles/common/handlers/main.yaml
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Syntax-check**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --syntax-check
|
||||
```
|
||||
|
||||
Expected: no errors (confirms `main.yml` alone still satisfies the `notify: Update font cache` handler reference in `roles/common/tasks/main.yml`).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git commit -m "chore(common): remove dead duplicate handlers/main.yaml (main.yml is the one Ansible loads)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Real playbook run and final verification
|
||||
|
||||
This is the only step in the plan that touches live machine state beyond `--check` dry-runs. It's expected to be safe and mostly a no-op: every package involved is already installed (confirmed via `pacman -Qeq` during the audit), and the dotfiles role's "Remove existing config files before stowing" task unconditionally removes and recreates its symlink targets on every run by design — that's what fixes the root-ownership bug, with no manual `rm`/`chown` needed.
|
||||
|
||||
- [ ] **Step 1: Run the full playbook**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml
|
||||
```
|
||||
|
||||
Expected: playbook completes with `failed=0`. Some tasks report `changed` (dotfiles symlinks recreated, any packages that were pulled in by Tasks 3-4 but not yet actually reconciled by pacman state).
|
||||
|
||||
- [ ] **Step 2: Verify symlink ownership**
|
||||
|
||||
```bash
|
||||
ls -la ~/.config/hypr ~/.config/zellij ~/.config/starship.toml ~/.config/kanshi
|
||||
```
|
||||
|
||||
Expected: all four owned by `tudattr`, not `root`.
|
||||
|
||||
- [ ] **Step 3: Verify no drift remains**
|
||||
|
||||
```bash
|
||||
ansible-playbook site.yml --check --diff
|
||||
git status
|
||||
```
|
||||
|
||||
Expected: second `--check` run reports no further changes (idempotent), `git status` shows a clean working tree.
|
||||
@@ -0,0 +1,121 @@
|
||||
# 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:
|
||||
|
||||
1. Fix the `become` default so privilege escalation is opt-in per task, not
|
||||
global.
|
||||
2. Commit the existing in-flight diff and untracked files as-is — they're
|
||||
already the intended state.
|
||||
3. 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 = True` from `ansible.cfg` `[privilege_escalation]` (default
|
||||
becomes `False`).
|
||||
- Add explicit `become: true` to 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` / `getent` tasks and the sudoers-setup step (common role)
|
||||
- No change needed for `kewlfft.aur.aur` tasks or the `paru` makepkg shell
|
||||
task — they never had explicit `become` set, so they now correctly inherit
|
||||
`become: 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`/
|
||||
`.profile` tasks — same reasoning, they now correctly run unprivileged.
|
||||
- Net effect on `~/.config` symlink ownership: no manual `chown`/`rm` step is
|
||||
needed. The dotfiles role's "Remove existing config files before stowing"
|
||||
task unconditionally removes the `hypr`/`zellij`/`starship.toml`/`kanshi`
|
||||
targets before every `stow` run, so simply re-running `site.yml` after 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/multimedia` package/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.conf`
|
||||
- `dotfiles/hypr/.config/hypr/monitors.sh`
|
||||
- `dotfiles/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:**
|
||||
- `gitleaks`
|
||||
- `nuclei-bin` (AUR)
|
||||
- `picocom`
|
||||
- `python-pyserial`
|
||||
|
||||
**Add to `common` role:**
|
||||
- `nano`
|
||||
- `vim`
|
||||
- `sshpass`
|
||||
- `smartmontools`
|
||||
- `cargo-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 --diff` runs clean (no unexpected
|
||||
changes) after the `become` fix and role edits.
|
||||
- `ansible-playbook site.yml` run confirms `~/.config/hypr`,
|
||||
`~/.config/zellij`, `~/.config/starship.toml`, `~/.config/kanshi` are
|
||||
owned by the invoking user, not root.
|
||||
- `git status` clean after committing.
|
||||
@@ -1,11 +1,4 @@
|
||||
|
||||
# #######################################################################################
|
||||
# AUTOGENERATED HYPRLAND CONFIG.
|
||||
# EDIT THIS CONFIG ACCORDING TO THE WIKI INSTRUCTIONS.
|
||||
# #######################################################################################
|
||||
|
||||
autogenerated = 0 # remove this line to remove the warning
|
||||
|
||||
# This is an example Hyprland config file.
|
||||
# Refer to the wiki for more information.
|
||||
# https://wiki.hypr.land/Configuring/
|
||||
@@ -23,7 +16,7 @@ autogenerated = 0 # remove this line to remove the warning
|
||||
################
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Monitors/
|
||||
monitor=,preferred,auto,auto
|
||||
monitor=,preferred,auto,1
|
||||
|
||||
|
||||
###################
|
||||
@@ -188,7 +181,7 @@ animations {
|
||||
|
||||
# See https://wiki.hypr.land/Configuring/Dwindle-Layout/ for more
|
||||
dwindle {
|
||||
pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
# pseudotile = true # Master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
|
||||
preserve_split = true # You probably want this
|
||||
}
|
||||
|
||||
@@ -210,18 +203,18 @@ misc {
|
||||
|
||||
# https://wiki.hypr.land/Configuring/Variables/#input
|
||||
input {
|
||||
kb_layout = us
|
||||
kb_layout = eu
|
||||
kb_variant =
|
||||
kb_model =
|
||||
kb_options = caps:ctrl_modifier
|
||||
kb_rules =
|
||||
|
||||
follow_mouse = 1
|
||||
follow_mouse = 0
|
||||
|
||||
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
|
||||
|
||||
touchpad {
|
||||
natural_scroll = false
|
||||
natural_scroll = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +239,7 @@ $mainMod = SUPER # Sets "Windows" key as main modifier
|
||||
# Example binds, see https://wiki.hypr.land/Configuring/Binds/ for more
|
||||
bind = $mainMod, Return, exec, $terminal
|
||||
bind = $mainMod SHIFT, Q, killactive,
|
||||
bind = $mainMod SHIFT, Space, exec, hyprlock
|
||||
# bind = $mainMod, M, exec, command -v hyprshutdown >/dev/null 2>&1 && hyprshutdown || hyprctl dispatch exit
|
||||
bind = $mainMod, E, exec, $fileManager
|
||||
bind = $mainMod, B, exec, $browser
|
||||
|
||||
82
dotfiles/hypr/.config/hypr/hyprlock.conf
Normal file
82
dotfiles/hypr/.config/hypr/hyprlock.conf
Normal file
@@ -0,0 +1,82 @@
|
||||
# hyprlock.conf
|
||||
# for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock
|
||||
|
||||
$font = Monospace
|
||||
|
||||
general {
|
||||
hide_cursor = false
|
||||
}
|
||||
|
||||
animations {
|
||||
enabled = true
|
||||
bezier = linear, 1, 1, 0, 0
|
||||
animation = fadeIn, 1, 5, linear
|
||||
animation = fadeOut, 1, 5, linear
|
||||
animation = inputFieldDots, 1, 2, linear
|
||||
}
|
||||
|
||||
background {
|
||||
monitor =
|
||||
path = screenshot
|
||||
blur_passes = 3
|
||||
}
|
||||
|
||||
input-field {
|
||||
monitor =
|
||||
size = 20%, 5%
|
||||
outline_thickness = 3
|
||||
inner_color = rgba(0, 0, 0, 0.0)
|
||||
|
||||
outer_color = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||
check_color = rgba(00ff99ee) rgba(ff6633ee) 120deg
|
||||
fail_color = rgba(ff6633ee) rgba(ff0066ee) 40deg
|
||||
|
||||
font_color = rgb(143, 143, 143)
|
||||
fade_on_empty = false
|
||||
rounding = 15
|
||||
|
||||
font_family = $font
|
||||
placeholder_text = Input password...
|
||||
fail_text = $PAMFAIL
|
||||
|
||||
dots_spacing = 0.3
|
||||
|
||||
position = 0, -20
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
|
||||
# TIME
|
||||
label {
|
||||
monitor =
|
||||
text = $TIME
|
||||
font_size = 90
|
||||
font_family = $font
|
||||
|
||||
position = -30, 0
|
||||
halign = right
|
||||
valign = top
|
||||
}
|
||||
|
||||
# DATE
|
||||
label {
|
||||
monitor =
|
||||
text = cmd[update:60000] date +"%A, %d %B %Y"
|
||||
font_size = 25
|
||||
font_family = $font
|
||||
|
||||
position = -30, -150
|
||||
halign = right
|
||||
valign = top
|
||||
}
|
||||
|
||||
label {
|
||||
monitor =
|
||||
text = $LAYOUT[en,ru]
|
||||
font_size = 24
|
||||
onclick = hyprctl switchxkblayout all next
|
||||
|
||||
position = 250, -20
|
||||
halign = center
|
||||
valign = center
|
||||
}
|
||||
51
dotfiles/hypr/.config/hypr/monitors.sh
Executable file
51
dotfiles/hypr/.config/hypr/monitors.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
PHILIPS="Philips Consumer Electronics Company PHL 325B1L UK02311010066"
|
||||
INTERNAL="eDP-1"
|
||||
PENDING_FILE="/tmp/hypr-monitor-pending-pid"
|
||||
|
||||
LOG="/tmp/hypr-monitors.log"
|
||||
|
||||
log() { echo "[$(date '+%H:%M:%S')] $*" >> "$LOG"; }
|
||||
|
||||
handle_monitor_event() {
|
||||
local monitors
|
||||
monitors=$(hyprctl monitors)
|
||||
log "handle_monitor_event called"
|
||||
log "Active monitors: $(echo "$monitors" | grep '^Monitor')"
|
||||
|
||||
if echo "$monitors" | grep -q "$PHILIPS"; then
|
||||
log "Home setup detected — enabling externals"
|
||||
hyprctl keyword monitor "DP-6, 2560x1440@60, 0x0, 1" && log "DP-6 ok" || log "DP-6 FAILED"
|
||||
hyprctl keyword monitor "DP-7, 1920x1080@120, 2560x0, 1" && log "DP-7 ok" || log "DP-7 FAILED"
|
||||
sleep 1
|
||||
hyprctl keyword monitor "$INTERNAL, disable" && log "eDP-1 disabled" || log "eDP-1 disable FAILED"
|
||||
else
|
||||
log "No external monitor — enabling internal"
|
||||
hyprctl keyword monitor "$INTERNAL, preferred, 0x0, 1" && log "eDP-1 enabled" || log "eDP-1 enable FAILED"
|
||||
hyprctl dispatch dpms on "$INTERNAL" && log "eDP-1 DPMS on" || log "eDP-1 DPMS FAILED"
|
||||
sleep 0.5
|
||||
for i in $(seq 1 10); do
|
||||
hyprctl dispatch moveworkspacetomonitor "$i" "$INTERNAL" 2>/dev/null
|
||||
done
|
||||
log "Workspaces moved to eDP-1"
|
||||
fi
|
||||
}
|
||||
|
||||
schedule_handle() {
|
||||
local pending
|
||||
pending=$(cat "$PENDING_FILE" 2>/dev/null)
|
||||
[ -n "$pending" ] && kill "$pending" 2>/dev/null
|
||||
|
||||
(sleep 1.5 && handle_monitor_event) &
|
||||
echo $! > "$PENDING_FILE"
|
||||
}
|
||||
|
||||
log "Script started"
|
||||
handle_monitor_event
|
||||
|
||||
socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while read -r line; do
|
||||
if [[ $line == "monitoradded"* ]] || [[ $line == "monitorremoved"* ]]; then
|
||||
schedule_handle
|
||||
fi
|
||||
done
|
||||
36
dotfiles/kanshi/.config/kanshi/config
Normal file
36
dotfiles/kanshi/.config/kanshi/config
Normal file
@@ -0,0 +1,36 @@
|
||||
output "Philips Consumer Electronics Company PHL 325B1L UK02311010066" {
|
||||
mode 2560x1440@60
|
||||
position 0,0
|
||||
scale 1
|
||||
alias $HOME_LEFT
|
||||
}
|
||||
|
||||
output "ASUSTek COMPUTER INC VG248 L4LMQS119147" {
|
||||
mode 1920x1080@120
|
||||
position 2560,0
|
||||
scale 1
|
||||
alias $HOME_RIGHT
|
||||
}
|
||||
|
||||
output eDP-1 {
|
||||
mode 1920x1200@60
|
||||
scale 1
|
||||
alias $INTERNAL
|
||||
}
|
||||
|
||||
# Profile for when you are at your desk
|
||||
profile home {
|
||||
output $HOME_LEFT enable
|
||||
output $HOME_RIGHT enable
|
||||
output $INTERNAL disable
|
||||
}
|
||||
|
||||
# Profile for when you are on the go
|
||||
profile outside {
|
||||
output $INTERNAL enable
|
||||
exec sleep 1 && hyprctl dispatch dpms on eDP-1
|
||||
}
|
||||
|
||||
profile fallback {
|
||||
|
||||
}
|
||||
34
dotfiles/starship/.config/starship.toml
Normal file
34
dotfiles/starship/.config/starship.toml
Normal file
@@ -0,0 +1,34 @@
|
||||
# Get editor completions based on the config schema
|
||||
"$schema" = 'https://starship.rs/config-schema.json'
|
||||
|
||||
# Inserts a blank line between shell prompts
|
||||
add_newline = true
|
||||
|
||||
# Replace the '❯' symbol in the prompt with '➜'
|
||||
[character] # The name of the module we are configuring is 'character'
|
||||
success_symbol = '[➜](bold green)' # The 'success_symbol' segment is being set to '➜' with the color 'bold green'
|
||||
|
||||
# Disable the package module, hiding it from the prompt completely
|
||||
[package]
|
||||
disabled = true
|
||||
|
||||
[kubernetes]
|
||||
disabled = false
|
||||
format = 'on [⛵ ($user on )($cluster in )$context \($namespace\)](dimmed green) '
|
||||
contexts = [
|
||||
{ context_pattern = "dev.local.cluster.k8s", style = "green", symbol = "💔 " },
|
||||
]
|
||||
|
||||
[docker_context]
|
||||
format = 'via [🐋 $context](blue bold)'
|
||||
|
||||
[azure]
|
||||
disabled = false
|
||||
format = "on [$symbol($username)]($style) "
|
||||
symbol = ' '
|
||||
style = 'blue bold'
|
||||
|
||||
[battery]
|
||||
full_symbol = '🔋 '
|
||||
charging_symbol = '⚡️ '
|
||||
discharging_symbol = '💀 '
|
||||
@@ -1,6 +1,8 @@
|
||||
# tasks file for ai
|
||||
- name: Install AI development tools
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- opencode
|
||||
- ollama
|
||||
state: present
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
archsetup_user: "{{ ansible_user_id }}"
|
||||
archsetup_repo: "{{ lookup('env', 'ARCHSETUP_REPO', default='https://github.com/YOUR_USERNAME/archsetup') }}"
|
||||
archsetup_user_home: "{{ lookup('env', 'HOME') }}"
|
||||
archsetup_dir: "{{ archsetup_user_home }}/archsetup"
|
||||
archsetup_dir: "{{ playbook_dir }}"
|
||||
aur_helper: paru
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
- name: Update font cache
|
||||
ansible.builtin.command: fc-cache -fv
|
||||
changed_when: false
|
||||
@@ -28,7 +28,7 @@
|
||||
- unzip
|
||||
- zip
|
||||
- less
|
||||
- man
|
||||
- man-db
|
||||
- texinfo
|
||||
- which
|
||||
- zsh
|
||||
@@ -42,7 +42,18 @@
|
||||
- tokei
|
||||
- git-delta
|
||||
- yazi
|
||||
- ueberzugpp
|
||||
- tealdeer
|
||||
- ledger
|
||||
- btop
|
||||
- htop
|
||||
- bind
|
||||
- socat
|
||||
- iperf3
|
||||
- nano
|
||||
- vim
|
||||
- sshpass
|
||||
- smartmontools
|
||||
state: present
|
||||
|
||||
- name: Install Rust toolchain and Python uv
|
||||
@@ -60,6 +71,11 @@
|
||||
- uv
|
||||
state: present
|
||||
|
||||
- name: Install cargo-audit
|
||||
command: cargo install cargo-audit --locked
|
||||
args:
|
||||
creates: "{{ archsetup_user_home }}/.cargo/bin/cargo-audit"
|
||||
|
||||
- name: Install Fonts
|
||||
become: true
|
||||
pacman:
|
||||
@@ -193,3 +209,18 @@
|
||||
alias lt="eza --tree --level=2 --icons"
|
||||
alias df="df -h"
|
||||
alias wttr="curl wttr.in/Essen"
|
||||
|
||||
- name: Deploy zshrc yazi zellij wrapper
|
||||
blockinfile:
|
||||
path: "{{ archsetup_user_home }}/.zshrc"
|
||||
marker: "# {mark} ANSIBLE MANAGED - YAZI ZELLIJ"
|
||||
block: |
|
||||
# Zellij only supports (buggy) Sixel image preview; force yazi to think
|
||||
# it's running in Kitty so it falls back to Uberzug++ instead
|
||||
yazi() {
|
||||
if [[ -n "$ZELLIJ" ]]; then
|
||||
TERM=xterm-kitty command yazi "$@"
|
||||
else
|
||||
command yazi "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
- texlive-bin
|
||||
- texlive-basic
|
||||
- jupyter-nbconvert
|
||||
- docker
|
||||
- code
|
||||
- github-cli
|
||||
state: present
|
||||
|
||||
@@ -8,4 +8,15 @@
|
||||
- openbsd-netcat
|
||||
- binwalk
|
||||
- gobuster
|
||||
- gitleaks
|
||||
- picocom
|
||||
- python-pyserial
|
||||
state: present
|
||||
|
||||
- name: Install CTF AUR packages
|
||||
kewlfft.aur.aur:
|
||||
name: "{{ item }}"
|
||||
use: "{{ aur_helper | default('paru') }}"
|
||||
state: present
|
||||
loop:
|
||||
- nuclei-bin
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
pacman:
|
||||
name:
|
||||
- docker
|
||||
- docker-compose
|
||||
- docker-buildx
|
||||
- kubectl
|
||||
- terraform
|
||||
- ansible
|
||||
@@ -13,6 +15,16 @@
|
||||
- kubectx
|
||||
state: present
|
||||
|
||||
- name: Install DevOps AUR tools
|
||||
kewlfft.aur.aur:
|
||||
name: "{{ item }}"
|
||||
use: "{{ aur_helper | default('paru') }}"
|
||||
state: present
|
||||
loop:
|
||||
- kubeseal
|
||||
- tea
|
||||
- winbox
|
||||
|
||||
- name: Deploy DevOps aliases
|
||||
blockinfile:
|
||||
path: "{{ archsetup_user_home }}/.zshrc"
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
dotfiles_stow_packages:
|
||||
- hypr
|
||||
- zellij
|
||||
- starship
|
||||
- kanshi
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
loop:
|
||||
- hypr
|
||||
- zellij
|
||||
- starship.toml
|
||||
- kanshi
|
||||
|
||||
- name: Create symlinks with stow
|
||||
command: stow --target={{ archsetup_user_home }} {{ dotfiles_stow_packages | join(' ') }}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- hyprland
|
||||
- waybar
|
||||
- wofi
|
||||
- kitty
|
||||
@@ -14,10 +15,20 @@
|
||||
- brightnessctl
|
||||
- playerctl
|
||||
- grim
|
||||
- slurp
|
||||
- wl-clipboard
|
||||
- xdg-desktop-portal-hyprland
|
||||
- zellij
|
||||
- veracrypt
|
||||
- kanshi
|
||||
- dolphin
|
||||
- xdg-utils
|
||||
- qt5-wayland
|
||||
- qt6-wayland
|
||||
- polkit-kde-agent
|
||||
- power-profiles-daemon
|
||||
- sddm
|
||||
- uwsm
|
||||
state: present
|
||||
|
||||
- name: Install basic Linux tools
|
||||
@@ -31,19 +42,32 @@
|
||||
- dust
|
||||
- ffmpeg
|
||||
- sxiv
|
||||
- pandoc
|
||||
- p7zip
|
||||
- pandoc-cli
|
||||
- 7zip
|
||||
- entr
|
||||
- less
|
||||
- man
|
||||
- texinfo
|
||||
- which
|
||||
- iw
|
||||
- network-manager-applet
|
||||
- networkmanager
|
||||
- iwd
|
||||
- wireless_tools
|
||||
- wpa_supplicant
|
||||
- udiskie
|
||||
- ntfs-3g
|
||||
state: present
|
||||
|
||||
- name: Install audio stack
|
||||
become: true
|
||||
pacman:
|
||||
name:
|
||||
- pipewire
|
||||
- pipewire-alsa
|
||||
- pipewire-jack
|
||||
- pipewire-pulse
|
||||
- wireplumber
|
||||
- gst-plugin-pipewire
|
||||
- libpulse
|
||||
state: present
|
||||
|
||||
- name: Install Bluetooth support
|
||||
become: true
|
||||
pacman:
|
||||
@@ -59,9 +83,21 @@
|
||||
name:
|
||||
- cups
|
||||
- cups-pdf
|
||||
- cups-pk-helper
|
||||
- system-config-printer
|
||||
- simple-scan
|
||||
- tesseract-data-eng
|
||||
state: present
|
||||
|
||||
- name: Install Brother printer AUR packages
|
||||
kewlfft.aur.aur:
|
||||
name: "{{ item }}"
|
||||
use: "{{ aur_helper | default('paru') }}"
|
||||
state: present
|
||||
loop:
|
||||
- brscan4
|
||||
- brother-mfc-l2710dw
|
||||
|
||||
- name: Enable services
|
||||
become: true
|
||||
systemd:
|
||||
@@ -72,6 +108,9 @@
|
||||
- cups
|
||||
- docker
|
||||
- udisks2
|
||||
- NetworkManager
|
||||
- power-profiles-daemon
|
||||
- sddm
|
||||
|
||||
- name: Add user to docker group
|
||||
become: true
|
||||
|
||||
@@ -10,4 +10,19 @@
|
||||
- mpv
|
||||
- firefox
|
||||
- qutebrowser
|
||||
- yt-dlp
|
||||
- signal-desktop
|
||||
- keepassxc
|
||||
- lutris
|
||||
- steam
|
||||
state: present
|
||||
|
||||
- name: Install multimedia AUR packages
|
||||
kewlfft.aur.aur:
|
||||
name: "{{ item }}"
|
||||
use: "{{ aur_helper | default('paru') }}"
|
||||
state: present
|
||||
loop:
|
||||
- brave-bin
|
||||
- rustdesk-bin
|
||||
- proton-vpn-cli
|
||||
|
||||
Reference in New Issue
Block a user