Update Fleet-maintained apps - #49763
Conversation
Generated automatically with cmd/maintained-apps.
Script Diff Resultsee/maintained-apps/outputs/advanced-installer/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/akiflow/darwin.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/cleanmymac/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/clop/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/comet/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/cursor/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/dataflare/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/firefox@nightly/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/granola/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/granola/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/lookaway/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/macwhisper/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/megasync/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/microsoft-teams/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/powerphotos/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/prisma-browser/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/pritunl/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/rustrover/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/setapp/darwin.json=== Install Script (no changes) ===
=== Uninstall // e23bd29d -> 9ef05eb6 ===
--- /tmp/old.y36rsS 2026-07-22 17:37:23.330978176 +0000
+++ /tmp/new.m4h0Ln 2026-07-22 17:37:23.330978176 +0000
@@ -5,6 +5,76 @@
LOGGED_IN_USER=$(scutil <<< "show State:/Users/ConsoleUser" | awk '/Name :/ { print $3 }')
# functions
+remove_launchctl_service() {
+ local service="$1"
+ local booleans=("true" "false")
+ local plist_status
+ local paths
+ local should_sudo
+
+ echo "Removing launchctl service ${service}"
+
+ # A wildcard label can't be used with launchctl or as a plist name, so expand
+ # it to the labels of currently loaded services that match the pattern.
+ local services=("$service")
+ if [[ "$service" == *"*"* ]]; then
+ local regex
+ # Escape regex metacharacters, turn '*' into '.*', and anchor the pattern so
+ # it matches a full label rather than a substring.
+ regex=$(printf '%s' "$service" | sed -e 's/[][(){}.^$+?|\\]/\\&/g' -e 's/\*/.*/g')
+ regex="^${regex}$"
+ services=()
+ local id
+ # Match every loaded job by label regardless of PID; launchctl list reports
+ # loaded-but-not-running jobs with a "-" in the PID column.
+ while read -r _ _ id; do
+ [[ "$id" =~ $regex ]] && services+=("$id")
+ done < <(launchctl list 2>/dev/null | tail -n +2)
+ if [[ ${#services[@]} -eq 0 ]]; then
+ echo "No loaded launchctl service matches ${service}"
+ return
+ fi
+ fi
+
+ local service_label
+ for service_label in "${services[@]}"; do
+ for should_sudo in "${booleans[@]}"; do
+ plist_status=$(launchctl list "${service_label}" 2>/dev/null)
+
+ if [[ $plist_status == \{* ]]; then
+ if [[ $should_sudo == "true" ]]; then
+ sudo launchctl remove "${service_label}"
+ else
+ launchctl remove "${service_label}"
+ fi
+ sleep 1
+ fi
+
+ paths=(
+ "/Library/LaunchAgents/${service_label}.plist"
+ "/Library/LaunchDaemons/${service_label}.plist"
+ )
+
+ # if not using sudo, prepend the home directory to the paths
+ if [[ $should_sudo == "false" ]]; then
+ for i in "${!paths[@]}"; do
+ paths[i]="${HOME}${paths[i]}"
+ done
+ fi
+
+ for path in "${paths[@]}"; do
+ if [[ -e "$path" ]]; then
+ if [[ $should_sudo == "true" ]]; then
+ sudo rm -f -- "$path"
+ else
+ rm -f -- "$path"
+ fi
+ fi
+ done
+ done
+ done
+}
+
trash() {
local logged_in_user="$1"
local target_file="$2"
@@ -52,10 +122,19 @@
fi
}
+remove_launchctl_service 'com.setapp.DesktopClient.SetappAgent'
+remove_launchctl_service 'com.setapp.DesktopClient.SetappAssistant'
+remove_launchctl_service 'com.setapp.DesktopClient.SetappLauncher'
+remove_launchctl_service 'com.setapp.DesktopClient.SetappUpdater'
sudo rm -rf "$APPDIR/Setapp.app"
trash $LOGGED_IN_USER '~/Library/Application Scripts/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'
+trash $LOGGED_IN_USER '~/Library/Application Support/Setapp*'
trash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient'
trash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient.SetappAgent'
+trash $LOGGED_IN_USER '~/Library/Containers/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'
+trash $LOGGED_IN_USER '~/Library/HTTPStorages/com.setapp.DesktopClient*'
+trash $LOGGED_IN_USER '~/Library/LaunchAgents/com.setapp.DesktopClient.*plist'
trash $LOGGED_IN_USER '~/Library/Logs/Setapp'
+trash $LOGGED_IN_USER '~/Library/Preferences/com.setapp.DesktopClient.plist'
trash $LOGGED_IN_USER '~/Library/Preferences/com.setapp.DesktopClient.SetappAgent.plist'
trash $LOGGED_IN_USER '~/Library/Saved Application State/com.setapp.DesktopClient.savedState'ee/maintained-apps/outputs/typora/windows.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/vivaldi/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/wechat/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) ===ee/maintained-apps/outputs/whatsapp/darwin.json=== Install Script (no changes) ===
=== Uninstall Script (no changes) === |
WalkthroughUpdated 23 maintained-app JSON entries across Windows and macOS with newer versions, patched-query thresholds, installer URLs, and SHA-256 checksums. Existing detection and script references remain unchanged for most entries. Setapp also received a new uninstall script that removes related launchctl services and expands cleanup across user Library locations. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 Checkov (3.3.8)ee/maintained-apps/outputs/advanced-installer/windows.jsonTraceback (most recent call last): ee/maintained-apps/outputs/akiflow/darwin.jsonTraceback (most recent call last): ee/maintained-apps/outputs/beyond-compare/darwin.jsonTraceback (most recent call last):
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/setapp/darwin.json`:
- Line 20: Update remove_launchctl_service to resolve the logged-in console
user’s UID and use launchctl asuser with the user’s gui/<uid> domain to boot out
each Setapp label, rather than calling launchctl list/remove in the
root/current-process domain. Keep removal of both user and system plist
locations as appropriate, but ensure the user LaunchAgents are resolved from
/Users/<logged-in-user> instead of root $HOME; preserve wildcard label matching
and the existing trash cleanup.
- Line 20: The cleanup script must handle cases where LOGGED_IN_USER is empty,
root, or loginwindow before invoking trash. Add validation after determining
LOGGED_IN_USER and skip the per-user trash calls when no valid GUI console user
exists; preserve system-wide cleanup such as launchctl removal and deleting
Setapp.app.
🪄 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: 2454856f-4c5e-4997-883a-ea86f2ded4be
📒 Files selected for processing (24)
ee/maintained-apps/outputs/advanced-installer/windows.jsonee/maintained-apps/outputs/akiflow/darwin.jsonee/maintained-apps/outputs/beyond-compare/darwin.jsonee/maintained-apps/outputs/cleanmymac/darwin.jsonee/maintained-apps/outputs/clop/darwin.jsonee/maintained-apps/outputs/comet/windows.jsonee/maintained-apps/outputs/cursor/darwin.jsonee/maintained-apps/outputs/dataflare/darwin.jsonee/maintained-apps/outputs/firefox@nightly/darwin.jsonee/maintained-apps/outputs/granola/darwin.jsonee/maintained-apps/outputs/granola/windows.jsonee/maintained-apps/outputs/lookaway/darwin.jsonee/maintained-apps/outputs/macwhisper/darwin.jsonee/maintained-apps/outputs/megasync/windows.jsonee/maintained-apps/outputs/microsoft-teams/windows.jsonee/maintained-apps/outputs/powerphotos/darwin.jsonee/maintained-apps/outputs/prisma-browser/windows.jsonee/maintained-apps/outputs/pritunl/windows.jsonee/maintained-apps/outputs/rustrover/windows.jsonee/maintained-apps/outputs/setapp/darwin.jsonee/maintained-apps/outputs/typora/windows.jsonee/maintained-apps/outputs/vivaldi/darwin.jsonee/maintained-apps/outputs/wechat/darwin.jsonee/maintained-apps/outputs/whatsapp/darwin.json
| "refs": { | ||
| "1ba0fd31": "#!/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# extract contents\nunzip \"$INSTALLER_PATH\" -d \"$TMPDIR\"\n# copy to the applications folder\nquit_and_track_application 'com.setapp.DesktopClient.SetappAgent'\nif [ -d \"$APPDIR/Setapp.app\" ]; then\n\tsudo mv \"$APPDIR/Setapp.app\" \"$TMPDIR/Setapp.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Setapp.app\" \"$APPDIR\"\nrelaunch_application 'com.setapp.DesktopClient.SetappAgent'\n", | ||
| "e23bd29d": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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\nsudo rm -rf \"$APPDIR/Setapp.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient.SetappAgent'\ntrash $LOGGED_IN_USER '~/Library/Logs/Setapp'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.setapp.DesktopClient.SetappAgent.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.setapp.DesktopClient.savedState'\n" | ||
| "9ef05eb6": "#!/bin/bash\n\n# variables\nAPPDIR=\"/Applications/\"\nLOGGED_IN_USER=$(scutil <<< \"show State:/Users/ConsoleUser\" | awk '/Name :/ { print $3 }')\n# functions\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 # A wildcard label can't be used with launchctl or as a plist name, so expand\n # it to the labels of currently loaded services that match the pattern.\n local services=(\"$service\")\n if [[ \"$service\" == *\"*\"* ]]; then\n local regex\n # Escape regex metacharacters, turn '*' into '.*', and anchor the pattern so\n # it matches a full label rather than a substring.\n regex=$(printf '%s' \"$service\" | sed -e 's/[][(){}.^$+?|\\\\]/\\\\&/g' -e 's/\\*/.*/g')\n regex=\"^${regex}$\"\n services=()\n local id\n # Match every loaded job by label regardless of PID; launchctl list reports\n # loaded-but-not-running jobs with a \"-\" in the PID column.\n while read -r _ _ id; do\n [[ \"$id\" =~ $regex ]] && services+=(\"$id\")\n done < <(launchctl list 2>/dev/null | tail -n +2)\n if [[ ${#services[@]} -eq 0 ]]; then\n echo \"No loaded launchctl service matches ${service}\"\n return\n fi\n fi\n\n local service_label\n for service_label in \"${services[@]}\"; do\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service_label}\" 2>/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service_label}\"\n else\n launchctl remove \"${service_label}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service_label}.plist\"\n \"/Library/LaunchDaemons/${service_label}.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 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.setapp.DesktopClient.SetappAgent'\nremove_launchctl_service 'com.setapp.DesktopClient.SetappAssistant'\nremove_launchctl_service 'com.setapp.DesktopClient.SetappLauncher'\nremove_launchctl_service 'com.setapp.DesktopClient.SetappUpdater'\nsudo rm -rf \"$APPDIR/Setapp.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Setapp*'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.setapp.DesktopClient.SetappAgent'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.setapp.DesktopClient*'\ntrash $LOGGED_IN_USER '~/Library/LaunchAgents/com.setapp.DesktopClient.*plist'\ntrash $LOGGED_IN_USER '~/Library/Logs/Setapp'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.setapp.DesktopClient.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.setapp.DesktopClient.SetappAgent.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.setapp.DesktopClient.savedState'\n" |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -u
echo "== file location =="
git ls-files | rg '(^|/)darwin\.json$' | sed -n '1,20p'
echo
echo "== target file relevant lines =="
cat -n ee/maintained-apps/outputs/setapp/darwin.json | sed -n '1,80p'
echo
echo "== find related scripts/settings =="
rg -n "export|LOGGED_IN_USER|launchctl list|launchctl remove|HomeDir|ConsoleUser|Setapp|LaunchAgents|LaunchDaemons|com.setapp" ee/maintained-apps -SRepository: fleetdm/fleet
Length of output: 50370
🌐 Web query:
Apple launchctl list remove root gui UID bootout man page
💡 Result:
In modern macOS, the bootout command is the recommended method for removing or stopping services, replacing the older load and unload commands [1]. Core Commands and Usage: 1. bootout: Use this to remove a service from a domain or stop it. The syntax is: launchctl bootout [domain-target]/[service-identifier] [2][3][4] Example (GUI domain): launchctl bootout gui/501/com.example.service [1][5] Example (System domain): sudo launchctl bootout system/com.example.daemon [1][6][7] 2. list: Use this to view loaded services. The recommended alternative for modern use is the print subcommand, which provides more detailed output [3][8]. launchctl list [2][1] launchctl print [domain-target]/[service-identifier] [3][9] 3. Domain Specifiers: - gui/UID: Targets the graphical user interface domain for a specific user [4][9]. - system/: Targets the system-wide domain (requires root privileges for modifications) [2][9]. - user/UID: Targets the user domain (can exist independently of a GUI session) [4][9]. Important Considerations: - Remove vs. Bootout: While older versions of launchctl included a remove command [10][8], bootout is the current standard for managing the lifecycle of services in defined domains [1][7]. - Persistence: If you want to permanently disable a service so it does not restart after a reboot, you must remove or move its corresponding.plist file (e.g., from /Library/LaunchDaemons or ~/Library/LaunchAgents) [6]. Using bootout only stops the current instance [6][7]. - Disabling: You can also use the disable subcommand to prevent a service from loading in future sessions without deleting the file [3][5]. launchctl disable [domain-target]/[service-identifier] [5][11] Always verify the exact service identifier by using the list or print subcommands before attempting to bootout or disable a service [3][6][9].
Citations:
- 1: https://linuxcommandlibrary.com/man/launchctl
- 2: https://manp.gs/mac/1/launchctl
- 3: https://leancrew.com/all-this/man/man1/launchctl.html
- 4: https://www.unix.com/man_page/mojave/1/launchctl/
- 5: https://apple.stackexchange.com/questions/471436/disabling-services-daemons-agents-with-launchctl
- 6: https://apple.stackexchange.com/questions/74779/launchtl-any-way-to-disable-a-daemon-after-removing-the-plist-file
- 7: https://apple.stackexchange.com/questions/366281/launchd-confusion-on-semantics-of-bootstrap-and-bootout-etc-after-reading-manu
- 8: https://github.com/Apple-FOSS-Mirror/launchd/blob/master/support/launchctl.c
- 9: https://keith.github.io/xcode-man-pages/launchctl.1.html
- 10: https://real-world-systems.com/docs/launchctl.1.html
- 11: https://www.real-world-systems.com/docs/launchctl.1.html
🏁 Script executed:
#!/bin/bash
set -u
echo "== setapp darwin.json =="
sed -n '1,90p' ee/maintained-apps/outputs/setapp/darwin.json
echo
echo "== setapp-related launchctl paths =="
rg -n "launchctl|asuser|BOOTSTRAP_DOMAIN|HomeDir|console|ConsoleUser|SetappAgent|SetappAssistant|SetappLauncher|SetappUpdater|LaunchAgents|LaunchDaemons" ee/maintained-apps/outputs/setapp ee/maintained-apps/outputs/busycontacts ee/maintained-apps/outputs/supportcompanion ee/maintained-apps/outputs/runjs ee/maintained-apps/outputs -S | sed -n '1,200p'Repository: fleetdm/fleet
Length of output: 50370
Remove Setapp launches from the logged-in user’s launchctl domain.
launchctl list/remove here targets root’s/current-process domain, not the user’s gui/<uid>/user domain, so these per-user agents can stay loaded even after their ~/Library/LaunchAgents/* plists are deleted and later relaunch. Resolve the console UID and launchctl asuser into /bootout each Setapp label, then trash the corresponding user LaunchAgents without relying on root $HOME.
🤖 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/setapp/darwin.json` at line 20, Update
remove_launchctl_service to resolve the logged-in console user’s UID and use
launchctl asuser with the user’s gui/<uid> domain to boot out each Setapp label,
rather than calling launchctl list/remove in the root/current-process domain.
Keep removal of both user and system plist locations as appropriate, but ensure
the user LaunchAgents are resolved from /Users/<logged-in-user> instead of root
$HOME; preserve wildcard label matching and the existing trash cleanup.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- locate file ---'
git ls-files | grep -F 'ee/maintained-apps/outputs/setapp/darwin.json' || true
echo '--- read relevant file excerpt ---'
wc -l ee/maintained-apps/outputs/setapp/darwin.json || true
sed -n '1,180p' ee/maintained-apps/outputs/setapp/darwin.json | cat -n
echo '--- run shell behavior probe for the exact command shape mentioned ---'
tmplog="$(mktemp)"
tmpuser="$(mktemp)"
cat > "$tmplog" <<'BASH'
#!/bin/bash
set -u
# Simulate the function body and the exact call used in the output.
remove_launchctl_service() {
echo "remove_launchctl_service: $*"
}
trash() {
local logged_in_user="$1"
local target_file="$2"
local timestamp="1970-01-01-0"
local rand="123"
if [[ "$target_file" == ~* ]]; then
target_file="/Users/$logged_in_user${target_file:1}"
fi
local file_name
if [[ -e "$target_file" ]]; then
echo "removing $target_file."
mv -f "$target_file" "/Users/$logged_in_user/.Trash/${file_name}_${timestamp}_${rand}" || true
else
echo "$target_file doesn't exist."
fi
}
echo "LOGGED_IN_USER: [${LOGGED_IN_USER}]"
trash $LOGGED_IN_USER '~/Library/Application Scripts/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'
BASH
chmod +x "$tmplog"
for logged_in_user in '' 'root' 'loginwindow' 'alice'; do
echo "=== LOGGED_IN_USER=${logged_in_user:-<empty>} ==="
LOGGED_IN_USER="$logged_in_user" bash "$tmplog"
done
rm -f "$tmplog" "$tmpuser"Repository: fleetdm/fleet
Length of output: 10103
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- scan Setapp uninstall references for installed uninstall refs ---'
rg -n '"uninstall_script_ref":\s*"9ef05eb6"|uninstall_script_ref|LOGGED_IN_USER|Library/Application Scripts|Library/LaunchAgents' . --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' || true
echo '--- shell argument-shape probe with current-dir function definitions isolated ---'
tmp="$(mktemp -d)"
cat > "$tmp/uninstall.sh" <<'BASH'
#!/bin/bash
set -u
APPDIR="/Applications/"
LOGGED_IN_USER=$(printf '%s' "$1")
trash() {
local logged_in_user="$1"
local target_file="$2"
local timestamp="1970-01-01-0"
local rand="123"
if [[ "$target_file" == ~* ]]; then
target_file="/Users/$logged_in_user${target_file:1}"
fi
file_name="$(basename "$target_file")"
if [[ -e "$target_file" ]]; then
echo "removing $target_file."
echo "trash target: $target_file"
else
echo "$target_file doesn't exist."
fi
}
remove_launchctl_service() {
echo "remove_launchctl_service: $*"
}
remove_launchctl_service 'com.setapp.DesktopClient.SetappAgent'
sudo_rm_status=0
sudo -n rm -rf "$APPDIR/Setapp.app" 2>/dev/null || sudo_rm_status="$?"
echo "sudo rm return: $sudo_rm_status"
trash $LOGGED_IN_USER '~/Library/Application Scripts/com.setapp.DesktopClient.SetappAgent.FinderSyncExt'
BASH
chmod +x "$tmp/uninstall.sh"
for logged_in_user in '' 'root' 'loginwindow' 'alice'; do
echo "=== LOGGED_IN_USER=${logged_in_user:-<empty>} ==="
bash "$tmp/uninstall.sh" "$logged_in_user" || true
done
rm -rf "$tmp"Repository: fleetdm/fleet
Length of output: 50370
Handle the no-consoul-user case before invoking trash.
LOGGED_IN_USER can be empty, root, or loginwindow; with an empty value, trash $LOGGED_IN_USER '~/Library/...' sends the file path into $1 and leaves $2 empty, so the ~ expansion and cleanup path become invalid. Validate LOGGED_IN_USER and skip or specially handle per-user cleanup when no GUI console user exists.
🤖 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/setapp/darwin.json` at line 20, The cleanup script
must handle cases where LOGGED_IN_USER is empty, root, or loginwindow before
invoking trash. Add validation after determining LOGGED_IN_USER and skip the
per-user trash calls when no valid GUI console user exists; preserve system-wide
cleanup such as launchctl removal and deleting Setapp.app.
|
Closing in favor of #49776. |
Automated ingestion of latest Fleet-maintained app data.
FMA installer verification report
Mode: report-only · Scope: changed vs.
HEAD· 140 app(s) verified, 10 failure(s), 0 warning(s)Failure and warning details
advanced-installer/windows (23.9)
betterzip/darwin (6.0)
downie/darwin (4.12.11)
emclient/windows (10.4.5642)
miro/windows (0.11.162)
naps2/windows (8.3.1)
notion/windows (7.27.0)
power-bi/windows (2.156.951.0)
tableau/darwin (2026.2.1)
teleport-connect/windows (18.10.1)
Summary by CodeRabbit