diff --git a/AGENTS.md b/AGENTS.md index d7ef707..c603576 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,81 +8,107 @@ Project type: Ansible-based infrastructure plus managed dotfiles. - Inventory: `ansible/inventory/hosts.yml` - Group vars: `ansible/inventory/group_vars/*.yml` - Host vars: `ansible/inventory/host_vars/*.yml` -- Roles: `ansible/roles/*/tasks/main.yml` -- Dotfiles source of truth: `dotfiles/` -- Scripts: `scripts/` -- Sensitive local material/examples: `secrets/` -- Active orchestration: `all -> dotfiles_common`; `void -> packages_void, services_runit, profile_desktop_i3` +- Active roles: `dotfiles_common`, `packages_void`, `services_runit`, `profile_desktop_i3` - Roles present but not currently wired into `ansible/site.yml`: `base`, `dotfiles`, `packages_ubuntu`, `services_systemd`, `profile_workstation_gnome`, `profile_server` +- Dotfiles source of truth: `dotfiles/` +- Utility scripts: `scripts/` +- Sensitive local material/examples: `secrets/` ## Local instruction files Checked in this repository when this file was written: - `.cursorrules`: not present - `.cursor/rules/`: not present - `.github/copilot-instructions.md`: not present -If any of these appear later, treat them as higher-priority local instructions. +If any of these files appear later, treat them as higher-priority local instructions. ## Working assumptions - Favor idempotent, host-safe changes over cleverness. - Preserve the intended layering: `all -> OS -> profile -> host`. - Validate on one host before broad rollout. +- Prefer minimal, targeted edits over cleanup refactors. +- Treat `secrets/` as sensitive and avoid exposing secret values. - The repo contains both first-party dotfiles and vendored third-party plugin code. +## Current orchestration model +`ansible/site.yml` currently applies: +- `all -> dotfiles_common` +- `void -> packages_void, services_runit, profile_desktop_i3` + +Inventory also defines Ubuntu workstation and server hosts, but those paths are not yet orchestrated by the main playbook. + ## Build, lint, and test commands -There is no compile/build step. Validation is Ansible syntax checks, inventory inspection, dry-runs, and linting. +There is no compile/build step. Validation is based on Ansible syntax checks, inventory inspection, dry-runs, and linting. + Install base tooling if needed: ```bash python3 -m pip install ansible +ansible-galaxy collection install community.general ``` + Core commands: ```bash ansible-playbook ansible/site.yml ansible-playbook ansible/site.yml --check --diff +ansible-playbook ansible/site.yml --syntax-check ansible-playbook ansible/site.yml --limit ikaros ansible-playbook ansible/site.yml --limit nymph -ansible-playbook ansible/site.yml --syntax-check ansible-inventory --graph ansible-inventory --host ikaros ``` -Linting if available locally: + +Linting and static checks if available locally: ```bash ansible-lint ansible/site.yml ansible-lint ansible/roles yamllint ansible/ +sh -n scripts/bootstrap_mail.sh +shellcheck scripts/bootstrap_mail.sh ``` ## Single-test equivalents -There is no Molecule, pytest, or dedicated unit-test suite. Use these as the closest equivalent to a single targeted test. +There is no Molecule, pytest, or dedicated unit-test suite. Use the narrowest validation that matches the change. + Fastest confidence check: ```bash ansible-playbook ansible/site.yml --syntax-check ``` -Best default host-scoped safety check: + +Best default host-scoped dry-run: ```bash ansible-playbook ansible/site.yml --limit ikaros --check --diff ``` -Start from one task when validating a narrow change: + +Validate one host's resolved inventory only: +```bash +ansible-inventory --host ikaros +``` + +Validate a narrow Ansible change starting from one task: ```bash ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy common dotfiles" --check --diff ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Install Void nonfree repository if needed" --check --diff ansible-playbook ansible/site.yml --limit ikaros --start-at-task "Copy desktop dotfiles" --check --diff ``` -Inventory-only resolution for one machine: + +Validate the standalone mail bootstrap script only: ```bash -ansible-inventory --host ikaros +sh -n scripts/bootstrap_mail.sh +shellcheck scripts/bootstrap_mail.sh ``` -Notes: -- Keep task names stable and unique enough for `--start-at-task`. -- Prefer `--check --diff` before package, service, PAM, or session changes. -- Use `--limit` aggressively to avoid accidental multi-host rollout. + +Testing notes: +- Prefer `--limit` aggressively to avoid accidental multi-host rollout. +- Prefer `--check --diff` before package, service, PAM, bootloader, or session changes. +- Keep task names stable and unique enough to support `--start-at-task`. +- If you change only vars, inventory inspection may be the quickest useful check. ## Code style guidelines ### General principles -- Prefer minimal, scoped changes that match the existing layout. -- Preserve idempotency and avoid hidden mutable state. +- Keep orchestration in playbooks and implementation details in roles. - Prefer declarative modules over imperative commands. -- Keep orchestration in playbooks and implementation in roles. - Avoid unrelated refactors while solving a targeted task. +- Preserve idempotency and make state transitions explicit. +- Keep changes readable for humans reviewing YAML and shell. ### Ansible modules and task structure - Use FQCN module names such as `ansible.builtin.copy` and `community.general.xbps`. @@ -90,46 +116,55 @@ Notes: - Use `command` only when no good module exists. - Use `shell` only when shell features are genuinely required. - When using `command` or `shell`, set `changed_when` and `failed_when` when defaults are misleading. -- Keep task names imperative, outcome-based, and unique enough to support `--start-at-task`. +- Keep task names imperative, outcome-based, and unique enough for `--start-at-task`. +- Use `block` when a sequence of tasks shares one condition or operational purpose. +- Prefer `loop` with clear `loop_control.label` for user-facing collections. ### YAML formatting - Start YAML files with `---`. -- Use 2-space indentation; do not use tabs. -- Keep lists and maps in stable, readable order. +- Use 2-space indentation and never tabs. +- Keep keys, lists, and maps in stable, readable order. - Quote file modes as strings: `"0644"`, `"0755"`, `"0600"`, `"0700"`. - Prefer multiline Jinja when a one-line expression becomes hard to read. - Avoid formatting-only churn in untouched sections. ### Variables, types, and templating -- Use `snake_case` consistently. -- Follow existing naming families like `common_packages`, `profile_packages`, and `host_packages`. +- Use `snake_case` consistently for variables and facts. +- Follow existing naming families such as `common_packages`, `profile_packages`, `host_packages`, and `host_dotfiles`. - Keep booleans as booleans, not quoted strings. - Keep structured data as YAML collections, not comma-separated strings. -- Guard optional values with `default([])`, `default({})`, or `default('')` as appropriate. +- Guard optional collections and maps with `default([])` and `default({})`. +- Guard optional strings with `default('')`. - Build user paths from `{{ user_home }}` instead of hardcoding home directories. +- Prefer explicit derived facts with `set_fact` only when reuse improves clarity. ### Naming conventions - Role names stay lowercase with underscores. - Inventory groups and matching var files should stay semantically aligned. -- New variables should fit existing patterns instead of inventing one-off names. +- Host-specific overrides belong in `host_vars`, not shared group files. +- New variables should fit existing naming patterns instead of introducing one-off aliases. ### Error handling and safety - Guard OS-specific or profile-specific behavior with `when` clauses. -- Prefer explicit loops and inputs over assumptions. +- Prefer explicit inputs over assumptions about host state. - Do not suppress failures without a clear operational reason. -- Use `failed_when: false` only for intentional state probes. +- Use `failed_when: false` only for intentional probes or best-effort detection. +- Use `no_log: true` for secrets, tokens, passwords, and sensitive command results. - Keep tasks non-interactive and automation-safe. - Avoid destructive operations in user homes unless the request clearly requires them. +- Fail early with `ansible.builtin.fail` when prerequisites such as architecture or required metadata are missing. -### Dotfiles and script conventions -- Prefer POSIX `sh` for simple login/session scripts. -- Use Bash only when Bash features are required; then use `#!/usr/bin/env bash`. +### Shell and script conventions +- Prefer POSIX `sh` for simple login/session/bootstrap scripts. +- Use Bash only when Bash features are actually required; then use `#!/usr/bin/env bash`. - Quote variable expansions unless intentional word splitting is required. +- Keep helper functions small and focused. +- Exit with clear errors when required commands or files are missing. - Preserve executable bits for deployed scripts where appropriate. -- Keep session-start changes conservative; verify startup paths and environment assumptions. ### Imports and external code -- There are no Python import conventions to optimize for here; most changes are YAML and shell. +- There are no Python import conventions to optimize for here; most changes are YAML, Jinja, and shell. +- If you add Python later, follow standard-library, third-party, local import grouping and keep modules small. - Treat `dotfiles/common/.tmux/plugins/` as vendored upstream code unless the task explicitly targets it. - Prefer changing first-party wrapper/config files before patching vendored plugin internals. @@ -138,31 +173,36 @@ Notes: - `ansible/inventory/group_vars/*.yml`: shared defaults by layer. - `ansible/inventory/host_vars/*.yml`: host-specific overrides only. - `ansible/roles/*/tasks/main.yml`: role implementation details. +- `ansible/templates/**/*.j2`: keep secrets and host-specific values parameterized. - `dotfiles/`: user-facing config and session scripts deployed by roles. -- `secrets/vault.yml`: treat as sensitive; do not expose values in tracked plain text. +- `scripts/`: standalone local utilities; keep them safe to run manually. ## Validation expectations before finishing Run the narrowest useful checks for the area you changed. + Default minimum: ```bash ansible-playbook ansible/site.yml --syntax-check ``` -Preferred for role or vars changes: + +Preferred for role, template, or vars changes: ```bash ansible-playbook ansible/site.yml --limit --check --diff ``` + Useful supporting checks: ```bash ansible-inventory --graph ansible-inventory --host ansible-lint ansible/site.yml yamllint ansible/ +sh -n scripts/bootstrap_mail.sh ``` ## Agent workflow expectations -- Read the relevant inventory, vars, role tasks, and deployed dotfiles before editing. +- Read the relevant inventory, vars, role tasks, templates, and deployed dotfiles before editing. - Do not change unrelated files to "clean things up". -- Keep README and AGENTS aligned when workflows materially change. +- Keep `README.md` and `AGENTS.md` aligned when workflows materially change. - If you add a new operational area, also add the validation command agents should run. - Prefer host-limited validation before broad apply. - Call out any verification you could not run.