Fix Docker Desktop macOS FMA not reporting an installed version - #50885
Conversation
The Docker Desktop macOS Fleet-maintained app matched on "com.electron.dockerdesktop", which belongs to the embedded Electron bundle at /Applications/Docker.app/Contents/MacOS/Docker Desktop.app. The installed app is /Applications/Docker.app, which reports "com.docker.docker". That was harmless until #47831 added `path NOT LIKE '%.app/Contents/%'` to the macOS software inventory query, which drops embedded bundles. Since then nothing in inventory carries the embedded identifier, so the FMA's software title had no installed versions: no installed version, no "Installed" status, and "Install" offered on hosts that already had Docker Desktop. Patch policies kept working because they run the FMA's SQL against the host's unfiltered apps table. Point the app at "com.docker.docker" and add a migration that re-points already-added Docker Desktop installers to the correct software title, since fleet_maintained_apps self-heals on catalog sync but installer bindings do not. Fixes #50875
The migration re-points a foreign key rather than modifying these records, so assign updated_at to itself on the five tables whose updated_at is ON UPDATE CURRENT_TIMESTAMP (software_installers, host_software_installs, software_install_upcoming_activities, policies, software_title_team_pins).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
WalkthroughDocker Desktop macOS detection now uses Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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.
Warning
- Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.
Pull request overview
This PR fixes a macOS Fleet-maintained app (FMA) mismatch for Docker Desktop where the catalog’s unique_identifier pointed at a nested Electron bundle, but software inventory filtering retains only the top-level /Applications/Docker.app bundle identifier—causing Docker Desktop to appear not installed and not report a version.
Changes:
- Update Docker Desktop FMA identifiers and generated outputs to use
com.docker.docker(top-level bundle) for matching against inventory. - Add a MySQL data migration to rebind existing installer/title references from the stale title to the inventory title (or relabel in place when inventory title doesn’t exist).
- Update Homebrew ingester behavior/tests to reflect the new identifier and keep
.backpath handling for patch status.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| server/datastore/mysql/schema.sql | Bumps migration status seed/auto-increment to include the new migration. |
| server/datastore/mysql/migrations/tables/20260810152924_FixDockerDesktopBundleIdentifier.go | Adds migration to relabel/merge stale Docker Desktop software title and re-point related references. |
| server/datastore/mysql/migrations/tables/20260810152924_FixDockerDesktopBundleIdentifier_test.go | Adds migration test coverage for relabel/merge/skip scenarios and timestamp preservation. |
| ee/maintained-apps/outputs/docker-desktop/darwin.json | Regenerates Docker Desktop output queries to use com.docker.docker. |
| ee/maintained-apps/outputs/apps.json | Updates Docker Desktop entry’s unique_identifier to com.docker.docker. |
| ee/maintained-apps/inputs/homebrew/docker-desktop.json | Updates Docker Desktop input unique_identifier to com.docker.docker. |
| ee/maintained-apps/ingesters/homebrew/ingester.go | Clarifies .back handling comment for Docker Desktop patch query generation. |
| ee/maintained-apps/ingesters/homebrew/ingester_test.go | Updates expectations for Docker Desktop identifier and generated queries. |
| changes/50875-docker-desktop-installed-version | Release note entry (content excluded from review). |
Files excluded by content exclusion policy (1)
- changes/50875-docker-desktop-installed-version
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| for _, si := range staleInstallers { | ||
| if _, blocked := blockedTeams[si.teamID]; blocked { | ||
| continue | ||
| } | ||
| if _, err := tx.Exec( | ||
| `UPDATE software_installers SET title_id = ?, updated_at = updated_at WHERE id = ?`, | ||
| targetTitleID, si.id, | ||
| ); err != nil { | ||
| return fmt.Errorf("re-pointing Docker Desktop installer %d: %w", si.id, err) | ||
| } | ||
| } | ||
|
|
||
| // Install history and queued installs have no title-scoped unique key, so they move | ||
| // wholesale. | ||
| for _, t := range []titleRefColumn{ | ||
| {"host_software_installs", "software_title_id", true}, | ||
| {"software_install_upcoming_activities", "software_title_id", true}, | ||
| {"software", "title_id", false}, | ||
| {"policies", "patch_software_title_id", true}, | ||
| } { | ||
| if _, err := tx.Exec( | ||
| fmt.Sprintf(`UPDATE %s SET %s = ?%s WHERE %s = ?`, | ||
| t.table, t.column, t.preserveUpdatedAt(), t.column), | ||
| targetTitleID, staleTitleID, | ||
| ); err != nil { |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #50885 +/- ##
==========================================
- Coverage 68.53% 68.53% -0.01%
==========================================
Files 3977 3978 +1
Lines 256148 256274 +126
Branches 13627 13627
==========================================
+ Hits 175557 175630 +73
- Misses 64977 65010 +33
- Partials 15614 15634 +20
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Related issue: Resolves #50875
Docker Desktop on macOS never reported an installed version or "Installed" status, and offered "Install" on hosts that already had it.
Root cause
The FMA's
unique_identifierwascom.electron.dockerdesktop, which belongs to the embedded Electron bundle. The installed app reports a different identifier:/Applications/Docker.appcom.docker.docker/Applications/Docker.app/Contents/MacOS/Docker Desktop.appcom.electron.dockerdesktopThe identifier was changed from
com.docker.docker→com.electron.dockerdesktopin #37670 (Jan 5) as "the new bundle identifier … reflecting the current packaging". The top-level bundle never changed.That was latent until #47831 (#44199, Jul 9) added an embedded-bundle filter to the macOS software inventory query in
server/service/osquery_utils/queries.go:/Applications/Docker.app/Contents/MacOS/Docker Desktop.appmatches that pattern, so the only row carrying the embedded identifier is filtered out and inventory keeps just/Applications/Docker.app→com.docker.docker. Since FMA↔inventory matching is by bundle identifier (addSoftwareTitleToMatchingSoftware), the title the FMA owns had zero installed versions.Patch policies kept passing, because they run the FMA's
exists/patchedSQL directly against the host's unfilteredappstable. That's also why #50041 needed a.backpath exclusion, and why patch status and the software UI have disagreed since July.What changed
Catalog —
unique_identifieris nowcom.docker.docker, withdocker-desktop/darwin.jsonregenerated.outputs/apps.jsonneeded a hand-edit becauseupdateAppsListFile(cmd/maintained-apps/main.go) only appends new apps and never updates an existing entry's identifier — filed separately.The remaining
com.electron.dockerdesktopreferences are intentional and untouched: the install script's quit/relaunch targets (the Electron bundle is what responds to AppleScript) and the cask's zap paths. The.backexclusion in the patched query also stays —/Applications/Docker.app.backis a top-level bundle reportingcom.docker.dockerat a path the nested-bundle filter does not match, so a stale.backwould otherwise show a false "Update available".Migration (
20260810152924_FixDockerDesktopBundleIdentifier) —fleet_maintained_appsself-heals on catalog sync (UpsertMaintainedAppupdatesunique_identifieron duplicate slug) andReconcileMaintainedAppSoftwareNamesrenames the existing "Docker" title, but an already-added installer'ssoftware_installers.title_idbinding does not:com.docker.dockertitle yet → relabel the stale title in place, so everything already pointing at it stays correct.software.title_id, and per-team settings (icons, display names, pins, update schedules), then drop the stale title.Notes for reviewers
dedup_tokenis the version for FMAs and the storage_id otherwise, soidx_software_installers_dedupwould not have caught that collision. Those teams keep the pre-migration state instead of having data silently reshaped.fk_software_installers_titleisON DELETE SET NULL, so deleting it while an installer still pointed at it would orphan that installer. Note also thatfk_patch_software_title_idisON DELETE CASCADE— re-pointing the patch policy is what keeps it from being deleted outright.software_titlesaresoftware.title_id(re-pointed),software_titles_host_counts(deleted; the cron recomputes), andkernel_host_counts/in_house_app_install_tokens, neither of which can hold a macOS app title.ReconcileMaintainedAppSoftwareNamesrenames it to "Docker Desktop" on the next catalog sync, so there is a window after upgrade where the name is still "Docker". I left that to the sync rather than duplicating the rename logic in the migration.Checklist for submitter
changes/.SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements). Table/column names in the migration's generated SQL come from hardcoded struct literals, never from data; all values are placeholders.Testing
Five migration tests cover: relabel-in-place, merge-into-inventory-title, duplicate per-team settings dropped, teams with an existing installer skipped (and the stale title consequently retained), and no-op when the FMA was never added. The merge test also asserts
updated_atis not restamped. Updated the homebrew ingester test expectations for the new identifier.Verified: full
server/datastore/mysql/migrations/tablessuite passes (201s),ee/maintained-apps/...,cmd/maintained-apps/..., and the FMA datastore tests pass,make lint-go-incrementalclean.Root cause was confirmed against real bundles rather than inferred —
PlistBuddyon both Docker bundles for the identifiers above, andlsregister -dumpto confirm LaunchServices registers the nested bundles (which is why the rawappstable sees them and patch policies pass).Needs QA on a real instance: add the Docker Desktop FMA, confirm the installed version and "Installed" status appear on a host that already has it, and confirm an upgrade over an instance that already had the FMA added re-points the existing installer.
For unreleased bug fixes in a release candidate, one of:
The migration touches only rows tied to a single software title, and is a no-op on instances that never added the Docker Desktop FMA.
Database migrations
Five of the written tables have
updated_atasON UPDATE CURRENT_TIMESTAMP:software_installers,host_software_installs,software_install_upcoming_activities,policies, andsoftware_title_team_pins. Since this re-points a foreign key rather than modifying the records, each statement assignsupdated_at = updated_atso MySQL leaves them alone, with a test asserting it. (Bumping them would have been cosmetic — none of these columns drives ordering, scheduling, or invalidation; policy membership usespolicy_membership.updated_atandhosts.policy_updated_at, and the activity feed orders by the activities table — but preserving them is more faithful to what the migration actually does.)COLLATE utf8mb4_unicode_ci). No columns added or altered; this is a data-only migration.Summary by CodeRabbit