Skip to content

Update Fleet-maintained apps - #48466

Closed
fleet-release wants to merge 1 commit into
mainfrom
fma-2606300518
Closed

Update Fleet-maintained apps#48466
fleet-release wants to merge 1 commit into
mainfrom
fma-2606300518

Conversation

@fleet-release

@fleet-release fleet-release commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Automated ingestion of latest Fleet-maintained app data.

Summary by CodeRabbit

  • New Features
    • Updated app install metadata for many maintained apps on macOS and Windows, including new versions, download links, and checksums.
  • Bug Fixes
    • Improved version detection so upgrade status reflects the latest available releases.
    • Updated install/uninstall behavior for a few apps to better handle app relaunching and cleanup during installation or removal.

Generated automatically with cmd/maintained-apps.

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

@github-actions

Copy link
Copy Markdown
Contributor

Script Diff Results

ee/maintained-apps/outputs/aws-cli/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/beyond-compare/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/claude/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/cloudflare-warp/darwin.json

=== Install // 1f52647e -> 9b3fd23b ===

--- /tmp/old.2dVwWM	2026-06-30 05:26:28.300422213 +0000
+++ /tmp/new.duWjzo	2026-06-30 05:26:28.301422230 +0000
@@ -96,5 +96,5 @@
 
 # install pkg files
 quit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'
-sudo installer -pkg "$TMPDIR/Cloudflare_WARP_2026.4.1390.0.pkg" -target /
+sudo installer -pkg "$TMPDIR/Cloudflare_WARP_2026.6.822.0.pkg" -target /
 relaunch_application 'com.cloudflare.1dot1dot1dot1.macos'

=== Uninstall // 0274a056 -> c3d26634 ===

--- /tmp/old.Mg94ur	2026-06-30 05:26:28.330422735 +0000
+++ /tmp/new.hGvbjG	2026-06-30 05:26:28.331422752 +0000
@@ -206,6 +206,7 @@
 remove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'
 remove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.warp.daemon'
 quit_application 'com.cloudflare.1dot1dot1dot1.macos'
+(cd /Users/$LOGGED_IN_USER && sudo '/Applications/Cloudflare WARP.app/Contents/Resources/uninstall.sh')
 remove_pkg_files 'com.cloudflare.1dot1dot1dot1.macos'
 forget_pkg 'com.cloudflare.1dot1dot1dot1.macos'
 sudo rm -rf '/usr/local/bin/warp-cli'

ee/maintained-apps/outputs/cloudflare-warp/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/codex-app/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/cryptomator/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/discord/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/discord/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/docker-desktop/darwin.json

=== Install Script (no changes) ===
=== Uninstall // dd14c72d -> 5ecd2319 ===

--- /tmp/old.OdW6fw	2026-06-30 05:26:28.560426738 +0000
+++ /tmp/new.Lw72V2	2026-06-30 05:26:28.560426738 +0000
@@ -142,6 +142,7 @@
 remove_launchctl_service 'com.docker.vmnetd'
 quit_application 'com.docker.docker'
 quit_application 'com.electron.dockerdesktop'
+quit_application 'com.electron.dockerdesktop'
 sudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'
 sudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'
 sudo rmdir '~/.docker/bin'

ee/maintained-apps/outputs/drawio/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/loom/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/loom/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/microsoft-azure-storage-explorer/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/mimestream/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/nextcloud/darwin.json

=== Install // 19e01bca -> c18d4d3b ===

--- /tmp/old.bVxKOp	2026-06-30 05:26:28.800430915 +0000
+++ /tmp/new.XWNmLF	2026-06-30 05:26:28.800430915 +0000
@@ -96,5 +96,5 @@
 
 # install pkg files
 quit_and_track_application 'com.nextcloud.desktopclient'
-sudo installer -pkg "$TMPDIR/Nextcloud-33.0.6.pkg" -target /
+sudo installer -pkg "$TMPDIR/Nextcloud-33.0.7.pkg" -target /
 relaunch_application 'com.nextcloud.desktopclient'

=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/notepadexe/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/notion-calendar/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/novabench/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/principle/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/rancher/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/reaper/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/reaper/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/reqable/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/reqable/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/requestly/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/rocket-chat/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/rstudio/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/shift/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/shift/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/spokenly/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/suspicious-package/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/tailscale-app/darwin.json

=== Install // 7093a361 -> c084062d ===

--- /tmp/old.XlKMu1	2026-06-30 05:26:29.462442438 +0000
+++ /tmp/new.KabTUu	2026-06-30 05:26:29.463442455 +0000
@@ -96,5 +96,5 @@
 
 # install pkg files
 quit_and_track_application 'io.tailscale.ipn.macsys'
-sudo installer -pkg "$TMPDIR/Tailscale-1.98.5-macos.pkg" -target /
+sudo installer -pkg "$TMPDIR/Tailscale-1.98.8-macos.pkg" -target /
 relaunch_application 'io.tailscale.ipn.macsys'

=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/tailscale/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/todoist-app/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/todoist-app/windows.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/warp/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

ee/maintained-apps/outputs/wins/darwin.json

=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR updates the maintained-app output manifests for 35+ applications across macOS (darwin.json) and Windows (windows.json). For each app, the version field, queries.patched SQL version-comparison threshold, installer_url, and sha256 checksum are advanced to the latest release. A subset of entries—Cloudflare WARP, Docker Desktop, Nextcloud, and Tailscale (macOS)—additionally replace install_script_ref and/or uninstall_script_ref with new shell script content reflecting the updated package filenames and relaunch logic.

Possibly related PRs

  • fleetdm/fleet#48387: Updates the same ee/maintained-apps/outputs/notepadexe/darwin.json and spokenly/darwin.json manifests using the identical field pattern.
  • fleetdm/fleet#48410: Updates the same ee/maintained-apps/outputs/reqable/windows.json manifest by changing the version, queries.patched target, installer_url, and sha256.
  • fleetdm/fleet#47596: Updates ee/maintained-apps/outputs/docker-desktop/darwin.json with the same field-level changes (version, patched query, installer URL, sha256).
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is on-topic but far too minimal and misses the required template sections and checklist items. Add the template sections, include the related issue, and complete or delete each checklist item with testing details.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: updating Fleet-maintained apps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fma-2606300518

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ee/maintained-apps/outputs/cloudflare-warp/darwin.json`:
- Line 19: The install script in the Cloudflare WARP updater should stop and
return a non-zero status when either quitting the app or installing the package
fails. Update `quit_and_track_application` so a timeout or failed quit is
treated as an error instead of only logging, and check the result of the `sudo
installer -pkg ... -target /` step before calling `relaunch_application`. Use
the existing `quit_and_track_application` and the installer invocation near the
end of the script to gate the rest of the flow and fail the script early on any
error.

In `@ee/maintained-apps/outputs/docker-desktop/darwin.json`:
- Around line 19-20: The uninstall flow is missing cleanup for the `hub-tool`
symlink created by the install logic in the Docker Desktop script. Update the
uninstall script to remove `/usr/local/bin/hub-tool` alongside the other
`/usr/local/bin` and `/usr/local/cli-plugins` artifacts, using the same cleanup
style as the existing `trash`/`rm -rf` removals. Make sure the change is placed
near the other binary cleanup steps so it stays in sync with the install side
that creates `hub-tool`.
- Line 20: The uninstall cleanup in the Darwin script is using quoted tilde
paths, so entries like the `rmdir` calls and the cache removals are treated as
literal strings instead of resolving to the logged-in user’s home directory.
Update the cleanup logic in `remove_launchctl_service`, `trash`, and the
uninstall sequence so home-directory paths expand correctly for the current
user, and make sure the uninstall removes the `hub-tool` binary alongside the
other `/usr/local/bin` artifacts.

In `@ee/maintained-apps/outputs/nextcloud/darwin.json`:
- Line 20: The install flow in the `c18d4d3b` script does not stop when `sudo
installer` fails, so the subsequent `relaunch_application` can hide a bad pkg
install. In the main install sequence, capture the exit status from `sudo
installer` and abort immediately on failure, returning that non-zero status
instead of continuing. Use the existing `quit_and_track_application` and
`relaunch_application` flow to place the failure check directly between them.

In `@ee/maintained-apps/outputs/tailscale-app/darwin.json`:
- Line 20: Make the Tailscale install script fail fast when app quit or package
install steps fail. In quit_and_track_application, return a non-zero status when
the app does not quit within timeout_duration, and propagate that failure to the
caller. In the main flow around sudo installer -pkg and relaunch_application,
check the installer exit status and skip relaunch if the install failed; use the
existing quit_and_track_application and relaunch_application symbols to keep the
failure path explicit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7e97a77a-599b-4b94-a6cb-5067d4bdefab

📥 Commits

Reviewing files that changed from the base of the PR and between 21c3a3f and 5c04f80.

📒 Files selected for processing (38)
  • ee/maintained-apps/outputs/aws-cli/windows.json
  • ee/maintained-apps/outputs/beyond-compare/darwin.json
  • ee/maintained-apps/outputs/claude/darwin.json
  • ee/maintained-apps/outputs/cloudflare-warp/darwin.json
  • ee/maintained-apps/outputs/cloudflare-warp/windows.json
  • ee/maintained-apps/outputs/codex-app/darwin.json
  • ee/maintained-apps/outputs/cryptomator/windows.json
  • ee/maintained-apps/outputs/discord/darwin.json
  • ee/maintained-apps/outputs/discord/windows.json
  • ee/maintained-apps/outputs/docker-desktop/darwin.json
  • ee/maintained-apps/outputs/drawio/windows.json
  • ee/maintained-apps/outputs/loom/darwin.json
  • ee/maintained-apps/outputs/loom/windows.json
  • ee/maintained-apps/outputs/microsoft-azure-storage-explorer/darwin.json
  • ee/maintained-apps/outputs/mimestream/darwin.json
  • ee/maintained-apps/outputs/nextcloud/darwin.json
  • ee/maintained-apps/outputs/notepadexe/darwin.json
  • ee/maintained-apps/outputs/notion-calendar/darwin.json
  • ee/maintained-apps/outputs/novabench/darwin.json
  • ee/maintained-apps/outputs/principle/darwin.json
  • ee/maintained-apps/outputs/rancher/darwin.json
  • ee/maintained-apps/outputs/reaper/darwin.json
  • ee/maintained-apps/outputs/reaper/windows.json
  • ee/maintained-apps/outputs/reqable/darwin.json
  • ee/maintained-apps/outputs/reqable/windows.json
  • ee/maintained-apps/outputs/requestly/darwin.json
  • ee/maintained-apps/outputs/rocket-chat/darwin.json
  • ee/maintained-apps/outputs/rstudio/windows.json
  • ee/maintained-apps/outputs/shift/darwin.json
  • ee/maintained-apps/outputs/shift/windows.json
  • ee/maintained-apps/outputs/spokenly/darwin.json
  • ee/maintained-apps/outputs/suspicious-package/darwin.json
  • ee/maintained-apps/outputs/tailscale-app/darwin.json
  • ee/maintained-apps/outputs/tailscale/windows.json
  • ee/maintained-apps/outputs/todoist-app/darwin.json
  • ee/maintained-apps/outputs/todoist-app/windows.json
  • ee/maintained-apps/outputs/warp/darwin.json
  • ee/maintained-apps/outputs/wins/darwin.json

"refs": {
"0274a056": "#!/bin/bash\n\n# variables\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.warp.daemon'\nquit_application 'com.cloudflare.1dot1dot1dot1.macos'\nremove_pkg_files 'com.cloudflare.1dot1dot1dot1.macos'\nforget_pkg 'com.cloudflare.1dot1dot1dot1.macos'\nsudo rm -rf '/usr/local/bin/warp-cli'\nsudo rm -rf '/usr/local/bin/warp-dex'\nsudo rm -rf '/usr/local/bin/warp-diag'\n/Applications/Cloudflare\\ WARP.app/Contents/Resources/uninstall.sh\ntrash $LOGGED_IN_USER '/Library/LaunchDaemons/com.cloudflare.1dot1dot1dot1.macos.warp.daemon.plist'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.cloudflare.1dot1dot1dot1.macos.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.cloudflare.1dot1dot1dot1.macos'\n",
"1f52647e": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'\nsudo installer -pkg \"$TMPDIR/Cloudflare_WARP_2026.4.1390.0.pkg\" -target /\nrelaunch_application 'com.cloudflare.1dot1dot1dot1.macos'\n"
"9b3fd23b": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'\nsudo installer -pkg \"$TMPDIR/Cloudflare_WARP_2026.6.822.0.pkg\" -target /\nrelaunch_application 'com.cloudflare.1dot1dot1dot1.macos'\n",

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail the install when the quit or pkg step fails.

quit_and_track_application only logs timeout, and sudo installer ... is unchecked. As written, the script can still exit 0 after a failed quit or failed pkg install, so Fleet may report WARP as patched when nothing changed.

Suggested fix
-#!/bin/bash
+#!/bin/bash
+set -euo pipefail
@@
   if [[ "$quit_success" = false ]]; then
-    echo "Application '$bundle_id' did not quit."
+    echo "Application '$bundle_id' did not quit." >&2
+    return 1
   fi
 }
@@
 quit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'
 sudo installer -pkg "$TMPDIR/Cloudflare_WARP_2026.6.822.0.pkg" -target /
 relaunch_application 'com.cloudflare.1dot1dot1dot1.macos'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"9b3fd23b": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'\nsudo installer -pkg \"$TMPDIR/Cloudflare_WARP_2026.6.822.0.pkg\" -target /\nrelaunch_application 'com.cloudflare.1dot1dot1dot1.macos'\n",
"9b3fd23b": "#!/bin/bash\nset -euo pipefail\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.\" >&2\n return 1\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# install pkg files\nquit_and_track_application 'com.cloudflare.1dot1dot1dot1.macos'\nsudo installer -pkg \"$TMPDIR/Cloudflare_WARP_2026.6.822.0.pkg\" -target /\nrelaunch_application 'com.cloudflare.1dot1dot1dot1.macos'\n",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/cloudflare-warp/darwin.json` at line 19, The
install script in the Cloudflare WARP updater should stop and return a non-zero
status when either quitting the app or installing the package fails. Update
`quit_and_track_application` so a timeout or failed quit is treated as an error
instead of only logging, and check the result of the `sudo installer -pkg ...
-target /` step before calling `relaunch_application`. Use the existing
`quit_and_track_application` and the installer invocation near the end of the
script to gate the rest of the flow and fail the script early on any error.

