-
Notifications
You must be signed in to change notification settings - Fork 8
Add preflight-check for cask packages to prevent manual installation conflicts #271
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -23,6 +23,12 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: all_packages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: always | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Load cask to app name mappings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| include_vars: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| file: "{{ dev_env_dir }}/config/packages/cask-app-names.yml" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: cask_mappings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: always | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Collect all package lists | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: brew_packages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set_fact: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -104,31 +110,77 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when: all_removed_homebrew | length > 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| become: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Docker and container runtime installation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: [docker] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Preflight check for cask packages | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: cask | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check if Docker Desktop is already installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: brew list --cask docker-desktop >/dev/null 2>&1 && echo yes || echo no | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: docker_desktop_installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Get all installed casks from Homebrew (single call optimization) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: brew list --cask 2>/dev/null || true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: all_installed_casks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed_when: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| become: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Remove Docker Desktop from install list if already installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set_fact: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| all_cask_packages: "{{ all_cask_packages | difference(['docker-desktop']) }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| when: "'docker-desktop' in all_cask_packages and docker_desktop_installed.stdout == 'yes'" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check which cask packages are already installed via Homebrew | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Use cached list of installed casks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| installed_casks="{{ all_installed_casks.stdout }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for pkg in {{ all_cask_packages | map('quote') | join(' ') }}; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if echo "$installed_casks" | grep -qx "$pkg"; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$pkg" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: homebrew_cask_check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed_when: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| become: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Google Chrome installation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tags: [chrome] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check if Google Chrome is already installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: brew list --cask google-chrome >/dev/null 2>&1 && echo yes || echo no | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: google_chrome_installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check which cask packages are manually installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Use cached list of installed casks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| installed_casks="{{ all_installed_casks.stdout }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% for cask in all_cask_packages %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% if cask in cask_mappings.cask_to_app_mapping %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if [ -d "/Applications/{{ cask_mappings.cask_to_app_mapping[cask] }}" ]; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ! echo "$installed_casks" | grep -qx "{{ cask }}"; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "{{ cask }}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% endif %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% endfor %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: manual_cask_check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed_when: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| become: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+136
to
+153
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check which cask packages are manually installed | |
| shell: | | |
| # Use cached list of installed casks | |
| installed_casks="{{ all_installed_casks.stdout }}" | |
| {% for cask in all_cask_packages %} | |
| {% if cask in cask_mappings.cask_to_app_mapping %} | |
| if [ -d "/Applications/{{ cask_mappings.cask_to_app_mapping[cask] }}" ]; then | |
| if ! echo "$installed_casks" | grep -qx "{{ cask }}"; then | |
| echo "{{ cask }}" | |
| fi | |
| fi | |
| {% endif %} | |
| {% endfor %} | |
| register: manual_cask_check | |
| changed_when: false | |
| become: false | |
| - name: Check which cask packages are manually installed (stat loop) | |
| stat: | |
| path: "/Applications/{{ cask_mappings.cask_to_app_mapping[item] | default('') }}" | |
| loop: "{{ all_cask_packages }}" | |
| loop_control: | |
| label: "{{ item }}" | |
| register: manual_cask_stat_results | |
| when: item in cask_mappings.cask_to_app_mapping | |
| changed_when: false | |
| become: false | |
| - name: Set manually_installed_casks fact | |
| set_fact: | |
| manually_installed_casks: >- | |
| {{ | |
| manual_cask_stat_results.results | |
| | selectattr('stat.exists', 'defined') | |
| | selectattr('stat.exists') | |
| | map(attribute='item') | |
| | select('match', '^(?!.*(' ~ (all_installed_casks.stdout | regex_replace('\n', '|')) ~ ')).*$') | |
| | list | |
| }} |
Copilot
AI
Oct 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional check if all_cask_packages | length > 0 is redundant since the outer when condition already guards against empty lists at line 171. The else branch 'None (all already installed)' will never execute. Simplify to just {{ all_cask_packages | join(', ') }}.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
Copilot
AI
Oct 24, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition ansible_verbosity >= 0 will always be true since Ansible verbosity starts at 0 by default. This effectively means the debug message will always display. If the intent is to always show this message, remove the when condition. If conditional display is needed, use a higher verbosity threshold (e.g., >= 1).
| when: ansible_verbosity >= 0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Mapping of Homebrew cask package names to their Application bundle names | ||
| # This file is used by the preflight-check to detect manually installed applications | ||
| # Format: cask_package_name: "Application Name.app" | ||
| # | ||
| # Only include casks that: | ||
| # 1. Are commonly installed manually by users | ||
| # 2. Cause installation conflicts when already present | ||
| # 3. Have standard installation paths in /Applications | ||
paolomainardi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # | ||
| # Do NOT include casks that are rarely installed manually or have no known conflict issues. | ||
| # Example: Do not include utility casks that are typically installed as dependencies and do not conflict with manual installations. | ||
|
|
||
| cask_to_app_mapping: | ||
| google-chrome: "Google Chrome.app" | ||
| docker-desktop: "Docker.app" | ||
| visual-studio-code: "Visual Studio Code.app" | ||
| slack: "Slack.app" | ||
| zoom: "zoom.us.app" | ||
| iterm2: "iTerm.app" | ||
| ghostty: "Ghostty.app" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Homebrew cask check is redundant. The script checks if packages are in
all_installed_casks.stdout, which was already obtained by runningbrew list --cask. Since we have this data, we could filterall_cask_packagesdirectly in Ansible usingintersectinstead of executing another shell command. This would eliminate the need for shell execution and improve maintainability.