fix(homebrew): pre-create /usr/local/cli-plugins for docker cask; modernize fact refs#7
Merged
Conversation
…ernize fact refs Two related fixes surfaced during a fresh Mac Studio bootstrap run. 1) docker-desktop cask install failed mid-run with 'sudo: a terminal is required to read the password' (#5). Docker Desktop's cask postinstall hook does `sudo mkdir -p /usr/local/cli-plugins` even on Apple Silicon (it keeps the Intel-era /usr/local convention for CLI plugins). On a fresh Mac that directory does not exist and /usr/local/ is root-owned, so the cask's internal sudo call fails when Ansible runs brew under `become: false` (no TTY). Fix: new pre-task in roles/homebrew that runs under `become: true` and creates /usr/local/cli-plugins owned by the invoking user before the cask install loop. Once the directory exists, brew's postinstall hook skips its sudo step entirely. The pre-task is guarded by a `when:` clause that only triggers when the cask list actually includes docker or docker-desktop, so non-Docker deployments stay sudo-free. For the new pre-task to work non-interactively, bootstrap.sh now passes --ask-become-pass to ansible-playbook so Ansible prompts the user for sudo password once at the start of the run. This is a one-time UX cost in exchange for clean idempotent installs. 2) Eight references to the deprecated top-level fact `ansible_user_dir` across group_vars/all.yml, roles/foreman, roles/developer_tools, and roles/opencode (#6). ansible-core 2.24 removes auto-injection of `ansible_*` top-level facts; the supported form is `ansible_facts['user_dir']`. Swept all occurrences. Behavior identical today, but the deprecation warning no longer fires and the playbook will keep working when ansible-core 2.24 lands. Verification: - ansible-playbook --syntax-check: clean - Diff is mechanical (~30 lines across 6 files) - CI's yamllint + ansible-lint + shellcheck + macos-check + macos-homebrew jobs are the safety net; macos-homebrew will exercise the cask install end-to-end on a fresh GitHub-hosted Apple Silicon runner. Fixes #5 Fixes #6
This was referenced May 23, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two related fixes that surfaced during a fresh Mac Studio bootstrap
run today:
/usr/local/cli-pluginsso the docker-desktopcask install no longer needs sudo. Add
--ask-become-passtobootstrap.sh so the new
become: truepre-task can acquirecredentials.
ansible_user_dirtop-level fact to the supported
ansible_facts['user_dir']form.Why
Fixes #5
Fixes #6
./bootstrap.shdied mid-run installing the docker-desktop cask:Root cause: Docker Desktop's cask postinstall hook does
sudo mkdir -p /usr/local/cli-pluginseven on Apple Silicon. On a fresh Macthat directory does not exist, /usr/local/ is root-owned, and brew
runs under
become: falseso there is no TTY for sudo to prompt.Pre-creating the directory under the invoking user removes the
sudo step from brew's cask path entirely. The post-fix invocation
of
brew install --cask docker-desktopis a pure-user-space install.The deprecation warning was a free win to bundle with #5: same
playbook file set, easy mechanical sweep, future-proofs against
ansible-core 2.24.
How
#5 — docker-desktop sudo
roles/homebrew/tasks/main.ymlgains a pre-task before the caskloop:
The
when:clause keeps non-Docker deployments sudo-free.bootstrap.shadds--ask-become-passto theansible-playbookinvocation so Ansible can prompt for sudo password once when the
new pre-task runs.
#6 — deprecated fact references
Eight occurrences swept from
{{ ansible_user_dir }}to{{ ansible_facts['user_dir'] }}:group_vars/all.yml× 3 (src_dir, model_store, foreman_workspace_root)roles/foreman/tasks/main.yml× 1roles/developer_tools/tasks/main.yml× 2roles/opencode/tasks/main.yml× 2No behavior change today; the playbook keeps working when
ansible-core 2.24 ships and removes the auto-injection.
Verification
Local:
ansible-playbook --syntax-checkcleanansible_user_dirreferences remain anywhereCI:
run on every PR
macos-homebrewis the high-leverage check here: it runsansible-playbook ... --tags 'always,system,homebrew'againsta fresh GitHub-hosted Apple Silicon runner, which would have
caught [BUG] homebrew cask install of docker-desktop fails: sudo without TTY when creating /usr/local/cli-plugins #5 if the CI gap from [FEATURE] CI: smoke-test ./bootstrap.sh end-to-end (closes the gap that let #2 ship) #4 (
./bootstrap.shentrypointnot exercised) had not also let the EXTRA_ARGS bug slip
bootstrap.sh), so it exercises the homebrew role end-to-end and
will fail if the cask install can't complete
Related
Checklist
users (no doc change in this PR; the
--ask-become-passwillbecome visible in the form of a one-time sudo prompt)