Comment on lines 19 to +20
"2c3a200a": "#!/bin/bash\n\nset -euo pipefail\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\nMOUNT_POINT=\"\"\n\ncleanup() {\n local mp=\"${MOUNT_POINT:-}\"\n if [[ -n \"$mp\" ]]; then\n if mount | grep -q \" on $mp \"; then\n hdiutil detach \"$mp\" >/dev/null 2>&1 || true\n fi\n rmdir \"$mp\" >/dev/null 2>&1 || true\n fi\n}\ntrap cleanup EXIT\n\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name\n 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 || echo \"false\")\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return 0\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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 0\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 within ${timeout_duration}s; aborting install.\" >&2\n return 1\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name\n 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:-0}\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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)\nif ! hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"; then\n echo \"Failed to mount DMG '$INSTALLER_PATH'.\" >&2\n exit 1\nfi\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\nMOUNT_POINT=\"\"\n# copy to the applications folder\nquit_and_track_application 'com.electron.dockerdesktop'\nif [ -d \"$APPDIR/Docker.app\" ]; then\n\tsudo mv \"$APPDIR/Docker.app\" \"$TMPDIR/Docker.app.bkp\"\nfi\n# Docker Desktop's own in-app updater leaves a Docker.app.back bundle alongside\n# Docker.app when it self-updates. osquery's apps table still picks up the\n# stale bundle by its bundle_identifier, which causes Fleet patch policies to\n# report Docker as out of date even after a successful upgrade.\nsudo rm -rf \"$APPDIR/Docker.app.back\"\nsudo cp -R \"$TMPDIR/Docker.app\" \"$APPDIR\"\nrelaunch_application 'com.electron.dockerdesktop'\nmkdir -p /usr/local/cli-plugins\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/cli-plugins/docker-compose\" \"/usr/local/cli-plugins/docker-compose\"\nmkdir -p /usr/local/bin\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/hub-tool\" \"/usr/local/bin/hub-tool\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/kubectl\" \"/usr/local/bin/kubectl.docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker\" \"/usr/local/bin/docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-desktop\" \"/usr/local/bin/docker-credential-desktop\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-ecr-login\" \"/usr/local/bin/docker-credential-ecr-login\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-osxkeychain\" \"/usr/local/bin/docker-credential-osxkeychain\"\n",
"dd14c72d": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"
"5ecd2319": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"

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.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Uninstall leaves the hub-tool symlink behind.

The install script creates /usr/local/bin/hub-tool, but the uninstall script never removes it. After Docker.app is deleted, that PATH entry becomes a broken symlink.

Suggested fix
 sudo rm -rf '/usr/local/bin/docker'
+sudo rm -rf '/usr/local/bin/hub-tool'
 sudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"2c3a200a": "#!/bin/bash\n\nset -euo pipefail\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\nMOUNT_POINT=\"\"\n\ncleanup() {\n local mp=\"${MOUNT_POINT:-}\"\n if [[ -n \"$mp\" ]]; then\n if mount | grep -q \" on $mp \"; then\n hdiutil detach \"$mp\" >/dev/null 2>&1 || true\n fi\n rmdir \"$mp\" >/dev/null 2>&1 || true\n fi\n}\ntrap cleanup EXIT\n\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name\n 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 || echo \"false\")\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return 0\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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 0\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 within ${timeout_duration}s; aborting install.\" >&2\n return 1\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name\n 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:-0}\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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)\nif ! hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"; then\n echo \"Failed to mount DMG '$INSTALLER_PATH'.\" >&2\n exit 1\nfi\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\nMOUNT_POINT=\"\"\n# copy to the applications folder\nquit_and_track_application 'com.electron.dockerdesktop'\nif [ -d \"$APPDIR/Docker.app\" ]; then\n\tsudo mv \"$APPDIR/Docker.app\" \"$TMPDIR/Docker.app.bkp\"\nfi\n# Docker Desktop's own in-app updater leaves a Docker.app.back bundle alongside\n# Docker.app when it self-updates. osquery's apps table still picks up the\n# stale bundle by its bundle_identifier, which causes Fleet patch policies to\n# report Docker as out of date even after a successful upgrade.\nsudo rm -rf \"$APPDIR/Docker.app.back\"\nsudo cp -R \"$TMPDIR/Docker.app\" \"$APPDIR\"\nrelaunch_application 'com.electron.dockerdesktop'\nmkdir -p /usr/local/cli-plugins\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/cli-plugins/docker-compose\" \"/usr/local/cli-plugins/docker-compose\"\nmkdir -p /usr/local/bin\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/hub-tool\" \"/usr/local/bin/hub-tool\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/kubectl\" \"/usr/local/bin/kubectl.docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker\" \"/usr/local/bin/docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-desktop\" \"/usr/local/bin/docker-credential-desktop\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-ecr-login\" \"/usr/local/bin/docker-credential-ecr-login\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-osxkeychain\" \"/usr/local/bin/docker-credential-osxkeychain\"\n",
"dd14c72d": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"
"5ecd2319": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"
sudo rm -rf '/usr/local/bin/docker'
sudo rm -rf '/usr/local/bin/hub-tool'
sudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/docker-desktop/darwin.json` around lines 19 - 20,
The uninstall flow is missing cleanup for the `hub-tool` symlink created by the
install logic in the Docker Desktop script. Update the uninstall script to
remove `/usr/local/bin/hub-tool` alongside the other `/usr/local/bin` and
`/usr/local/cli-plugins` artifacts, using the same cleanup style as the existing
`trash`/`rm -rf` removals. Make sure the change is placed near the other binary
cleanup steps so it stays in sync with the install side that creates `hub-tool`.

"refs": {
"2c3a200a": "#!/bin/bash\n\nset -euo pipefail\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath \"$INSTALLER_PATH\")\")\nMOUNT_POINT=\"\"\n\ncleanup() {\n local mp=\"${MOUNT_POINT:-}\"\n if [[ -n \"$mp\" ]]; then\n if mount | grep -q \" on $mp \"; then\n hdiutil detach \"$mp\" >/dev/null 2>&1 || true\n fi\n rmdir \"$mp\" >/dev/null 2>&1 || true\n fi\n}\ntrap cleanup EXIT\n\n# functions\n\nquit_and_track_application() {\n local bundle_id=\"$1\"\n local var_name\n 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 || echo \"false\")\n if [[ \"$app_running\" != \"true\" ]]; then\n eval \"export $var_name=0\"\n return 0\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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 0\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 within ${timeout_duration}s; aborting install.\" >&2\n return 1\n fi\n}\n\n\nrelaunch_application() {\n local bundle_id=\"$1\"\n local var_name\n 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:-0}\"\n if [[ \"$was_running\" != \"1\" ]]; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console 2>/dev/null || echo \"\")\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)\nif ! hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"; then\n echo \"Failed to mount DMG '$INSTALLER_PATH'.\" >&2\n exit 1\nfi\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\nMOUNT_POINT=\"\"\n# copy to the applications folder\nquit_and_track_application 'com.electron.dockerdesktop'\nif [ -d \"$APPDIR/Docker.app\" ]; then\n\tsudo mv \"$APPDIR/Docker.app\" \"$TMPDIR/Docker.app.bkp\"\nfi\n# Docker Desktop's own in-app updater leaves a Docker.app.back bundle alongside\n# Docker.app when it self-updates. osquery's apps table still picks up the\n# stale bundle by its bundle_identifier, which causes Fleet patch policies to\n# report Docker as out of date even after a successful upgrade.\nsudo rm -rf \"$APPDIR/Docker.app.back\"\nsudo cp -R \"$TMPDIR/Docker.app\" \"$APPDIR\"\nrelaunch_application 'com.electron.dockerdesktop'\nmkdir -p /usr/local/cli-plugins\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/cli-plugins/docker-compose\" \"/usr/local/cli-plugins/docker-compose\"\nmkdir -p /usr/local/bin\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/hub-tool\" \"/usr/local/bin/hub-tool\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/kubectl\" \"/usr/local/bin/kubectl.docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker\" \"/usr/local/bin/docker\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-desktop\" \"/usr/local/bin/docker-credential-desktop\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-ecr-login\" \"/usr/local/bin/docker-credential-ecr-login\"\n/bin/ln -h -f -s -- \"$APPDIR/Docker.app/Contents/Resources/bin/docker-credential-osxkeychain\" \"/usr/local/bin/docker-credential-osxkeychain\"\n",
"dd14c72d": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"
"5ecd2319": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.docker.helper'\nremove_launchctl_service 'com.docker.socket'\nremove_launchctl_service 'com.docker.vmnetd'\nquit_application 'com.docker.docker'\nquit_application 'com.electron.dockerdesktop'\nquit_application 'com.electron.dockerdesktop'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.socket'\nsudo rm -rf '/Library/PrivilegedHelperTools/com.docker.vmnetd'\nsudo rmdir '~/.docker/bin'\nsudo rm -rf \"$APPDIR/Docker.app\"\nsudo rm -rf '/usr/local/bin/docker'\nsudo rm -rf '/usr/local/bin/docker-credential-osxkeychain'\nsudo rm -rf '/usr/local/bin/kubectl.docker'\nsudo rm -rf '/usr/local/cli-plugins/docker-compose'\nsudo rm -rf '/usr/local/bin/docker-credential-desktop'\nsudo rm -rf '/usr/local/bin/docker-credential-ecr-login'\nsudo rmdir '~/Library/Caches/com.plausiblelabs.crashreporter.data'\nsudo rmdir '~/Library/Caches/KSCrashReports'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker-compose.backup'\ntrash $LOGGED_IN_USER '/usr/local/bin/docker.backup'\ntrash $LOGGED_IN_USER '~/.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.docker.helper.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.electron.dockerdesktop.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.bugsnag.Bugsnag/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Caches/KSCrashReports/Docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.docker.helper'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/group.com.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.docker.docker.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/Docker Desktop'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.docker.docker.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.docker-frontend.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.electron.dockerdesktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.docker-frontend.savedState'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.electron.dockerdesktop.savedState'\n"

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,220p' ee/maintained-apps/outputs/docker-desktop/darwin.json

Repository: fleetdm/fleet

Length of output: 13206


🏁 Script executed:

rg -n "hub-tool|rmdir '~/.docker/bin'|Library/Caches/com.plausiblelabs.crashreporter.data|Library/Caches/KSCrashReports" ee/maintained-apps/outputs/docker-desktop/darwin.json

Repository: fleetdm/fleet

Length of output: 12485


Fix the uninstall cleanup paths. sudo rmdir '~/.docker/bin' and the quoted ~/Library/Caches/... paths are literal strings, so they never touch the user’s home directories. The uninstall also leaves /usr/local/bin/hub-tool behind even though install creates it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/docker-desktop/darwin.json` at line 20, The
uninstall cleanup in the Darwin script is using quoted tilde paths, so entries
like the `rmdir` calls and the cache removals are treated as literal strings
instead of resolving to the logged-in user’s home directory. Update the cleanup
logic in `remove_launchctl_service`, `trash`, and the uninstall sequence so
home-directory paths expand correctly for the current user, and make sure the
uninstall removes the `hub-tool` binary alongside the other `/usr/local/bin`
artifacts.

"19e01bca": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.nextcloud.desktopclient'\nsudo installer -pkg \"$TMPDIR/Nextcloud-33.0.6.pkg\" -target /\nrelaunch_application 'com.nextcloud.desktopclient'\n",
"a2c351cf": "#!/bin/bash\n\n# variables\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.nextcloud.desktopclient'\nquit_application 'com.nextcloud.desktopclient'\nremove_pkg_files 'com.nextcloud.desktopclient'\nforget_pkg 'com.nextcloud.desktopclient'\nsudo rm -rf '/Applications/Nextcloud.app'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.nextcloud.desktopclient.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Nextcloud'\ntrash $LOGGED_IN_USER '~/Library/Caches/Nextcloud'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.nextcloud.desktopclient.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/com.nextcloud.desktopclient'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.nextcloud.desktopclient.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/Nextcloud'\n"
"a2c351cf": "#!/bin/bash\n\n# variables\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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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\nremove_launchctl_service 'com.nextcloud.desktopclient'\nquit_application 'com.nextcloud.desktopclient'\nremove_pkg_files 'com.nextcloud.desktopclient'\nforget_pkg 'com.nextcloud.desktopclient'\nsudo rm -rf '/Applications/Nextcloud.app'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.nextcloud.desktopclient.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Nextcloud'\ntrash $LOGGED_IN_USER '~/Library/Caches/Nextcloud'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.nextcloud.desktopclient.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/com.nextcloud.desktopclient'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.nextcloud.desktopclient.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/Nextcloud'\n",
"c18d4d3b": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.nextcloud.desktopclient'\nsudo installer -pkg \"$TMPDIR/Nextcloud-33.0.7.pkg\" -target /\nrelaunch_application 'com.nextcloud.desktopclient'\n"

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate pkg install failures from the new c18d4d3b script.

Line 20 runs sudo installer and then immediately continues to relaunch_application. Without an explicit failure check, a pkg install error can be masked by a later zero exit, so the MDM flow can treat a failed update as successful.

♻️ Proposed fix
 # install pkg files
 quit_and_track_application 'com.nextcloud.desktopclient'
-sudo installer -pkg "$TMPDIR/Nextcloud-33.0.7.pkg" -target /
+sudo installer -pkg "$TMPDIR/Nextcloud-33.0.7.pkg" -target / || exit 1
 relaunch_application 'com.nextcloud.desktopclient'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"c18d4d3b": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'com.nextcloud.desktopclient'\nsudo installer -pkg \"$TMPDIR/Nextcloud-33.0.7.pkg\" -target /\nrelaunch_application 'com.nextcloud.desktopclient'\n"
# install pkg files
quit_and_track_application 'com.nextcloud.desktopclient'
sudo installer -pkg "$TMPDIR/Nextcloud-33.0.7.pkg" -target / || exit 1
relaunch_application 'com.nextcloud.desktopclient'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/nextcloud/darwin.json` at line 20, The install
flow in the `c18d4d3b` script does not stop when `sudo installer` fails, so the
subsequent `relaunch_application` can hide a bad pkg install. In the main
install sequence, capture the exit status from `sudo installer` and abort
immediately on failure, returning that non-zero status instead of continuing.
Use the existing `quit_and_track_application` and `relaunch_application` flow to
place the failure check directly between them.

"refs": {
"3e9b0078": "#!/bin/bash\n\n# variables\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_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\n # If the target contains glob characters, expand it and move each match.\n if [[ \"$target_file\" == *[*?[]* ]]; then\n local file file_name\n local matched=false\n local i=0\n # compgen -G expands the (quoted) pattern itself, so paths containing\n # spaces glob correctly; reading line by line keeps each match intact.\n while IFS= read -r file; do\n [[ -n \"$file\" ]] || continue\n [[ -e \"$file\" || -L \"$file\" ]] || continue\n matched=true\n i=$((i + 1))\n file_name=\"$(basename \"$file\")\"\n echo \"removing $file.\"\n # The per-match counter keeps matches that share a basename from\n # overwriting each other in the trash.\n mv -f \"$file\" \"$trash/${file_name}_${timestamp}_${rand}_${i}\"\n done < <(compgen -G \"$target_file\" 2>/dev/null)\n if [[ \"$matched\" == false ]]; then\n echo \"$target_file doesn't exist.\"\n fi\n return\n fi\n\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 'io.tailscale.ipn.macsys'\nremove_pkg_files 'com.tailscale.ipn.macsys'\nforget_pkg 'com.tailscale.ipn.macsys'\nsudo rm -rf '/usr/local/bin/tailscale'\nsudo rm -rf '/usr/local/share/man/man8/tssentineld.8'\ntrash $LOGGED_IN_USER '/Library/Tailscale'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/*.io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/io.tailscale.ipn.macsys.login-item-helper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/io.tailscale.ipn.macsys.share-extension'\ntrash $LOGGED_IN_USER '~/Library/Caches/io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/Containers/io.tailscale.ipn.macos.network-extension'\ntrash $LOGGED_IN_USER '~/Library/Containers/io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/Containers/io.tailscale.ipn.macsys.login-item-helper'\ntrash $LOGGED_IN_USER '~/Library/Containers/io.tailscale.ipn.macsys.share-extension'\ntrash $LOGGED_IN_USER '~/Library/Containers/Tailscale'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/*.io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/io.tailscale.ipn.macsys'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/io.tailscale.ipn.macsys.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/io.tailscale.ipn.macsys.plist'\n",
"7093a361": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'io.tailscale.ipn.macsys'\nsudo installer -pkg \"$TMPDIR/Tailscale-1.98.5-macos.pkg\" -target /\nrelaunch_application 'io.tailscale.ipn.macsys'\n"
"c084062d": "#!/bin/bash\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# install pkg files\nquit_and_track_application 'io.tailscale.ipn.macsys'\nsudo installer -pkg \"$TMPDIR/Tailscale-1.98.8-macos.pkg\" -target /\nrelaunch_application 'io.tailscale.ipn.macsys'\n"

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== file: ee/maintained-apps/outputs/tailscale-app/darwin.json ==="
python3 - <<'PY'
import json
from pathlib import Path
p = Path('ee/maintained-apps/outputs/tailscale-app/darwin.json')
data = json.loads(p.read_text())
for k, v in data.items():
    if isinstance(v, str) and 'Tailscale-1.98.8-macos.pkg' in v:
        print('key:', k)
        print(v)
PY

echo
echo "=== nearby macOS app install scripts in maintained-apps outputs ==="
rg -n "sudo installer -pkg|set -euo pipefail|quit_and_track_application|relaunch_application" ee/maintained-apps/outputs -g '*.json' -g '*.js' -g '*.sh' | sed -n '1,200p'

echo
echo "=== tailscale file excerpt with line numbers ==="
python3 - <<'PY'
from pathlib import Path
import json
p = Path('ee/maintained-apps/outputs/tailscale-app/darwin.json')
data = json.loads(p.read_text())
for k,v in data.items():
    if isinstance(v,str) and '#!/bin/bash' in v:
        lines = v.splitlines()
        for i,l in enumerate(lines,1):
            if i <= 120:
                print(f"{i:3}: {l}")
        break
PY

Repository: fleetdm/fleet

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path
p = Path('ee/maintained-apps/outputs/tailscale-app/darwin.json')
data = json.loads(p.read_text())
print('keys:', list(data.keys())[:10], '... total', len(data))
for k, v in data.items():
    if isinstance(v, str) and '#!/bin/bash' in v:
        print('script key:', k)
        lines = v.splitlines()
        for i, l in enumerate(lines, 1):
            if 1 <= i <= min(len(lines), 120):
                print(f"{i:3}: {l}")
        break
PY

echo '---'
rg -n "sudo installer -pkg|set -euo pipefail|quit_and_track_application|relaunch_application" ee/maintained-apps/outputs -g '*.json' -g '*.js' -g '*.sh' | sed -n '1,160p'

Repository: fleetdm/fleet

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path
p = Path('ee/maintained-apps/outputs/tailscale-app/darwin.json')
data = json.loads(p.read_text())
print('keys:', list(data.keys())[:10], '... total', len(data))
for k, v in data.items():
    if isinstance(v, str) and '#!/bin/bash' in v:
        print('script key:', k)
        lines = v.splitlines()
        for i, l in enumerate(lines, 1):
            if i <= min(len(lines), 120):
                print(f"{i:3}: {l}")
        break
PY

echo '---'
rg -n "sudo installer -pkg|set -euo pipefail|quit_and_track_application|relaunch_application" ee/maintained-apps/outputs -g '*.json' -g '*.js' -g '*.sh' | sed -n '1,160p'

Repository: fleetdm/fleet

Length of output: 50370


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path

p = Path('ee/maintained-apps/outputs/tailscale-app/darwin.json')
data = json.loads(p.read_text())

print("top-level keys:", list(data.keys()))
for key in ("versions", "refs"):
    val = data.get(key)
    print(f"\n== {key} ==")
    print("type:", type(val).__name__)
    if isinstance(val, dict):
        print("entry count:", len(val))
        for k, v in val.items():
            if isinstance(v, str):
                print("entry:", k, "len:", len(v), "has installer:", "installer -pkg" in v, "has set -e:", "set -e" in v)
            else:
                print("entry:", k, "type:", type(v).__name__)
PY

echo
echo "== tailscale-only text search =="
rg -n "installer -pkg|quit_and_track_application|relaunch_application|set -e|pipefail|exit 1" ee/maintained-apps/outputs/tailscale-app/darwin.json

Repository: fleetdm/fleet

Length of output: 3804


Fail the Tailscale install on quit/package errors
quit_and_track_application only logs a timeout, and the sudo installer -pkg ... call is unchecked, so the script can still exit 0 even when the app never quits or the package never installs. That can leave Fleet with a false patched state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ee/maintained-apps/outputs/tailscale-app/darwin.json` at line 20, Make the
Tailscale install script fail fast when app quit or package install steps fail.
In quit_and_track_application, return a non-zero status when the app does not
quit within timeout_duration, and propagate that failure to the caller. In the
main flow around sudo installer -pkg and relaunch_application, check the
installer exit status and skip relaunch if the install failed; use the existing
quit_and_track_application and relaunch_application symbols to keep the failure
path explicit.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: test-fma-pr-only

Failed stage: Filter apps.json and verify changed apps [❌]

Failed test name: ""

Failure summary:

The action failed during the app validation step because the Wins (wins/darwin) app version check
did not match what was installed/detected:
- The validator expected version 3.4, but osquery
detected Wins.app as version 3.3 (Found app: 'Wins' ... Version: 3.3).
- This caused a hard error:
App version '3.4' was not found by osquery and the step exited with exit status 1 / Process
completed with exit code 1.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

168:  * [new branch]            11938-loadtesting-branch    -> origin/11938-loadtesting-branch
169:  * [new branch]            12007-ui-idp-metadata       -> origin/12007-ui-idp-metadata
170:  * [new branch]            12351-try-fix-for-fleet-desktop -> origin/12351-try-fix-for-fleet-desktop
171:  * [new branch]            12356-puppet-callback       -> origin/12356-puppet-callback
172:  * [new branch]            12375-query-bulk-action     -> origin/12375-query-bulk-action
173:  * [new branch]            12474-documentation         -> origin/12474-documentation
174:  * [new branch]            12614-federated-auth-support -> origin/12614-federated-auth-support
175:  * [new branch]            12687-host-issues-query     -> origin/12687-host-issues-query
176:  * [new branch]            12696-loadtest-cis-changes  -> origin/12696-loadtest-cis-changes
177:  * [new branch]            12842-fleetd-bitlocker-management -> origin/12842-fleetd-bitlocker-management
178:  * [new branch]            12842-fleetd-bitlocker-table -> origin/12842-fleetd-bitlocker-table
179:  * [new branch]            12889-faster-software       -> origin/12889-faster-software
180:  * [new branch]            13287-loadtest-changes      -> origin/13287-loadtest-changes
181:  * [new branch]            13288-poc-fleet-fips        -> origin/13288-poc-fleet-fips
182:  * [new branch]            13485-query-reports-migration -> origin/13485-query-reports-migration
183:  * [new branch]            13574-cache-failed-policy-counts -> origin/13574-cache-failed-policy-counts
184:  * [new branch]            13657-docs                  -> origin/13657-docs
...

