Skip to content

Add Google Gemini as a macOS FMA - #44268

Merged
allenhouchins merged 4 commits into
mainfrom
allenhouchins-gemini-macos-fma
Apr 28, 2026
Merged

Add Google Gemini as a macOS FMA#44268
allenhouchins merged 4 commits into
mainfrom
allenhouchins-gemini-macos-fma

Conversation

@allenhouchins

@allenhouchins allenhouchins commented Apr 28, 2026

Copy link
Copy Markdown
Member

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

  • New Features
    • Added support for Google Gemini app on macOS, including automated installation, uninstallation, and update detection capabilities.
    • Added Gemini app icon display in the user interface for proper visual identification.

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.
fleet-release
fleet-release previously approved these changes Apr 28, 2026
@codecov

codecov Bot commented Apr 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 66.72%. Comparing base (9ec20e6) to head (5ba74fa).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...end/pages/SoftwarePage/components/icons/Gemini.tsx 50.00% 1 Missing ⚠️
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     
Flag Coverage Δ
frontend 54.74% <50.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.
fleet-release
fleet-release previously approved these changes Apr 28, 2026
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.
fleet-release
fleet-release previously approved these changes Apr 28, 2026
@allenhouchins
allenhouchins marked this pull request as ready for review April 28, 2026 01:53
@allenhouchins
allenhouchins requested a review from a team as a code owner April 28, 2026 01:53
Copilot AI review requested due to automatic review settings April 28, 2026 01:53
@fleet-release
fleet-release requested a review from eashaw April 28, 2026 01:53

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@allenhouchins
allenhouchins merged commit 0df456d into main Apr 28, 2026
24 of 25 checks passed
@allenhouchins
allenhouchins deleted the allenhouchins-gemini-macos-fma branch April 28, 2026 01:53
@coderabbitai

coderabbitai Bot commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1db41345-0ffa-4356-83ac-b1f91a9e8332

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec20e6 and 5ba74fa.

⛔ Files ignored due to path filters (1)
  • website/assets/images/app-icon-google-gemini-60x60@2x.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • ee/maintained-apps/inputs/homebrew/google-gemini.json
  • ee/maintained-apps/outputs/apps.json
  • ee/maintained-apps/outputs/google-gemini/darwin.json
  • frontend/pages/SoftwarePage/components/icons/Gemini.tsx
  • frontend/pages/SoftwarePage/components/icons/index.ts

Walkthrough

This 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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch allenhouchins-gemini-macos-fma

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +4 to +8
"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);"
},

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +7
{
"name": "Gemini",
"unique_identifier": "com.google.GeminiMacOS",
"token": "google-gemini",
"installer_format": "dmg",
"slug": "google-gemini/darwin",
"default_categories": ["Productivity"]

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
],
"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"

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants