Add Google Gemini as a macOS FMA - #44268
Conversation
Register Google Gemini for macOS: add a Homebrew input (ee/maintained-apps/inputs/homebrew/gemini.json), update apps index (ee/maintained-apps/outputs/apps.json), and add a darwin output with installer/uninstaller scripts and version 1.49.2.233 (ee/maintained-apps/outputs/google-gemini/darwin.json). Also add frontend icon component and asset (frontend/pages/SoftwarePage/components/icons/Gemini.tsx, updated icon index) and a 60x60@2x PNG app icon (website/assets/images/app-icon-google-gemini-60x60@2x.png). Includes installer URL and script refs for install/uninstall, plus app relaunch handling.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #44268 +/- ##
==========================================
- Coverage 66.79% 66.72% -0.07%
==========================================
Files 2626 2627 +1
Lines 211228 211166 -62
Branches 9547 9429 -118
==========================================
- Hits 141080 140908 -172
- Misses 57331 57478 +147
+ Partials 12817 12780 -37
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Rename ee/maintained-apps/inputs/homebrew/gemini.json to ee/maintained-apps/inputs/homebrew/google-gemini.json. File contents remain unchanged (100% similarity); change clarifies the provider name to avoid ambiguity.
Add a "frozen": true flag to the Homebrew input for google-gemini and update the darwin output version from 1.49.2.233 to 1.51.0.245. This marks the app as frozen in inputs and records the new macOS release in outputs.
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
WalkthroughThis pull request adds support for the Google Gemini macOS application to the Fleet-maintained apps system. It includes a new homebrew input configuration file that specifies the app's metadata (name, identifier, token, installer format, catalog slug, and default category), updates the apps registry output file, creates a darwin-specific deployment configuration with installation and uninstallation logic, adds a new React icon component for the Gemini app, and registers the Gemini icon in the frontend icon mapping system. Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Registers Google Gemini as a maintained macOS app, including install/uninstall automation and UI/icon support.
Changes:
- Add Homebrew input definition for Google Gemini (token/slug/identifier + categories).
- Add maintained-app catalog entries (apps index + macOS/darwin output with installer + scripts).
- Add Gemini icon component + mapping, plus website PNG asset.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
ee/maintained-apps/inputs/homebrew/google-gemini.json |
Defines the Homebrew token/metadata for the Gemini maintained app. |
ee/maintained-apps/outputs/apps.json |
Registers Gemini in the maintained apps index for darwin. |
ee/maintained-apps/outputs/google-gemini/darwin.json |
Adds macOS installer URL, detection queries, and install/uninstall script refs for Gemini. |
frontend/pages/SoftwarePage/components/icons/Gemini.tsx |
Adds the Gemini icon component (base64 PNG embedded in SVG). |
frontend/pages/SoftwarePage/components/icons/index.ts |
Wires Gemini icon into the SoftwarePage icon lookup map. |
website/assets/images/app-icon-google-gemini-60x60@2x.png |
Adds a website app icon asset for Gemini. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "version": "1.51.0.245", | ||
| "queries": { | ||
| "exists": "SELECT 1 FROM apps WHERE bundle_identifier = 'com.google.GeminiMacOS';", | ||
| "patched": "SELECT 1 WHERE NOT EXISTS (SELECT 1 FROM apps WHERE bundle_identifier = 'com.google.GeminiMacOS' AND version_compare(bundle_short_version, '1.51.0.245') < 0);" | ||
| }, |
There was a problem hiding this comment.
PR description mentions adding a darwin output with version 1.49.2.233, but this output is declaring version 1.51.0.245 (and the patched query is also keyed to 1.51.0.245). Please confirm which version should be shipped and update either the PR description or this file so they match (including the version_compare string).
| { | ||
| "name": "Gemini", | ||
| "unique_identifier": "com.google.GeminiMacOS", | ||
| "token": "google-gemini", | ||
| "installer_format": "dmg", | ||
| "slug": "google-gemini/darwin", | ||
| "default_categories": ["Productivity"] |
There was a problem hiding this comment.
The PR description calls out adding the Homebrew input as ee/maintained-apps/inputs/homebrew/gemini.json, but the change actually adds google-gemini.json (which matches the existing naming convention where filename == token). Please update the PR description to avoid confusion for reviewers and future maintainers.
| ], | ||
| "refs": { | ||
| "6e7ec188": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n eval \"export $var_name=0\"\n return\n fi\n\n # App was running, mark it for relaunch\n eval \"export $var_name=1\"\n echo \"Application '$bundle_id' was running; will relaunch after installation.\"\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name=\"APP_WAS_RUNNING_$(echo \"$bundle_id\" | tr '.-' '__')\"\n local was_running\n\n # Check if the app was running before installation\n eval \"was_running=\\$$var_name\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping relaunching application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Relaunching application '$bundle_id'...\"\n\n # Launch the app in the logged-in user's GUI session. Apps launched by root\n # won't register with the user's Dock/GUI, so run 'open' as the console user.\n # Use 'launchctl asuser' to bootstrap into the console user's Mach namespace\n # and GUI session — 'sudo -u' alone doesn't do this, which can cause\n # LSOpenURLsWithRole() failures even when 'open' exits 0.\n local open_status=0\n if [[ $EUID -eq 0 ]]; then\n local console_uid\n console_uid=$(id -u \"$console_user\")\n /bin/launchctl asuser \"$console_uid\" sudo -u \"$console_user\" open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n else\n open -b \"$bundle_id\" >/dev/null 2>&1 || open_status=$?\n fi\n\n if [[ $open_status -eq 0 ]]; then\n echo \"Application '$bundle_id' relaunched successfully.\"\n else\n echo \"Failed to relaunch application '$bundle_id'.\"\n fi\n}\n\n\n# extract contents\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'com.google.GeminiMacOS'\nif [ -d \"$APPDIR/Gemini.app\" ]; then\n\tsudo mv \"$APPDIR/Gemini.app\" \"$TMPDIR/Gemini.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Gemini.app\" \"$APPDIR\"\nrelaunch_application 'com.google.GeminiMacOS'\n", | ||
| "e78a7042": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\n\nexpand_pkgid_and_map() {\n local PKGID=\"$1\"\n local FUNC=\"$2\"\n if [[ \"$PKGID\" == *\"*\" ]]; then\n local prefix=\"${PKGID%\\*}\"\n echo \"Expanding wildcard for PKGID: $PKGID\"\n for receipt in $(pkgutil --pkgs | grep \"^${prefix}\"); do\n echo \"Processing $receipt\"\n \"$FUNC\" \"$receipt\"\n done\n else\n \"$FUNC\" \"$PKGID\"\n fi\n}\n\nforget_pkg() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" forget_receipt\n}\n\nforget_receipt() {\n local PKGID=\"$1\"\n sudo pkgutil --forget \"$PKGID\"\n}\n\nquit_application() {\n local bundle_id=\"$1\"\n local timeout_duration=10\n\n # check if the application is running\n local app_running\n app_running=$(osascript -e \"application id \\\"$bundle_id\\\" is running\" 2>/dev/null)\n if [[ \"$app_running\" != \"true\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ -z \"$console_user\" || \"$console_user\" == \"root\" || \"$console_user\" == \"loginwindow\" ]]; then\n echo \"Not logged into a non-root GUI; skipping quitting application ID '$bundle_id'.\"\n return\n fi\n\n echo \"Quitting application '$bundle_id'...\"\n\n # try to quit the application within the timeout period\n local quit_success=false\n SECONDS=0\n while (( SECONDS < timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" >/dev/null 2>&1; then\n if ! pgrep -f \"$bundle_id\" >/dev/null 2>&1; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2>/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\nremove_pkg_files() {\n local PKGID=\"$1\"\n expand_pkgid_and_map \"$PKGID\" remove_receipt_files\n}\n\nremove_receipt_files() {\n local PKGID=\"$1\"\n local PKGINFO VOLUME INSTALL_LOCATION FULL_INSTALL_LOCATION\n\n echo \"pkgutil --pkg-info-plist \\\"$PKGID\\\"\"\n PKGINFO=$(pkgutil --pkg-info-plist \"$PKGID\")\n VOLUME=$(echo \"$PKGINFO\" | awk '/<key>volume<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n INSTALL_LOCATION=$(echo \"$PKGINFO\" | awk '/<key>install-location<\\/key>/ {getline; gsub(/.*<string>|<\\/string>.*/, \"\"); print}')\n\n if [ -z \"$INSTALL_LOCATION\" ] || [ \"$INSTALL_LOCATION\" = \"/\" ]; then\n FULL_INSTALL_LOCATION=\"$VOLUME\"\n else\n FULL_INSTALL_LOCATION=\"$VOLUME/$INSTALL_LOCATION\"\n FULL_INSTALL_LOCATION=$(echo \"$FULL_INSTALL_LOCATION\" | sed 's|//|/|g')\n fi\n\n echo \"sudo pkgutil --only-files --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-files --files \"$PKGID\" | sed \"s|^|/${INSTALL_LOCATION}/|\" | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n echo \"sudo pkgutil --only-dirs --files \\\"$PKGID\\\" | sed \\\"s|^|${FULL_INSTALL_LOCATION}/|\\\" | grep '\\\\.app$' | tr '\\\\\\\\n' '\\\\\\\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\"\n sudo pkgutil --only-dirs --files \"$PKGID\" | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" | grep '\\.app$' | tr '\\n' '\\0' | /usr/bin/sudo -u root -E -- /usr/bin/xargs -0 -- /bin/rm -rf\n\n root_app_dir=$(\n sudo pkgutil --only-dirs --files \"$PKGID\" \\\n | sed \"s|^|${FULL_INSTALL_LOCATION}/|\" \\\n | grep 'Applications' \\\n | awk '{ print length, $0 }' \\\n | sort -n \\\n | head -n1 \\\n | cut -d' ' -f2-\n )\n if [ -n \"$root_app_dir\" ]; then\n echo \"sudo rmdir -p \\\"$root_app_dir\\\" 2>/dev/null || :\"\n sudo rmdir -p \"$root_app_dir\" 2>/dev/null || :\n fi\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nquit_application 'com.google.GeminiMacOS'\nquit_application 'com.google.GeminiMacOS.launcher'\nsudo rm -rf \"$APPDIR/Gemini.app\"\nremove_launchctl_service 'com.google.GoogleUpdater.wake.system'\nremove_launchctl_service 'com.google.keystone.agent'\nremove_launchctl_service 'com.google.keystone.daemon'\nremove_launchctl_service 'com.google.keystone.system.agent'\nremove_launchctl_service 'com.google.keystone.system.xpcservice'\nremove_launchctl_service 'com.google.keystone.xpcservice'\nremove_pkg_files 'com.google.pkg.Keystone'\nforget_pkg 'com.google.pkg.Keystone'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.google.GeminiMacOS'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.google.GeminiMacOS.launcher'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.google.GeminiMacOS'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.google.GeminiMacOS.launcher'\ntrash $LOGGED_IN_USER '~/Library/Google/GoogleSoftwareUpdate/Actives/com.google.GeminiMacOS'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.google.GeminiMacOS'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.google.GeminiMacOS.plist'\n" |
There was a problem hiding this comment.
In the embedded uninstall script, remove_receipt_files() logs a command that prefixes paths with ${FULL_INSTALL_LOCATION}, but the actual pkgutil --only-files pipeline prefixes with /${INSTALL_LOCATION} instead. This mismatch looks unintentional and can produce incorrect paths if volume is not / (or just makes debugging misleading). Please align the command with the echoed ${FULL_INSTALL_LOCATION} prefix (or update the echo) so behavior and logging match.
Register Google Gemini for macOS: add a Homebrew input (ee/maintained-apps/inputs/homebrew/gemini.json), update apps index (ee/maintained-apps/outputs/apps.json), and add a darwin output with installer/uninstaller scripts and version 1.49.2.233 (ee/maintained-apps/outputs/google-gemini/darwin.json). Also add frontend icon component and asset (frontend/pages/SoftwarePage/components/icons/Gemini.tsx, updated icon index) and a 60x60@2x PNG app icon (website/assets/images/app-icon-google-gemini-60x60@2x.png). Includes installer URL and script refs for install/uninstall, plus app relaunch handling.
Summary by CodeRabbit