196:  * [new branch]            146orch                     -> origin/146orch
197:  * [new branch]            14753-windows-ps1-api       -> origin/14753-windows-ps1-api
198:  * [new branch]            14957-test-dep-screen       -> origin/14957-test-dep-screen
199:  * [new branch]            14957-test-dep-screen-patch -> origin/14957-test-dep-screen-patch
200:  * [new branch]            15068-disk-encryption-ii    -> origin/15068-disk-encryption-ii
201:  * [new branch]            15082-download-api-docs     -> origin/15082-download-api-docs
202:  * [new branch]            15082-make-endpoints-consistent -> origin/15082-make-endpoints-consistent
203:  * [new branch]            15082-update-download-endpoints -> origin/15082-update-download-endpoints
204:  * [new branch]            15380-hosts-api-doc         -> origin/15380-hosts-api-doc
205:  * [new branch]            15461-ui-dep-assign-profile-responses -> origin/15461-ui-dep-assign-profile-responses
206:  * [new branch]            15463-msi-fix               -> origin/15463-msi-fix
207:  * [new branch]            15559-move-scep             -> origin/15559-move-scep
208:  * [new branch]            15633                       -> origin/15633
209:  * [new branch]            15770-mac-os-vuln           -> origin/15770-mac-os-vuln
210:  * [new branch]            15801-migration             -> origin/15801-migration
211:  * [new branch]            15912-password-reset-error-code -> origin/15912-password-reset-error-code
212:  * [new branch]            16182-log-root              -> origin/16182-log-root
...

616:  * [new branch]            4701-tickets                -> origin/4701-tickets
617:  * [new branch]            4701-with-osqpfix           -> origin/4701-with-osqpfix
618:  * [new branch]            471-0716                    -> origin/471-0716
619:  * [new branch]            471-cloudfront              -> origin/471-cloudfront
620:  * [new branch]            471-tickets                 -> origin/471-tickets
621:  * [new branch]            4710mdm                     -> origin/4710mdm
622:  * [new branch]            4710mdmloadtest             -> origin/4710mdmloadtest
623:  * [new branch]            4711-bsln                   -> origin/4711-bsln
624:  * [new branch]            471RC-07-09                 -> origin/471RC-07-09
625:  * [new branch]            4720mdm                     -> origin/4720mdm
626:  * [new branch]            4720orch                    -> origin/4720orch
627:  * [new branch]            4720orch1                   -> origin/4720orch1
628:  * [new branch]            4720orch2                   -> origin/4720orch2
629:  * [new branch]            47289-new-fma-masv          -> origin/47289-new-fma-masv
630:  * [new branch]            472orchm                    -> origin/472orchm
631:  * [new branch]            47303-redis-moved-errors-from-getqueryresultscounts-incrqueryresultscounts-in-cluster-mode -> origin/47303-redis-moved-errors-from-getqueryresultscounts-incrqueryresultscounts-in-cluster-mode
632:  * [new branch]            4735orch                    -> origin/4735orch
...

656:  * [new branch]            7765-frontend               -> origin/7765-frontend
657:  * [new branch]            7766-backend-final-main-merge -> origin/7766-backend-final-main-merge
658:  * [new branch]            7766-backend-test           -> origin/7766-backend-test
659:  * [new branch]            7766-frontend               -> origin/7766-frontend
660:  * [new branch]            7993-figure-out-the-limits-of-our-current-search-approach -> origin/7993-figure-out-the-limits-of-our-current-search-approach
661:  * [new branch]            8021-orbit-use-specified-identifier -> origin/8021-orbit-use-specified-identifier
662:  * [new branch]            8186-vulnerable-software-false-positive-docker -> origin/8186-vulnerable-software-false-positive-docker
663:  * [new branch]            8593-gitops                 -> origin/8593-gitops
664:  * [new branch]            8708-docs                   -> origin/8708-docs
665:  * [new branch]            8708-encryption-key-api     -> origin/8708-encryption-key-api
666:  * [new branch]            8708-ingest-file-vault      -> origin/8708-ingest-file-vault
667:  * [new branch]            8708-ingest-macos-encription-key -> origin/8708-ingest-macos-encription-key
668:  * [new branch]            8708-ingest-macos-encription-key-ii -> origin/8708-ingest-macos-encription-key-ii
669:  * [new branch]            8974-native-smbios-uuid-support -> origin/8974-native-smbios-uuid-support
670:  * [new branch]            9260-cis-checks-5.10.x      -> origin/9260-cis-checks-5.10.x
671:  * [new branch]            9394-failed-to-create-device-auth-token-due-to-duplicate-key-errors -> origin/9394-failed-to-create-device-auth-token-due-to-duplicate-key-errors
672:  * [new branch]            9590-schema                 -> origin/9590-schema
...

861:  * [new branch]            allenhouchins-add-hardware-procurement-info -> origin/allenhouchins-add-hardware-procurement-info
862:  * [new branch]            allenhouchins-contour-normalize -> origin/allenhouchins-contour-normalize
863:  * [new branch]            allenhouchins-fma-installer-arch -> origin/allenhouchins-fma-installer-arch
864:  * [new branch]            allenhouchins-net-arm64     -> origin/allenhouchins-net-arm64
865:  * [new branch]            allenhouchins-new-blog-post -> origin/allenhouchins-new-blog-post
866:  * [new branch]            allenhouchins-patch-1       -> origin/allenhouchins-patch-1
867:  * [new branch]            allenhouchins-santa-table-updates -> origin/allenhouchins-santa-table-updates
868:  * [new branch]            allenhouchins-update-custom-tap -> origin/allenhouchins-update-custom-tap
869:  * [new branch]            allenhouchins-update-ingester -> origin/allenhouchins-update-ingester
870:  * [new branch]            allenhouchins-update-new-fma-skill -> origin/allenhouchins-update-new-fma-skill
871:  * [new branch]            always-create-event-next-thu -> origin/always-create-event-next-thu
872:  * [new branch]            and_osq_proj                -> origin/and_osq_proj
873:  * [new branch]            andrey/loadtest-oval-vulns  -> origin/andrey/loadtest-oval-vulns
874:  * [new branch]            android-cofniguration-profiles-research -> origin/android-cofniguration-profiles-research
875:  * [new branch]            android-enroll-note         -> origin/android-enroll-note
876:  * [new branch]            android-enrollment-error    -> origin/android-enrollment-error
877:  * [new branch]            android-fleet-vars          -> origin/android-fleet-vars
...

929:  * [new branch]            await-configuration-docs    -> origin/await-configuration-docs
930:  * [new branch]            aws-licensing-integration-proposal -> origin/aws-licensing-integration-proposal
931:  * [new branch]            backport-a41fb63            -> origin/backport-a41fb63
932:  * [new branch]            backport-c02af              -> origin/backport-c02af
933:  * [new branch]            backport-e65d6cf            -> origin/backport-e65d6cf
934:  * [new branch]            backport-ef07a406cc8dbccb4914bbd27631df74e5b13b49 -> origin/backport-ef07a406cc8dbccb4914bbd27631df74e5b13b49
935:  * [new branch]            backport-vpp-fix            -> origin/backport-vpp-fix
936:  * [new branch]            base-software-titles-loadtest -> origin/base-software-titles-loadtest
937:  * [new branch]            base-software-titles-loadtest-2 -> origin/base-software-titles-loadtest-2
938:  * [new branch]            beekeeper-fma-initial       -> origin/beekeeper-fma-initial
939:  * [new branch]            bettapizza                  -> origin/bettapizza
940:  * [new branch]            bettapizza--ghost-testimonial-update -> origin/bettapizza--ghost-testimonial-update
941:  * [new branch]            bettapizza-patch-1          -> origin/bettapizza-patch-1
942:  * [new branch]            bettapizza-patch-2          -> origin/bettapizza-patch-2
943:  * [new branch]            bettapizza-patch-3          -> origin/bettapizza-patch-3
944:  * [new branch]            better-byod-error           -> origin/better-byod-error
945:  * [new branch]            bounded-context-proposals   -> origin/bounded-context-proposals
...

979:  * [new branch]            cherry-pick-42773-vitals-constants -> origin/cherry-pick-42773-vitals-constants
980:  * [new branch]            cherry-pick-48012-to-docs-v4.89.0 -> origin/cherry-pick-48012-to-docs-v4.89.0
981:  * [new branch]            cherry-pick-8750-into-rc-minor-fleet-v4.60.0 -> origin/cherry-pick-8750-into-rc-minor-fleet-v4.60.0
982:  * [new branch]            cherry-pick-dogfood-change  -> origin/cherry-pick-dogfood-change
983:  * [new branch]            cherry-pick-dogfood-env-vars -> origin/cherry-pick-dogfood-env-vars
984:  * [new branch]            cherry-pick-fleetctl-changes-to-allow-testing -> origin/cherry-pick-fleetctl-changes-to-allow-testing
985:  * [new branch]            cherry-pick-mac-address-vital -> origin/cherry-pick-mac-address-vital
986:  * [new branch]            cherry-pick-patches         -> origin/cherry-pick-patches
987:  * [new branch]            cherry-pick-remove-overrides-in-dev-mode -> origin/cherry-pick-remove-overrides-in-dev-mode
988:  * [new branch]            cherry-pick-util            -> origin/cherry-pick-util
989:  * [new branch]            cherry-pick-webhooks        -> origin/cherry-pick-webhooks
990:  * [new branch]            cherrypick-v4.85.0-version-bump-into-main -> origin/cherrypick-v4.85.0-version-bump-into-main
991:  * [new branch]            chore-UI-update-for-CSP     -> origin/chore-UI-update-for-CSP
992:  * [new branch]            chore-add-specific-yarn-version -> origin/chore-add-specific-yarn-version
993:  * [new branch]            chore-backend-api-patterns  -> origin/chore-backend-api-patterns
994:  * [new branch]            chore-change-error-message-custom-profile -> origin/chore-change-error-message-custom-profile
995:  * [new branch]            chore-cp-chage-status-code-turn-off-mdm -> origin/chore-cp-chage-status-code-turn-off-mdm
...

1188:  * [new branch]            edwardsb-vuln-processing-fix-sandcastle -> origin/edwardsb-vuln-processing-fix-sandcastle
1189:  * [new branch]            edwardsb-vuln-processing-volume-fix -> origin/edwardsb-vuln-processing-volume-fix
1190:  * [new branch]            edwardsb/deployment-guide-updates -> origin/edwardsb/deployment-guide-updates
1191:  * [new branch]            edwardsb/restrict_console_access -> origin/edwardsb/restrict_console_access
1192:  * [new branch]            elastic_rum                 -> origin/elastic_rum
1193:  * [new branch]            enable-jit-by-default       -> origin/enable-jit-by-default
1194:  * [new branch]            enable-test-go-automation-macos -> origin/enable-test-go-automation-macos
1195:  * [new branch]            enforce-firefox-doh         -> origin/enforce-firefox-doh
1196:  * [new branch]            enforce-firefox-windows     -> origin/enforce-firefox-windows
1197:  * [new branch]            enforce-ios-26.5-update-deadline-july-2026 -> origin/enforce-ios-26.5-update-deadline-july-2026
1198:  * [new branch]            enforce-iso27001-workstations -> origin/enforce-iso27001-workstations
1199:  * [new branch]            enforce-macos-15.5-update-deadline -> origin/enforce-macos-15.5-update-deadline
1200:  * [new branch]            enforce-macos-version-workstations -> origin/enforce-macos-version-workstations
1201:  * [new branch]            enroll-android-feature-guide -> origin/enroll-android-feature-guide
1202:  * [new branch]            ericswenson0-patch-1        -> origin/ericswenson0-patch-1
1203:  * [new branch]            error-counts                -> origin/error-counts
1204:  * [new branch]            errors-perf-testing         -> origin/errors-perf-testing
1205:  * [new branch]            es-reports                  -> origin/es-reports
...

