-
Notifications
You must be signed in to change notification settings - Fork 7
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
Changes from 1 commit
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,65 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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: Check which cask packages are already installed via Homebrew | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for pkg in {{ all_cask_packages | map('quote') | join(' ') }}; do | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if brew list --cask "$pkg" >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "$pkg:brew" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+123
to
+131
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| register: homebrew_cask_check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| changed_when: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| become: false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Remove Docker Desktop from install list if already installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check which cask packages are manually installed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shell: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% 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 ! brew list --cask {{ cask }} >/dev/null 2>&1; then | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "{{ cask }}:manual" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% endif %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {% endfor %} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
paolomainardi marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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.
[nitpick] The suffix tags ':brew' and ':manual' are used as markers but are stripped immediately after parsing. Consider outputting just the package names directly from the shell commands instead of adding and removing markers, which would simplify the parsing logic.
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 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,17 @@ | ||
| # 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.
|
||
|
|
||
| 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.
This shell loop executes
brew list --caskfor each package individually, resulting in N brew invocations. Consider usingbrew list --caskonce to get all installed casks, then filtering against all_cask_packages in Ansible/Jinja2, which would reduce brew command overhead significantly.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.
@claude
Uh oh!
There was an error while loading. Please reload this page.
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.
Claude finished @paolomainardi's task —— View job
Analysis of brew optimization suggestion
Review Feedback
I've analyzed the review comment about optimizing the
brew list --caskcalls. The reviewer is absolutely correct - there's a significant performance issue with the current implementation.Current Implementation Issues
Lines 117-122 - Checking Homebrew-installed casks:
Lines 128-137 - Checking manually-installed casks:
Problem: If there are N casks to check, this executes
brew list --caskup to 2N times (once per package in each task). Eachbrewinvocation has overhead for process startup, loading Homebrew's Ruby environment, etc.Recommended Optimization
Call
brew list --caskonce to get all installed casks, then use shell logic to check against that cached list:Performance Impact
Additional Improvements to Consider
The reviewer also noted other good points:
:brewand:manualsuffixes could be eliminated by outputting just package names from different streams or fileswhenalready guards against empty listsWould you like me to implement these optimizations?