macbook-qa

Log | Files | Refs | README

claude.md (6538B)


      1 # Ansible macOS QA Workstation Provisioning
      2 
      3 ## Project Overview
      4 
      5 Ansible playbook and roles for provisioning a MacBook Pro for a QA analyst.
      6 Targets macOS (Apple Silicon and Intel compatible). Uses Ansible roles with
      7 proper plugin-based task modules wherever possible — avoid `command`/`shell`
      8 one-offs unless there is no suitable module.
      9 
     10 ---
     11 
     12 ## Target Environment
     13 
     14 - **OS**: macOS (Sonoma or later)
     15 - **User**: QA analyst
     16 - **Connection**: `local` (runs on the Mac itself via `ansible-playbook --connection=local`)
     17 - **Python**: System Python or Homebrew Python; do not assume a specific path
     18 
     19 ---
     20 
     21 ## Role Structure
     22 
     23 Organize as individual roles under `roles/`. Each role is self-contained with
     24 its own `tasks/`, `defaults/`, `vars/`, `handlers/`, and `meta/` directories
     25 as needed.
     26 
     27 ```
     28 roles/
     29   homebrew/
     30   xcode_cli/
     31   chrome/
     32   iterm2/
     33   powerlevel10k/
     34 ```
     35 
     36 Entry point: `site.yml` applies all roles in order.
     37 
     38 ---
     39 
     40 ## Roles — Requirements and Constraints
     41 
     42 ### homebrew
     43 
     44 - Use the [`community.general.homebrew`](https://docs.ansible.com/ansible/latest/collections/community/general/homebrew_module.html) module for all package installs.
     45 - Use [`community.general.homebrew_cask`](https://docs.ansible.com/ansible/latest/collections/community/general/homebrew_cask_module.html) for cask installs.
     46 - Bootstrap Homebrew itself only if it is not already installed. Use a
     47   `stat` module check on `/opt/homebrew/bin/brew` (Apple Silicon) and
     48   `/usr/local/bin/brew` (Intel) to detect presence before bootstrapping.
     49 - Bootstrapping Homebrew requires a `command` task (the install script);
     50   this is acceptable as a narrow exception since no plugin covers it.
     51 - Set `HOMEBREW_NO_AUTO_UPDATE=1` in the environment for install tasks to
     52   keep runs fast and deterministic.
     53 - Tap `homebrew/cask` if not already present using `community.general.homebrew_tap`.
     54 
     55 ### xcode_cli
     56 
     57 - Install Xcode Command Line Tools.
     58 - Check for existing install using the `stat` module on
     59   `/Library/Developer/CommandLineTools/usr/bin/git`.
     60 - If absent, trigger install via `command: xcode-select --install` — this is
     61   acceptable since no module covers CLT installation.
     62 - After triggering, wait for completion using a `wait_for` or loop on the
     63   `stat` check with `retries` and `delay`. Document that this step may
     64   require user interaction on a fresh machine.
     65 - Register the result and skip if already installed.
     66 
     67 ### chrome
     68 
     69 - Install Google Chrome via `community.general.homebrew_cask` with
     70   `name: google-chrome`.
     71 - Ensure the role is idempotent: running it twice should produce no changes
     72   the second time.
     73 
     74 ### iterm2
     75 
     76 - Install iTerm2 via `community.general.homebrew_cask` with `name: iterm2`.
     77 - Idempotent same as chrome role.
     78 - Optionally apply a default preferences plist if a `files/com.googlecode.iterm2.plist`
     79   is present in the role — use the `ansible.builtin.copy` module to place it
     80   at `~/Library/Preferences/com.googlecode.iterm2.plist`.
     81 
     82 ### powerlevel10k
     83 
     84 - Install the `powerlevel10k` theme for Zsh using Homebrew:
     85   - Tap `romkatv/powerlevel10k` via `community.general.homebrew_tap`.
     86   - Install `powerlevel10k` via `community.general.homebrew`.
     87 - Use `ansible.builtin.lineinfile` to add the sourcing line to `~/.zshrc`:
     88   ```
     89   source $(brew --prefix)/opt/powerlevel10k/powerlevel10k.zsh-theme
     90   ```
     91   Use `regexp` to make this idempotent (do not add duplicate lines).
     92 - Do **not** attempt to run the interactive `p10k configure` wizard — document
     93   in comments that the user must run this manually on first login.
     94 - Optionally copy a `files/.p10k.zsh` into `~/` using `ansible.builtin.copy`
     95   if the file exists in the role, so a pre-baked config can be committed.
     96 
     97 ---
     98 
     99 ## Module / Plugin Preferences
    100 
    101 | Task Type              | Preferred Module                          | Avoid              |
    102 |------------------------|-------------------------------------------|--------------------|
    103 | Install brew packages  | `community.general.homebrew`              | `command: brew`    |
    104 | Install cask apps      | `community.general.homebrew_cask`         | `command: brew`    |
    105 | Manage taps            | `community.general.homebrew_tap`          | `command: brew tap`|
    106 | File presence check    | `ansible.builtin.stat`                    | `command: ls`      |
    107 | Edit shell config      | `ansible.builtin.lineinfile`              | `command: echo >>` |
    108 | Copy config files      | `ansible.builtin.copy`                    | `command: cp`      |
    109 | Directory creation     | `ansible.builtin.file` (state: directory) | `command: mkdir`   |
    110 | Conditional skips      | `when: result.stat.exists`                | —                  |
    111 
    112 ---
    113 
    114 ## Collections
    115 
    116 Declare required collections in `requirements.yml`:
    117 
    118 ```yaml
    119 collections:
    120   - name: community.general
    121     version: ">=8.0.0"
    122 ```
    123 
    124 Install before running: `ansible-galaxy collection install -r requirements.yml`
    125 
    126 ---
    127 
    128 ## Inventory
    129 
    130 Use a minimal local inventory. Suggested `inventory/local.ini`:
    131 
    132 ```ini
    133 [mac]
    134 localhost ansible_connection=local ansible_python_interpreter=/usr/bin/python3
    135 ```
    136 
    137 ---
    138 
    139 ## Variables
    140 
    141 Define sensible defaults in each role's `defaults/main.yml`. Example for
    142 `homebrew` role:
    143 
    144 ```yaml
    145 homebrew_prefix_arm: /opt/homebrew
    146 homebrew_prefix_intel: /usr/local
    147 homebrew_packages: []
    148 homebrew_casks: []
    149 ```
    150 
    151 The top-level `group_vars/all.yml` can set QA-specific packages if needed.
    152 
    153 ---
    154 
    155 ## Idempotency Requirements
    156 
    157 Every task must be idempotent. Running the playbook multiple times must
    158 produce zero changes after the first successful run. Use:
    159 
    160 - `stat` + `when: not result.stat.exists` guards for bootstrapping steps
    161 - Module-native idempotency for Homebrew tasks (modules handle this)
    162 - `lineinfile` with `regexp` for shell config edits
    163 
    164 ---
    165 
    166 ## Error Handling
    167 
    168 - Use `ignore_errors: false` (default) — fail loudly.
    169 - For the Xcode CLT step, note in a comment that the task may time out on a
    170   fresh machine if the user does not click through the GUI dialog. Consider
    171   adding a `retries: 30 / delay: 10` loop on the stat check post-trigger.
    172 
    173 ---
    174 
    175 ## Running the Playbook
    176 
    177 ```bash
    178 # Install dependencies
    179 ansible-galaxy collection install -r requirements.yml
    180 
    181 # Dry run
    182 ansible-playbook -i inventory/local.ini site.yml --check --diff
    183 
    184 # Apply
    185 ansible-playbook -i inventory/local.ini site.yml
    186 ```
    187 
    188 ---
    189 
    190 ## Out of Scope
    191 
    192 - Managing macOS system preferences via `defaults write` (can be a follow-on role)
    193 - Configuring SSH keys or Git identity
    194 - Installing language runtimes (Node, Python, Ruby) — add roles as needed
    195 - Any CI/CD pipeline integration