1221:  * [new branch]            eugkuo-patch-6-slugs-FMA    -> origin/eugkuo-patch-6-slugs-FMA
1222:  * [new branch]            example-fleetctl-profiles   -> origin/example-fleetctl-profiles
1223:  * [new branch]            exp-mdbook-docs             -> origin/exp-mdbook-docs
1224:  * [new branch]            experiment-disable-some-deletes -> origin/experiment-disable-some-deletes
1225:  * [new branch]            experiment-hardcode-counters-to-zero -> origin/experiment-hardcode-counters-to-zero
1226:  * [new branch]            experiment-no-txs           -> origin/experiment-no-txs
1227:  * [new branch]            experiment-status-tooltip   -> origin/experiment-status-tooltip
1228:  * [new branch]            experiment-with-two-branches -> origin/experiment-with-two-branches
1229:  * [new branch]            experimental_moveitem       -> origin/experimental_moveitem
1230:  * [new branch]            experimient-no-counts       -> origin/experimient-no-counts
1231:  * [new branch]            expiredcertstool            -> origin/expiredcertstool
1232:  * [new branch]            express-vpn-win             -> origin/express-vpn-win
1233:  * [new branch]            extra-dep-logs              -> origin/extra-dep-logs
1234:  * [new branch]            f84ee4c5f4                  -> origin/f84ee4c5f4
1235:  * [new branch]            faf-doc-clarification       -> origin/faf-doc-clarification
1236:  * [new branch]            failing-test                -> origin/failing-test
1237:  * [new branch]            faq-updates-2026-06         -> origin/faq-updates-2026-06
...

1277:  * [new branch]            feature-hydrant-ca          -> origin/feature-hydrant-ca
1278:  * [new branch]            feature-idp-byod            -> origin/feature-idp-byod
1279:  * [new branch]            feature-orbit-find          -> origin/feature-orbit-find
1280:  * [new branch]            feature-prioritization      -> origin/feature-prioritization
1281:  * [new branch]            feature-v4.44.1-live-queries -> origin/feature-v4.44.1-live-queries
1282:  * [new branch]            feature-windows-mdm-policy-extraction -> origin/feature-windows-mdm-policy-extraction
1283:  * [new branch]            feature/add-blank-to-osquer-link -> origin/feature/add-blank-to-osquer-link
1284:  * [new branch]            feature/fleet-macos-password-sync -> origin/feature/fleet-macos-password-sync
1285:  * [new branch]            feature_14722-activity-feed-webhooks -> origin/feature_14722-activity-feed-webhooks
1286:  * [new branch]            feature_18115-crit-vuln-issues -> origin/feature_18115-crit-vuln-issues
1287:  * [new branch]            feature_19010-ipad-ios-lock-wipe-backend-changes -> origin/feature_19010-ipad-ios-lock-wipe-backend-changes
1288:  * [new branch]            feature_22078               -> origin/feature_22078
1289:  * [new branch]            fix                         -> origin/fix
1290:  * [new branch]            fix-35118-cis-win10-v4      -> origin/fix-35118-cis-win10-v4
1291:  * [new branch]            fix-4.84.4-k8s              -> origin/fix-4.84.4-k8s
1292:  * [new branch]            fix-40083-failed-install-retries -> origin/fix-40083-failed-install-retries
1293:  * [new branch]            fix-41290-vpp-added-status  -> origin/fix-41290-vpp-added-status
1294:  * [new branch]            fix-41337-save-env-secrets-dry-run -> origin/fix-41337-save-env-secrets-dry-run
1295:  * [new branch]            fix-43623-fma-patch-policy-gitops -> origin/fix-43623-fma-patch-policy-gitops
1296:  * [new branch]            fix-44199-embedded-app-bundles -> origin/fix-44199-embedded-app-bundles
1297:  * [new branch]            fix-44325-gitops-mode-tooltips -> origin/fix-44325-gitops-mode-tooltips
1298:  * [new branch]            fix-android-unenroll-activities -> origin/fix-android-unenroll-activities
1299:  * [new branch]            fix-blockquotes-google-groups -> origin/fix-blockquotes-google-groups
1300:  * [new branch]            fix-ca-verdict-spoof-16386  -> origin/fix-ca-verdict-spoof-16386
1301:  * [new branch]            fix-case-in-deploy-config-docs -> origin/fix-case-in-deploy-config-docs
1302:  * [new branch]            fix-cert-details-modal      -> origin/fix-cert-details-modal
1303:  * [new branch]            fix-checkerboard-scroll-to-right -> origin/fix-checkerboard-scroll-to-right
1304:  * [new branch]            fix-checkerboard-tooltip-nowrap -> origin/fix-checkerboard-tooltip-nowrap
1305:  * [new branch]            fix-cloudflare-warp-fma-output-json -> origin/fix-cloudflare-warp-fma-output-json
1306:  * [new branch]            fix-codeql-ci-error         -> origin/fix-codeql-ci-error
1307:  * [new branch]            fix-colima                  -> origin/fix-colima
1308:  * [new branch]            fix-color-for-mobile-sticky-nav -> origin/fix-color-for-mobile-sticky-nav
1309:  * [new branch]            fix-conditional-access-observer-authz -> origin/fix-conditional-access-observer-authz
1310:  * [new branch]            fix-conflict-in-feature_19010-ipad-ios-lock-wipe -> origin/fix-conflict-in-feature_19010-ipad-ios-lock-wipe
1311:  * [new branch]            fix-contrib-docs            -> origin/fix-contrib-docs
1312:  * [new branch]            fix-cve-2026-2792-firefox-esr -> origin/fix-cve-2026-2792-firefox-esr
1313:  * [new branch]            fix-cve-validate-enrichment-canary -> origin/fix-cve-validate-enrichment-canary
1314:  * [new branch]            fix-docker-cleanup-quay-nonfatal -> origin/fix-docker-cleanup-quay-nonfatal
1315:  * [new branch]            fix-docker-publish          -> origin/fix-docker-publish
1316:  * [new branch]            fix-dogfood-workstation     -> origin/fix-dogfood-workstation
1317:  * [new branch]            fix-embedded-bundle-titles-44199 -> origin/fix-embedded-bundle-titles-44199
1318:  * [new branch]            fix-firefox-cve-2026-2792   -> origin/fix-firefox-cve-2026-2792
1319:  * [new branch]            fix-firefox-fma-conflict-error-msg -> origin/fix-firefox-fma-conflict-error-msg
1320:  * [new branch]            fix-flakey-orbit-test       -> origin/fix-flakey-orbit-test
...

1327:  * [new branch]            fix-instability-ubuntu-latest -> origin/fix-instability-ubuntu-latest
1328:  * [new branch]            fix-ios-ipados-enrollment-label-42721 -> origin/fix-ios-ipados-enrollment-label-42721
1329:  * [new branch]            fix-list-sw-permissions     -> origin/fix-list-sw-permissions
1330:  * [new branch]            fix-mdm-enrolled-webhook    -> origin/fix-mdm-enrolled-webhook
1331:  * [new branch]            fix-mdm-redirect            -> origin/fix-mdm-redirect
1332:  * [new branch]            fix-mdm-url-truncation      -> origin/fix-mdm-url-truncation
1333:  * [new branch]            fix-missing-test-schema-update -> origin/fix-missing-test-schema-update
1334:  * [new branch]            fix-modernize               -> origin/fix-modernize
1335:  * [new branch]            fix-no-space-left-on-device-failure -> origin/fix-no-space-left-on-device-failure
1336:  * [new branch]            fix-notarization            -> origin/fix-notarization
1337:  * [new branch]            fix-orbit-notarization      -> origin/fix-orbit-notarization
1338:  * [new branch]            fix-orbit-nudge-test-mdm-connection-fidelity -> origin/fix-orbit-nudge-test-mdm-connection-fidelity
1339:  * [new branch]            fix-os-settings-name-col-truncation -> origin/fix-os-settings-name-col-truncation
1340:  * [new branch]            fix-os-settings-underline-descender-clip -> origin/fix-os-settings-underline-descender-clip
1341:  * [new branch]            fix-pricing-more-info-icon-documentationurl -> origin/fix-pricing-more-info-icon-documentationurl
1342:  * [new branch]            fix-receive-from-github-error -> origin/fix-receive-from-github-error
1343:  * [new branch]            fix-sandbox-local           -> origin/fix-sandbox-local
...

1357:  * [new branch]            fix-ui-typo-fleet-maintained-empty -> origin/fix-ui-typo-fleet-maintained-empty
1358:  * [new branch]            fix-vpp-edit-fleet-desktop-message -> origin/fix-vpp-edit-fleet-desktop-message
1359:  * [new branch]            fix-win-os-version          -> origin/fix-win-os-version
1360:  * [new branch]            fix_14825                   -> origin/fix_14825
1361:  * [new branch]            fixes-on-minor-fleet-v4.54.0 -> origin/fixes-on-minor-fleet-v4.54.0
1362:  * [new branch]            fixes-plus-4.35.1           -> origin/fixes-plus-4.35.1
1363:  * [new branch]            flaky-tests-nov-6           -> origin/flaky-tests-nov-6
1364:  * [new branch]            fleet-desktop-linux-browser -> origin/fleet-desktop-linux-browser
1365:  * [new branch]            fleet-desktop-version       -> origin/fleet-desktop-version
1366:  * [new branch]            fleet-mcp-inventory-tools   -> origin/fleet-mcp-inventory-tools
1367:  * [new branch]            fleet-mdm-server-url        -> origin/fleet-mdm-server-url
1368:  * [new branch]            fleet-multiple-abm-vpp-proposal -> origin/fleet-multiple-abm-vpp-proposal
1369:  * [new branch]            fleet-premium-users-usage-statistics -> origin/fleet-premium-users-usage-statistics
1370:  * [new branch]            fleet-release-infra         -> origin/fleet-release-infra
1371:  * [new branch]            fleet-server-config-1817    -> origin/fleet-server-config-1817
1372:  * [new branch]            fleet-server-log-request-errors -> origin/fleet-server-log-request-errors
1373:  * [new branch]            fleet-ship-rebuild          -> origin/fleet-ship-rebuild
...

2375:  * [new branch]            hotfix-failing-tests        -> origin/hotfix-failing-tests
2376:  * [new branch]            hotfix-query-update         -> origin/hotfix-query-update
2377:  * [new branch]            hotfix-revert-sofa-tables   -> origin/hotfix-revert-sofa-tables
2378:  * [new branch]            hotfix-v4.67.2-migration    -> origin/hotfix-v4.67.2-migration
2379:  * [new branch]            hotfix-v4.76.1              -> origin/hotfix-v4.76.1
2380:  * [new branch]            hover-gravatar              -> origin/hover-gravatar
2381:  * [new branch]            hughestaylor-patch-1        -> origin/hughestaylor-patch-1
2382:  * [new branch]            hughestaylor-patch-1-1      -> origin/hughestaylor-patch-1-1
2383:  * [new branch]            ignore-fleetctl-vulnerabilities -> origin/ignore-fleetctl-vulnerabilities
2384:  * [new branch]            improve-flakey-test         -> origin/improve-flakey-test
2385:  * [new branch]            improve-prometheus-metrics  -> origin/improve-prometheus-metrics
2386:  * [new branch]            improve-settings-subnav     -> origin/improve-settings-subnav
2387:  * [new branch]            include-mnt-in-gads-metric  -> origin/include-mnt-in-gads-metric
2388:  * [new branch]            increase-goreleaser-timeout-to-60m -> origin/increase-goreleaser-timeout-to-60m
2389:  * [new branch]            ingress-gateway-support     -> origin/ingress-gateway-support
2390:  * [new branch]            insert-software-installers-retry-error -> origin/insert-software-installers-retry-error
2391:  * [new branch]            install-all-recently-installed -> origin/install-all-recently-installed
...

2396:  * [new branch]            ireedy-patch-2              -> origin/ireedy-patch-2
2397:  * [new branch]            ireedy-patch-3              -> origin/ireedy-patch-3
2398:  * [new branch]            irena-deebradelo            -> origin/irena-deebradelo
2399:  * [new branch]            irena-handbook-edit         -> origin/irena-handbook-edit
2400:  * [new branch]            irena-test-with-john        -> origin/irena-test-with-john
2401:  * [new branch]            irenareedy-event-process    -> origin/irenareedy-event-process
2402:  * [new branch]            irenareedy-patch-1          -> origin/irenareedy-patch-1
2403:  * [new branch]            irenareedy-patch-2          -> origin/irenareedy-patch-2
2404:  * [new branch]            irenareedy-patch-4          -> origin/irenareedy-patch-4
2405:  * [new branch]            irenareedy-thumbtack-case-study -> origin/irenareedy-thumbtack-case-study
2406:  * [new branch]            iso-27001-compliance        -> origin/iso-27001-compliance
2407:  * [new branch]            iso42001-ai-discovery-inventory -> origin/iso42001-ai-discovery-inventory
2408:  * [new branch]            issue-11545-my-device-dep-modal -> origin/issue-11545-my-device-dep-modal
2409:  * [new branch]            issue-2716-specify-query-platforms -> origin/issue-2716-specify-query-platforms
2410:  * [new branch]            issue-3271-rate-limit-distributed-writes -> origin/issue-3271-rate-limit-distributed-writes
2411:  * [new branch]            issue-4361-mail-change-should-error -> origin/issue-4361-mail-change-should-error
2412:  * [new branch]            issue-conf-1968-delete-uninstalled-software-from-main -> origin/issue-conf-1968-delete-uninstalled-software-from-main
...

2618:  * [new branch]            melpike-api-default-fleet-windows-41787 -> origin/melpike-api-default-fleet-windows-41787
2619:  * [new branch]            melpike-api-device-url-43895 -> origin/melpike-api-device-url-43895
2620:  * [new branch]            melpike-api-minimum-version-39085 -> origin/melpike-api-minimum-version-39085
2621:  * [new branch]            melpike-api-py-script-41470 -> origin/melpike-api-py-script-41470
2622:  * [new branch]            melpike-api-windows-managed-account-43488 -> origin/melpike-api-windows-managed-account-43488
2623:  * [new branch]            melpike-audit-log-account-provisioning-45524 -> origin/melpike-audit-log-account-provisioning-45524
2624:  * [new branch]            melpike-audit-log-mdm-cmd-45021 -> origin/melpike-audit-log-mdm-cmd-45021
2625:  * [new branch]            melpike-audit-recovery-lock-password-rotate -> origin/melpike-audit-recovery-lock-password-rotate
2626:  * [new branch]            melpike-byod-wipe-lock-23242 -> origin/melpike-byod-wipe-lock-23242
2627:  * [new branch]            melpike-byod-wipe-lock-23242-1 -> origin/melpike-byod-wipe-lock-23242-1
2628:  * [new branch]            melpike-byod-wipe-lock-api-23242 -> origin/melpike-byod-wipe-lock-api-23242
2629:  * [new branch]            melpike-byod-wipe-lock-yaml-23242 -> origin/melpike-byod-wipe-lock-yaml-23242
2630:  * [new branch]            melpike-combine-guides-30674 -> origin/melpike-combine-guides-30674
2631:  * [new branch]            melpike-create-managed-local-account-guide-37141 -> origin/melpike-create-managed-local-account-guide-37141
2632:  * [new branch]            melpike-default-fleet-windows-41787 -> origin/melpike-default-fleet-windows-41787
2633:  * [new branch]            melpike-dep-error-messaging-43916 -> origin/melpike-dep-error-messaging-43916
2634:  * [new branch]            melpike-doc-deploy-sw-41470 -> origin/melpike-doc-deploy-sw-41470
...

2779:  * [new branch]            mna-doc-change-api-structure -> origin/mna-doc-change-api-structure
2780:  * [new branch]            mna-experiment-macos-profiles-uuid -> origin/mna-experiment-macos-profiles-uuid
2781:  * [new branch]            mna-fix-22558-windows-installer-stuck-pending -> origin/mna-fix-22558-windows-installer-stuck-pending
2782:  * [new branch]            mna-fix-duplicate-lock-unlock-activity -> origin/mna-fix-duplicate-lock-unlock-activity
2783:  * [new branch]            mna-fix-failing-tests       -> origin/mna-fix-failing-tests
2784:  * [new branch]            mna-fix-flaky-integration-test -> origin/mna-fix-flaky-integration-test
2785:  * [new branch]            mna-fix-oom-fleetctl-test   -> origin/mna-fix-oom-fleetctl-test
2786:  * [new branch]            mna-loadtest-enroll-host-limit -> origin/mna-loadtest-enroll-host-limit
2787:  * [new branch]            mna-orbit-node-key          -> origin/mna-orbit-node-key
2788:  * [new branch]            mna-revert-change-to-autogenerated-activities-doc -> origin/mna-revert-change-to-autogenerated-activities-doc
2789:  * [new branch]            mna-temp-osquery-perf-loadtest -> origin/mna-temp-osquery-perf-loadtest
2790:  * [new branch]            mna11997                    -> origin/mna11997
2791:  * [new branch]            mock-associate-assets-endpoint -> origin/mock-associate-assets-endpoint
2792:  * [new branch]            monitoring-test             -> origin/monitoring-test
2793:  * [new branch]            more-conditional-access-api-updates -> origin/more-conditional-access-api-updates
2794:  * [new branch]            more-fd-errors              -> origin/more-fd-errors
2795:  * [new branch]            move-conditional-access-script-to-testing-qa -> origin/move-conditional-access-script-to-testing-qa
...

3361:  * [new branch]            sgress454/34259-end-user-auth-setup-backend -> origin/sgress454/34259-end-user-auth-setup-backend
3362:  * [new branch]            sgress454/34528-end-user-auth-setup-agent -> origin/sgress454/34528-end-user-auth-setup-agent
3363:  * [new branch]            sgress454/35376             -> origin/sgress454/35376
3364:  * [new branch]            sgress454/35452             -> origin/sgress454/35452
3365:  * [new branch]            sgress454/37127-open-macos-end-user-auth-window -> origin/sgress454/37127-open-macos-end-user-auth-window
3366:  * [new branch]            sgress454/38063-skeleton-server -> origin/sgress454/38063-skeleton-server
3367:  * [new branch]            sgress454/39344-rename-teams-to-fleet-all-changes -> origin/sgress454/39344-rename-teams-to-fleet-all-changes
3368:  * [new branch]            sgress454/39344-rename-teams-to-fleet-api-changes -> origin/sgress454/39344-rename-teams-to-fleet-api-changes
3369:  * [new branch]            sgress454/40642-update-jit-saml-prefix -> origin/sgress454/40642-update-jit-saml-prefix
3370:  * [new branch]            sgress454/43482-re-pin      -> origin/sgress454/43482-re-pin
3371:  * [new branch]            sgress454/44077-disable-datasets-backend -> origin/sgress454/44077-disable-datasets-backend
3372:  * [new branch]            sgress454/44077-disable-datasets-docs -> origin/sgress454/44077-disable-datasets-docs
3373:  * [new branch]            sgress454/44077-disable-datasets-frontend -> origin/sgress454/44077-disable-datasets-frontend
3374:  * [new branch]            sgress454/45290-drop-mobile-from-charting -> origin/sgress454/45290-drop-mobile-from-charting
3375:  * [new branch]            sgress454/add-vex-for-cve-2026-23517 -> origin/sgress454/add-vex-for-cve-2026-23517
3376:  * [new branch]            sgress454/adr-for-error-codes -> origin/sgress454/adr-for-error-codes
3377:  * [new branch]            sgress454/adr-refactor-list-hosts -> origin/sgress454/adr-refactor-list-hosts
...

3429:  * [new branch]            software-titles-loadtest-2  -> origin/software-titles-loadtest-2
3430:  * [new branch]            solarized-theme             -> origin/solarized-theme
3431:  * [new branch]            solutions-folder            -> origin/solutions-folder
3432:  * [new branch]            sort-seen-hosts-before-inserting -> origin/sort-seen-hosts-before-inserting
3433:  * [new branch]            spalmesano0-patch-2         -> origin/spalmesano0-patch-2
3434:  * [new branch]            spike-fleet-lint-plugin     -> origin/spike-fleet-lint-plugin
3435:  * [new branch]            spike-ui-permissions-pattern -> origin/spike-ui-permissions-pattern
3436:  * [new branch]            spokanemac-add-passcode-ddm-profile -> origin/spokanemac-add-passcode-ddm-profile
3437:  * [new branch]            spokanemac-dogfood-additional-apps -> origin/spokanemac-dogfood-additional-apps
3438:  * [new branch]            spokanemac-handbook-release-article -> origin/spokanemac-handbook-release-article
3439:  * [new branch]            spokanemac-macdevops-conference-post -> origin/spokanemac-macdevops-conference-post
3440:  * [new branch]            spokanemac-meetup-template  -> origin/spokanemac-meetup-template
3441:  * [new branch]            spokanemac-migrating-fleetdm-from-dogfood-to-terraform-on-aws -> origin/spokanemac-migrating-fleetdm-from-dogfood-to-terraform-on-aws
3442:  * [new branch]            spokanemac-release-article-issue-template -> origin/spokanemac-release-article-issue-template
3443:  * [new branch]            step4-improvements          -> origin/step4-improvements
3444:  * [new branch]            store-query-execution-and-ingestion-errors -> origin/store-query-execution-and-ingestion-errors
3445:  * [new branch]            support-breaking-osquery-downgrades -> origin/support-breaking-osquery-downgrades
...

3513:  * [new branch]            ui-self-service-sg-5-21     -> origin/ui-self-service-sg-5-21
3514:  * [new branch]            unauth-installers           -> origin/unauth-installers
3515:  * [new branch]            uniform-brex-spending-limit -> origin/uniform-brex-spending-limit
3516:  * [new branch]            untx-software               -> origin/untx-software
3517:  * [new branch]            upcoming-activities-mdm-commands -> origin/upcoming-activities-mdm-commands
3518:  * [new branch]            upcoming-commands           -> origin/upcoming-commands
3519:  * [new branch]            update-1password-and-safari-policy-versions-2601200025 -> origin/update-1password-and-safari-policy-versions-2601200025
3520:  * [new branch]            update-1password-and-safari-policy-versions-2601200610 -> origin/update-1password-and-safari-policy-versions-2601200610
3521:  * [new branch]            update-1password-and-safari-policy-versions-2602261818 -> origin/update-1password-and-safari-policy-versions-2602261818
3522:  * [new branch]            update-1password-and-safari-policy-versions-2603101819 -> origin/update-1password-and-safari-policy-versions-2603101819
3523:  * [new branch]            update-1password-and-safari-policy-versions-2604080037 -> origin/update-1password-and-safari-policy-versions-2604080037
3524:  * [new branch]            update-1password-and-safari-policy-versions-2604080637 -> origin/update-1password-and-safari-policy-versions-2604080637
3525:  * [new branch]            update-Cloudflare-WARP-FMA-with-UpgradeCode -> origin/update-Cloudflare-WARP-FMA-with-UpgradeCode
3526:  * [new branch]            update-article-format       -> origin/update-article-format
3527:  * [new branch]            update-bug-report-template  -> origin/update-bug-report-template
3528:  * [new branch]            update-build-script-error-message -> origin/update-build-script-error-message
3529:  * [new branch]            update-cancel-and-done-links -> origin/update-cancel-and-done-links
...

3611:  * [new branch]            update-spec-story-skill     -> origin/update-spec-story-skill
3612:  * [new branch]            update-testing-qa-apps-2511191525 -> origin/update-testing-qa-apps-2511191525
3613:  * [new branch]            update-tuf-keys             -> origin/update-tuf-keys
3614:  * [new branch]            update-tuf-md               -> origin/update-tuf-md
3615:  * [new branch]            update-usage-statistics-mobile-devices -> origin/update-usage-statistics-mobile-devices
3616:  * [new branch]            update-user-password-length-validation-test -> origin/update-user-password-length-validation-test
3617:  * [new branch]            update-vex-statements       -> origin/update-vex-statements
3618:  * [new branch]            update-why-this-way         -> origin/update-why-this-way
3619:  * [new branch]            upgrade-eslint              -> origin/upgrade-eslint
3620:  * [new branch]            upgrade-go                  -> origin/upgrade-go
3621:  * [new branch]            upgrade-typescript          -> origin/upgrade-typescript
3622:  * [new branch]            upload-msiexec-install-log  -> origin/upload-msiexec-install-log
3623:  * [new branch]            usage-stats-mobile-devices-count -> origin/usage-stats-mobile-devices-count
3624:  * [new branch]            use-dockerhub-creds         -> origin/use-dockerhub-creds
3625:  * [new branch]            use-software-name-helper    -> origin/use-software-name-helper
3626:  * [new branch]            user-channel-error          -> origin/user-channel-error
3627:  * [new branch]            users-table-experiment      -> origin/users-table-experiment
...

3654:  * [new branch]            victor/36202-bootstrap-enroll -> origin/victor/36202-bootstrap-enroll
3655:  * [new branch]            victor/36452-activity-bounded-context -> origin/victor/36452-activity-bounded-context
3656:  * [new branch]            victor/36453-activity-scaffold -> origin/victor/36453-activity-scaffold
3657:  * [new branch]            victor/36793-logging        -> origin/victor/36793-logging
3658:  * [new branch]            victor/37241-archtest       -> origin/victor/37241-archtest
3659:  * [new branch]            victor/37806-fleet-activities -> origin/victor/37806-fleet-activities
3660:  * [new branch]            victor/38607-otel-logs      -> origin/victor/38607-otel-logs
3661:  * [new branch]            victor/38889-slog-mysql     -> origin/victor/38889-slog-mysql
3662:  * [new branch]            victor/39370-emacs-false-positive -> origin/victor/39370-emacs-false-positive
3663:  * [new branch]            victor/41374-backout        -> origin/victor/41374-backout
3664:  * [new branch]            victor/42318-bitlocker-pin  -> origin/victor/42318-bitlocker-pin
3665:  * [new branch]            victor/42843-esp-tracking-reference -> origin/victor/42843-esp-tracking-reference
3666:  * [new branch]            victor/44599-unknown-nodekey-prob -> origin/victor/44599-unknown-nodekey-prob
3667:  * [new branch]            victor/45491-repro          -> origin/victor/45491-repro
3668:  * [new branch]            victor/46387-docs           -> origin/victor/46387-docs
3669:  * [new branch]            victor/46387-windows-mdm-enrollment-auth-error -> origin/victor/46387-windows-mdm-enrollment-auth-error
3670:  * [new branch]            victor/46387-windows-mdm-federated-discovery -> origin/victor/46387-windows-mdm-federated-discovery
...

3778:  * [new branch]            wesite-fix-sales-rituals    -> origin/wesite-fix-sales-rituals
3779:  * [new branch]            willmayhone88-patch-1       -> origin/willmayhone88-patch-1
3780:  * [new branch]            win-fma-deepl               -> origin/win-fma-deepl
3781:  * [new branch]            win-fma-evernote            -> origin/win-fma-evernote
3782:  * [new branch]            win-fma-goland              -> origin/win-fma-goland
3783:  * [new branch]            win-fma-testing             -> origin/win-fma-testing
3784:  * [new branch]            win-fma-vnc-apps            -> origin/win-fma-vnc-apps
3785:  * [new branch]            win-mdm-loadtest            -> origin/win-mdm-loadtest
3786:  * [new branch]            windows-baseline            -> origin/windows-baseline
3787:  * [new branch]            windows-end-user-experience-labels -> origin/windows-end-user-experience-labels
3788:  * [new branch]            windows-firefox-doh-script  -> origin/windows-firefox-doh-script
3789:  * [new branch]            windows-mdm-autopilot       -> origin/windows-mdm-autopilot
3790:  * [new branch]            windows-mdm-c-poc           -> origin/windows-mdm-c-poc
3791:  * [new branch]            windows-mdm-docs-auto-enroll -> origin/windows-mdm-docs-auto-enroll
3792:  * [new branch]            windows-mdm-load-test       -> origin/windows-mdm-load-test
3793:  * [new branch]            windows_enable_and_configure-error-message -> origin/windows_enable_and_configure-error-message
3794:  * [new branch]            workstations-to-use-edge-channels -> origin/workstations-to-use-edge-channels
...

3806:  * [new branch]            zayhanlon-patch-1           -> origin/zayhanlon-patch-1
3807:  * [new branch]            zenity-execuser             -> origin/zenity-execuser
3808:  * [new branch]            zhumo-patch-1               -> origin/zhumo-patch-1
3809:  * [new branch]            zhumo-patch-2               -> origin/zhumo-patch-2
3810:  * [new branch]            zhumo-patch-3               -> origin/zhumo-patch-3
3811:  * [new branch]            zhumo-patch-4               -> origin/zhumo-patch-4
3812:  * [new branch]            zhumo-patch-5               -> origin/zhumo-patch-5
3813:  * [new branch]            zhumo-patch-6               -> origin/zhumo-patch-6
3814:  * [new branch]            zhumo-patch-7               -> origin/zhumo-patch-7
3815:  * [new branch]            zwass-actions-reminders     -> origin/zwass-actions-reminders
3816:  * [new branch]            zwass-improve-macos-cpe     -> origin/zwass-improve-macos-cpe
3817:  * [new branch]            zwass-patch-1               -> origin/zwass-patch-1
3818:  * [new branch]            zwass-patch-2               -> origin/zwass-patch-2
3819:  * [new branch]            zwinnerman-fixup            -> origin/zwinnerman-fixup
3820:  * [new branch]            zwinnerman-livequery-tracing -> origin/zwinnerman-livequery-tracing
3821:  * [new branch]            zwinnerman-redis-error-handling -> origin/zwinnerman-redis-error-handling
3822:  * [new branch]            zwinnerman-test             -> origin/zwinnerman-test
...

4530:  check-latest: false
4531:  token: ***
4532:  cache: true
4533:  env:
4534:  GH_TOKEN: ***
4535:  LOG_LEVEL: info
4536:  ##[endgroup]
4537:  Setup go version spec 1.26.4
4538:  Found in cache @ /Users/runner/hostedtoolcache/go/1.26.4/arm64
4539:  Added go to the path
4540:  Successfully set up Go version 1.26.4
4541:  [command]/Users/runner/hostedtoolcache/go/1.26.4/arm64/bin/go env GOMODCACHE
4542:  [command]/Users/runner/hostedtoolcache/go/1.26.4/arm64/bin/go env GOCACHE
4543:  /Users/runner/Library/Caches/go-build
4544:  /Users/runner/go/pkg/mod
4545:  ##[warning]Restore cache failed: Dependencies file is not found in /Users/runner/work/fleet/fleet. Supported file pattern: go.mod
4546:  go version go1.26.4 darwin/arm64
...

4647:  - reqable/darwin
4648:  - reqable/windows
4649:  - requestly/darwin
4650:  - rocket-chat/darwin
4651:  - rstudio/windows
4652:  - shift/darwin
4653:  - shift/windows
4654:  - spokenly/darwin
4655:  - suspicious-package/darwin
4656:  - tailscale-app/darwin
4657:  - tailscale/windows
4658:  - todoist-app/darwin
4659:  - todoist-app/windows
4660:  - warp/darwin
4661:  - wins/darwin
4662:  ##[group]Run # Default to no changes if detection step failed or didn't set output
4663:  �[36;1m# Default to no changes if detection step failed or didn't set output�[0m
4664:  �[36;1mHAS_CHANGES="true"�[0m
...

4868:  go: downloading go.opentelemetry.io/otel v1.43.0
4869:  go: downloading go.opentelemetry.io/otel/metric v1.43.0
4870:  go: downloading go.opentelemetry.io/otel/trace v1.43.0
4871:  go: downloading github.com/mattn/go-colorable v0.1.13
4872:  go: downloading github.com/edsrzf/mmap-go v1.1.0
4873:  go: downloading github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d
4874:  go: downloading go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0
4875:  go: downloading golang.org/x/oauth2 v0.35.0
4876:  go: downloading github.com/aws/aws-sdk-go-v2/config v1.32.12
4877:  go: downloading github.com/aws/aws-sdk-go-v2 v1.41.5
4878:  go: downloading github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.8
4879:  go: downloading github.com/spf13/cobra v1.9.1
4880:  go: downloading github.com/spf13/viper v1.20.1
4881:  go: downloading github.com/go-kit/kit v0.12.0
4882:  go: downloading github.com/smallstep/pkcs7 v0.0.0-20240723090913-5e2c6a136dfa
4883:  go: downloading github.com/hashicorp/go-multierror v1.1.1
4884:  go: downloading github.com/micromdm/plist v0.2.3-0.20260123201933-667adaf87d87
4885:  go: downloading github.com/MicahParks/jwkset v0.11.0
4886:  go: downloading github.com/golang-jwt/jwt/v4 v4.5.2
4887:  go: downloading github.com/WatchBeam/clock v0.0.0-20170901150240-b08e6b4da7ea
4888:  go: downloading github.com/mattn/go-isatty v0.0.20
4889:  go: downloading go.opentelemetry.io/otel/sdk v1.43.0
4890:  go: downloading github.com/andygrunwald/go-jira v1.16.0
4891:  go: downloading github.com/cenkalti/backoff/v4 v4.3.0
4892:  go: downloading github.com/cenkalti/backoff v2.2.1+incompatible
4893:  go: downloading github.com/nukosuke/go-zendesk v0.13.1
4894:  go: downloading github.com/igm/sockjs-go/v3 v3.0.2
4895:  go: downloading gopkg.in/yaml.v2 v2.4.0
4896:  go: downloading github.com/oschwald/maxminddb-golang v1.10.0
4897:  go: downloading golang.org/x/sys v0.45.0
4898:  go: downloading github.com/elastic/go-sysinfo v1.11.2
4899:  go: downloading github.com/pkg/errors v0.9.1
4900:  go: downloading go.elastic.co/fastjson v1.1.0
...

4952:  go: downloading github.com/pelletier/go-toml/v2 v2.2.3
4953:  go: downloading gopkg.in/yaml.v3 v3.0.1
4954:  go: downloading github.com/groob/finalizer v0.0.0-20170707115354-4c2ed49aabda
4955:  go: downloading github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8
4956:  go: downloading cloud.google.com/go/iam v1.5.3
4957:  go: downloading github.com/googleapis/gax-go/v2 v2.17.0
4958:  go: downloading go.opencensus.io v0.24.0
4959:  go: downloading golang.org/x/sync v0.21.0
4960:  go: downloading google.golang.org/grpc v1.79.3
4961:  go: downloading google.golang.org/protobuf v1.36.11
4962:  go: downloading github.com/nats-io/nkeys v0.4.15
4963:  go: downloading github.com/nats-io/nuid v1.0.1
4964:  go: downloading github.com/micromdm/nanolib v0.2.0
4965:  go: downloading go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
4966:  go: downloading github.com/go-logfmt/logfmt v0.5.1
4967:  go: downloading github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901
4968:  go: downloading cloud.google.com/go/pubsub/v2 v2.0.0
...

4971:  go: downloading google.golang.org/genproto v0.0.0-20260128011058-8636f8732409
4972:  go: downloading cloud.google.com/go/compute/metadata v0.9.0
4973:  go: downloading google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20
4974:  go: downloading cloud.google.com/go/auth/oauth2adapt v0.2.8
4975:  go: downloading go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0
4976:  go: downloading github.com/google/s2a-go v0.1.9
4977:  go: downloading github.com/googleapis/enterprise-certificate-proxy v0.3.12
4978:  time=level=INFO msg="GOOS environment variable is not set. Using system detected: 'darwin'"
4979:  time=level=INFO msg="INSTALLATION_SEARCH_DIRECTORY environment variable is not set. Using default: '/Applications'"
4980:  time=level=INFO msg="Validating app: Beyond Compare (beyond-compare/darwin)"
4981:  time=level=INFO msg=Downloading...
4982:  time=level=INFO msg="Executing install script..." app="Beyond Compare"
4983:  time=level=INFO msg="New application detected at: /Applications/Beyond Compare.app" app="Beyond Compare"
4984:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Beyond Compare.app'" app="Beyond Compare"
4985:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Beyond Compare.app'" app="Beyond Compare"
4986:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app="Beyond Compare"
4987:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Beyond Compare.app: No such xattr: com.apple.quarantine'" app="Beyond Compare"
4988:  time=level=INFO msg="Spctl output error: <nil>" app="Beyond Compare"
4989:  time=level=INFO msg="spctl status: spctl status: '/Applications/Beyond Compare.app: accepted\nsource=Notarized Developer ID'" app="Beyond Compare"
4990:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app="Beyond Compare"
4991:  time=level=INFO msg="Looking for app: Beyond Compare, version: 5.2.3.32296" app="Beyond Compare"
4992:  time=level=INFO msg="Found app: 'Beyond Compare' at /Applications/Beyond Compare.app, Version: 5.2.3.32296, Bundled Version: 5020.32.29" app="Beyond Compare"
4993:  time=level=INFO msg="Executing uninstall script for app..." app="Beyond Compare"
4994:  time=level=INFO msg="Looking for app: Beyond Compare, version: 5.2.3.32296" app="Beyond Compare"
4995:  time=level=INFO msg="All checks passed for app: Beyond Compare (beyond-compare/darwin)"
4996:  time=level=INFO msg="Validating app: Claude (claude/darwin)"
4997:  time=level=INFO msg=Downloading...
4998:  time=level=INFO msg="Executing install script..." app=Claude
4999:  time=level=INFO msg="New application detected at: /Applications/Claude.app" app=Claude
5000:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Claude.app'" app=Claude
5001:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Claude.app'" app=Claude
5002:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app=Claude
5003:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Claude.app: No such xattr: com.apple.quarantine'" app=Claude
5004:  time=level=INFO msg="Spctl output error: <nil>" app=Claude
5005:  time=level=INFO msg="spctl status: spctl status: '/Applications/Claude.app: accepted\nsource=Notarized Developer ID'" app=Claude
5006:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app=Claude
5007:  time=level=INFO msg="Looking for app: Claude, version: 1.17282.0" app=Claude
5008:  time=level=INFO msg="Found app: 'Claude' at /Applications/Claude.app, Version: 1.17282.0, Bundled Version: 1.17282.0" app=Claude
5009:  time=level=INFO msg="Executing uninstall script for app..." app=Claude
5010:  time=level=INFO msg="Looking for app: Claude, version: 1.17282.0" app=Claude
5011:  time=level=INFO msg="All checks passed for app: Claude (claude/darwin)"
5012:  time=level=INFO msg="Validating app: Cloudflare One (cloudflare-warp/darwin)"
5013:  time=level=INFO msg=Downloading...
5014:  time=level=INFO msg="Executing install script..." app="Cloudflare One"
5015:  time=level=INFO msg="New application detected at: /Applications/Cloudflare WARP.app" app="Cloudflare One"
5016:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Cloudflare WARP.app'" app="Cloudflare One"
5017:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Cloudflare WARP.app'" app="Cloudflare One"
5018:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app="Cloudflare One"
5019:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Cloudflare WARP.app: No such xattr: com.apple.quarantine'" app="Cloudflare One"
5020:  time=level=INFO msg="Spctl output error: <nil>" app="Cloudflare One"
5021:  time=level=INFO msg="spctl status: spctl status: '/Applications/Cloudflare WARP.app: accepted\nsource=Notarized Developer ID'" app="Cloudflare One"
5022:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app="Cloudflare One"
5023:  time=level=INFO msg="Looking for app: Cloudflare One, version: 2026.6.822.0" app="Cloudflare One"
5024:  time=level=INFO msg="Found app: 'Updater' at /Applications/Cloudflare WARP.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app, Version: 2.7.1, Bundled Version: 2045" app="Cloudflare One"
5025:  time=level=INFO msg="Found app: 'Cloudflare WARP' at /Applications/Cloudflare WARP.app, Version: 2026.6.822.0, Bundled Version: 20269006.822" app="Cloudflare One"
5026:  time=level=INFO msg="Executing uninstall script for app..." app="Cloudflare One"
5027:  time=level=INFO msg="Looking for app: Cloudflare One, version: 2026.6.822.0" app="Cloudflare One"
5028:  time=level=INFO msg="All checks passed for app: Cloudflare One (cloudflare-warp/darwin)"
5029:  time=level=INFO msg="Validating app: Codex (codex-app/darwin)"
5030:  time=level=INFO msg=Downloading...
5031:  time=level=INFO msg="Executing install script..." app=Codex
5032:  time=level=INFO msg="New application detected at: /Applications/Codex.app" app=Codex
5033:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Codex.app'" app=Codex
5034:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Codex.app'" app=Codex
5035:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app=Codex
5036:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Codex.app: No such xattr: com.apple.quarantine'" app=Codex
5037:  time=level=INFO msg="Spctl output error: <nil>" app=Codex
5038:  time=level=INFO msg="spctl status: spctl status: '/Applications/Codex.app: accepted\nsource=Notarized Developer ID'" app=Codex
5039:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app=Codex
5040:  time=level=INFO msg="Looking for app: Codex, version: 26.623.70822" app=Codex
5041:  time=level=INFO msg="Found app: 'Codex' at /Applications/Codex.app, Version: 26.623.70822, Bundled Version: 4559" app=Codex
5042:  time=level=INFO msg="Executing uninstall script for app..." app=Codex
5043:  time=level=INFO msg="Looking for app: Codex, version: 26.623.70822" app=Codex
5044:  time=level=INFO msg="All checks passed for app: Codex (codex-app/darwin)"
5045:  time=level=INFO msg="Validating app: Discord (discord/darwin)"
5046:  time=level=INFO msg=Downloading...
5047:  time=level=INFO msg="Executing install script..." app=Discord
5048:  time=level=INFO msg="New application detected at: /Applications/Discord.app" app=Discord
5049:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Discord.app'" app=Discord
5050:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Discord.app'" app=Discord
5051:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app=Discord
5052:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Discord.app: No such xattr: com.apple.quarantine'" app=Discord
5053:  time=level=INFO msg="Spctl output error: <nil>" app=Discord
5054:  time=level=INFO msg="spctl status: spctl status: '/Applications/Discord.app: accepted\nsource=Notarized Developer ID'" app=Discord
5055:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app=Discord
5056:  time=level=INFO msg="Looking for app: Discord, version: 0.0.397" app=Discord
5057:  time=level=INFO msg="Found app: 'Discord' at /Applications/Discord.app, Version: 0.0.397, Bundled Version: 0.0.397" app=Discord
5058:  time=level=INFO msg="Executing uninstall script for app..." app=Discord
5059:  time=level=INFO msg="Looking for app: Discord, version: 0.0.397" app=Discord
5060:  time=level=INFO msg="All checks passed for app: Discord (discord/darwin)"
5061:  time=level=INFO msg="Validating app: Docker Desktop (docker-desktop/darwin)"
5062:  time=level=INFO msg=Downloading...
5063:  time=level=INFO msg="Executing install script..." app="Docker Desktop"
5064:  time=level=INFO msg="New application detected at: /Applications/Docker.app" app="Docker Desktop"
5065:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Docker.app'" app="Docker Desktop"
5066:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Docker.app'" app="Docker Desktop"
5067:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app="Docker Desktop"
5068:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Docker.app: No such xattr: com.apple.quarantine'" app="Docker Desktop"
5069:  time=level=INFO msg="Spctl output error: <nil>" app="Docker Desktop"
5070:  time=level=INFO msg="spctl status: spctl status: '/Applications/Docker.app: accepted\nsource=Notarized Developer ID'" app="Docker Desktop"
5071:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app="Docker Desktop"
5072:  time=level=INFO msg="Looking for app: Docker Desktop, version: 4.80.0" app="Docker Desktop"
5073:  time=level=INFO msg="Found app: 'Docker' at /Applications/Docker.app, Version: 4.80.0, Bundled Version: 232116" app="Docker Desktop"
5074:  time=level=INFO msg="Executing uninstall script for app..." app="Docker Desktop"
5075:  time=level=INFO msg="Looking for app: Docker Desktop, version: 4.80.0" app="Docker Desktop"
5076:  time=level=INFO msg="All checks passed for app: Docker Desktop (docker-desktop/darwin)"
5077:  time=level=INFO msg="Validating app: Loom (loom/darwin)"
5078:  time=level=INFO msg=Downloading...
5079:  time=level=INFO msg="Executing install script..." app=Loom
5080:  time=level=INFO msg="New application detected at: /Applications/Loom.app" app=Loom
5081:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Loom.app'" app=Loom
5082:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Loom.app'" app=Loom
5083:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app=Loom
5084:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Loom.app: No such xattr: com.apple.quarantine'" app=Loom
5085:  time=level=INFO msg="Spctl output error: <nil>" app=Loom
5086:  time=level=INFO msg="spctl status: spctl status: '/Applications/Loom.app: accepted\nsource=Notarized Developer ID'" app=Loom
5087:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app=Loom
5088:  time=level=INFO msg="Looking for app: Loom, version: 0.357.1" app=Loom
5089:  time=level=INFO msg="Found app: 'Loom' at /Applications/Loom.app, Version: 0.357.1, Bundled Version: 0.357.1" app=Loom
5090:  time=level=INFO msg="Executing uninstall script for app..." app=Loom
5091:  time=level=INFO msg="Looking for app: Loom, version: 0.357.1" app=Loom
5092:  time=level=INFO msg="All checks passed for app: Loom (loom/darwin)"
5093:  time=level=INFO msg="Validating app: Microsoft Azure Storage Explorer (microsoft-azure-storage-explorer/darwin)"
5094:  time=level=INFO msg=Downloading...
5095:  time=level=INFO msg="Executing install script..." app="Microsoft Azure Storage Explorer"
5096:  time=level=INFO msg="New application detected at: /Applications/Microsoft Azure Storage Explorer.app" app="Microsoft Azure Storage Explorer"
5097:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Microsoft Azure Storage Explorer.app'" app="Microsoft Azure Storage Explorer"
5098:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Microsoft Azure Storage Explorer.app'" app="Microsoft Azure Storage Explorer"
5099:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app="Microsoft Azure Storage Explorer"
5100:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Microsoft Azure Storage Explorer.app: No such xattr: com.apple.quarantine'" app="Microsoft Azure Storage Explorer"
5101:  time=level=INFO msg="Spctl output error: <nil>" app="Microsoft Azure Storage Explorer"
5102:  time=level=INFO msg="spctl status: spctl status: '/Applications/Microsoft Azure Storage Explorer.app: accepted\nsource=Notarized Developer ID'" app="Microsoft Azure Storage Explorer"
5103:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app="Microsoft Azure Storage Explorer"
5104:  time=level=INFO msg="Looking for app: Microsoft Azure Storage Explorer, version: 1.44.0" app="Microsoft Azure Storage Explorer"
5105:  time=level=INFO msg="Found app: 'Microsoft Azure Storage Explorer' at /Applications/Microsoft Azure Storage Explorer.app, Version: 1.44.0, Bundled Version: 1.44.0" app="Microsoft Azure Storage Explorer"
5106:  time=level=INFO msg="Executing uninstall script for app..." app="Microsoft Azure Storage Explorer"
5107:  time=level=INFO msg="Looking for app: Microsoft Azure Storage Explorer, version: 1.44.0" app="Microsoft Azure Storage Explorer"
5108:  time=level=INFO msg="All checks passed for app: Microsoft Azure Storage Explorer (microsoft-azure-storage-explorer/darwin)"
5109:  time=level=INFO msg="Validating app: Mimestream (mimestream/darwin)"
5110:  time=level=INFO msg=Downloading...
5111:  time=level=INFO msg="Executing install script..." app=Mimestream
5112:  time=level=INFO msg="New application detected at: /Applications/Mimestream.app" app=Mimestream
5113:  time=level=INFO msg="Forcing LaunchServices refresh for: '/Applications/Mimestream.app'" app=Mimestream
5114:  time=level=INFO msg="Attempting to remove quarantine for: '/Applications/Mimestream.app'" app=Mimestream
5115:  time=level=INFO msg="Quarantine output error: checking quarantine status: exit status 1" app=Mimestream
5116:  time=level=INFO msg="Quarantine status: Quarantine status: 'xattr: /Applications/Mimestream.app: No such xattr: com.apple.quarantine'" app=Mimestream
5117:  time=level=INFO msg="Spctl output error: <nil>" app=Mimestream
5118:  time=level=INFO msg="spctl status: spctl status: '/Applications/Mimestream.app: accepted\nsource=Notarized Developer ID'" app=Mimestream
5119:  time=level=WARN msg="Error detected in post-installation steps: Error removing app quarantine: adding app to quarantine exceptions: exit status 4. Attempting to continue" app=Mimestream
5120:  time=level=INFO msg="Looking for app: Mimestream, version: 1.10.3" app=Mimestream
5121:  time=level=INFO msg="Found app: 'Mimestream' at /Applications/Mimestream.app, Version: 1.10.3, Bundled Version: 391" app=Mimestream
5122:  time=level=INFO msg="Executing uninstall script for app..." app=Mimestream
5123:  time=level=INFO msg="L...

@github-actions

Copy link
Copy Markdown
Contributor

Closing in favor of #48476.

@github-actions github-actions Bot closed this Jun 30, 2026
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.

2 participants