Skip to content

Update Fleet-maintained apps - #50127

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

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

Conversation

@fleet-release

@fleet-release fleet-release commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Automated ingestion of latest Fleet-maintained app data.

Summary by CodeRabbit

  • New Features
    • Updated maintained app versions and installer metadata for AnyBurn, ExifCleaner, Firefox Nightly, Postman, Tailscale, and other supported applications.
  • Bug Fixes
    • Improved macOS uninstall cleanup across numerous applications by removing additional caches, preferences, support files, extensions, recent-document entries, logs, and related user data.
    • Updated uninstall handling for newer app releases, including CMake, EtreCheckPro, Nextcloud, TeamViewer, Thunderbird, and others.

Generated automatically with cmd/maintained-apps.
@github-actions

Copy link
Copy Markdown
Contributor

Script Diff Results

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

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

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

=== Install Script (no changes) ===
=== Uninstall // 31c0d61d -> ab5ff0cc ===

--- /tmp/old.f4PY4t	2026-07-29 08:59:29.071819091 +0000
+++ /tmp/new.NrDVSk	2026-07-29 08:59:29.071819091 +0000
@@ -169,6 +169,13 @@
 sudo rm -rf '/Library/ScriptingAdditions/BartenderHelper.osax'
 sudo rm -rf '/System/Library/ScriptingAdditions/BartenderSystemHelper.osax'
 sudo rm -rf "$APPDIR/Bartender 6.app"
+trash $LOGGED_IN_USER '~/Library/Application Scripts/24J875RH8J.com.surteesstudios.Bartender'
+trash $LOGGED_IN_USER '~/Library/Application Support/Bartender 6'
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.surteesstudios.bartender.sfl*'
+trash $LOGGED_IN_USER '~/Library/Application Support/com.surteesstudios.Bartender.revenuecat'
 trash $LOGGED_IN_USER '~/Library/Caches/com.surteesstudios.Bartender'
+trash $LOGGED_IN_USER '~/Library/Caches/com.surteesstudios.Bartender.revenuecat'
 trash $LOGGED_IN_USER '~/Library/Cookies/com.surteesstudios.Bartender.binarycookies'
+trash $LOGGED_IN_USER '~/Library/Group Containers/24J875RH8J.com.surteesstudios.Bartender'
+trash $LOGGED_IN_USER '~/Library/HTTPStorages/com.surteesstudios.Bartender'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.surteesstudios.Bartender.plist'

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

=== Install Script (no changes) ===
=== Uninstall // 87ce57a8 -> ff358276 ===

--- /tmp/old.FeygjG	2026-07-29 08:59:29.115819190 +0000
+++ /tmp/new.wRrjdI	2026-07-29 08:59:29.115819190 +0000
@@ -53,5 +53,6 @@
 }
 
 sudo rm -rf "$APPDIR/CMake.app"
+trash $LOGGED_IN_USER '~/Library/Preferences/com.kitware.CMakeSetup.plist'
 trash $LOGGED_IN_USER '~/Library/Preferences/org.cmake.cmake.plist'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/org.cmake.cmake.savedState'

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

=== Install Script (no changes) ===
=== Uninstall // e680fa19 -> 3939b006 ===

--- /tmp/old.3zldRJ	2026-07-29 08:59:29.159819290 +0000
+++ /tmp/new.DVITMb	2026-07-29 08:59:29.159819290 +0000
@@ -55,5 +55,6 @@
 sudo rm -rf "$APPDIR/EtreCheckPro.app"
 trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.etresoft.etrecheck*.sfl*'
 trash $LOGGED_IN_USER '~/Library/Caches/com.etresoft.EtreCheck*'
+trash $LOGGED_IN_USER '~/Library/HTTPStorages/com.etresoft.EtreCheck*'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.etresoft.EtreCheck*.plist'
 trash $LOGGED_IN_USER '~/Library/WebKit/com.etresoft.EtreCheck*'

ee/maintained-apps/outputs/exifcleaner/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/gdevelop/darwin.json

=== Install Script (no changes) ===
=== Uninstall // cbcf926e -> c1bae326 ===

--- /tmp/old.ukzB7e	2026-07-29 08:59:29.294819595 +0000
+++ /tmp/new.3xKeRq	2026-07-29 08:59:29.295819597 +0000
@@ -53,6 +53,7 @@
 }
 
 sudo rm -rf "$APPDIR/GDevelop 5.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.gdevelop-app.ide.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/GDevelop 5'
 trash $LOGGED_IN_USER '~/Library/Logs/GDevelop 5'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.gdevelop-app.ide.plist'

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

=== Install Script (no changes) ===
=== Uninstall // 7b9b9d06 -> 8135b983 ===

--- /tmp/old.soVHfN	2026-07-29 08:59:29.346819712 +0000
+++ /tmp/new.uT9F3T	2026-07-29 08:59:29.347819714 +0000
@@ -53,6 +53,7 @@
 }
 
 sudo rm -rf "$APPDIR/Granola.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.granola.app.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/Granola'
 trash $LOGGED_IN_USER '~/Library/Caches/com.granola.app'
 trash $LOGGED_IN_USER '~/Library/HTTPStorages/com.granola.app'

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

=== Install Script (no changes) ===
=== Uninstall // f35082d0 -> 85ab2d46 ===

--- /tmp/old.i2tb6p	2026-07-29 08:59:29.401819836 +0000
+++ /tmp/new.tb7Ot3	2026-07-29 08:59:29.401819836 +0000
@@ -53,6 +53,7 @@
 }
 
 sudo rm -rf "$APPDIR/jamovi.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/org.jamovi.jamovi.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/jamovi'
 trash $LOGGED_IN_USER '~/Library/Logs/jamovi'
 trash $LOGGED_IN_USER '~/Library/Preferences/org.jamovi.jamovi.plist'

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

=== Install Script (no changes) ===
=== Uninstall // 1e1dcbc9 -> 983b071c ===

--- /tmp/old.bA2iGD	2026-07-29 08:59:29.448819943 +0000
+++ /tmp/new.5Dhpgz	2026-07-29 08:59:29.449819945 +0000
@@ -163,12 +163,17 @@
 remove_pkg_files 'app.kaleidoscope.uninstall_ksdiff'
 forget_pkg 'app.kaleidoscope.uninstall_ksdiff'
 sudo rm -rf "$APPDIR/Kaleidoscope.app"
+trash $LOGGED_IN_USER '~/Library/Application Scripts/app.kaleidoscope.v*.KaleidoscopePrism'
+trash $LOGGED_IN_USER '~/Library/Application Scripts/app.kaleidoscope.v*.KSShareExtension'
 trash $LOGGED_IN_USER '~/Library/Application Support/app.kaleidoscope.v*'
 trash $LOGGED_IN_USER '~/Library/Application Support/com.blackpixel.kaleidoscope'
 trash $LOGGED_IN_USER '~/Library/Application Support/Kaleidoscope'
 trash $LOGGED_IN_USER '~/Library/Caches/app.kaleidoscope.v*'
 trash $LOGGED_IN_USER '~/Library/Caches/com.blackpixel.kaleidoscope'
 trash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.blackpixel.kaleidoscope'
+trash $LOGGED_IN_USER '~/Library/Containers/app.kaleidoscope.v*.KaleidoscopePrism'
+trash $LOGGED_IN_USER '~/Library/Containers/app.kaleidoscope.v*.KSShareExtension'
+trash $LOGGED_IN_USER '~/Library/HTTPStorages/app.kaleidoscope.v*'
 trash $LOGGED_IN_USER '~/Library/Preferences/app.kaleidoscope.v*.plist'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.blackpixel.kaleidoscope.plist'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/app.kaleidoscope.v*.savedState'

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

=== Install Script (no changes) ===
=== Uninstall // ba6e6e70 -> 3b479c55 ===

--- /tmp/old.xlrgMZ	2026-07-29 08:59:29.497820053 +0000
+++ /tmp/new.Qx9wfy	2026-07-29 08:59:29.498820055 +0000
@@ -236,6 +236,7 @@
 forget_pkg 'com.malwarebytes.mbam.*'
 sudo rm -rf '/Library/Application Support/Malwarebytes/MBAM'
 sudo rmdir '/Library/Application Support/Malwarebytes'
+trash $LOGGED_IN_USER '~/Library/Application Support/com.malwarebytes.mbam'
 trash $LOGGED_IN_USER '~/Library/Application Support/com.malwarebytes.mbam.frontend.*'
 trash $LOGGED_IN_USER '~/Library/Caches/com.crashlytics.data/com.malwarebytes.mbam.frontend.*'
 trash $LOGGED_IN_USER '~/Library/Caches/com.malwarebytes.mbam.frontend.*'

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

=== Install Script (no changes) ===
=== Uninstall // 3ec00885 -> 6420e7ee ===

--- /tmp/old.xmAZLU	2026-07-29 08:59:29.545820162 +0000
+++ /tmp/new.9wvcxA	2026-07-29 08:59:29.545820162 +0000
@@ -233,10 +233,14 @@
 remove_pkg_files 'com.nextcloud.desktopclient'
 forget_pkg 'com.nextcloud.desktopclient'
 sudo rm -rf '/Applications/Nextcloud.app'
+trash $LOGGED_IN_USER '~/Library/Application Scripts/com.nextcloud.desktopclient.FileProviderExt'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.nextcloud.desktopclient.FinderSyncExt'
+trash $LOGGED_IN_USER '~/Library/Application Scripts/NKUJUXUJ3B.com.nextcloud.desktopclient'
 trash $LOGGED_IN_USER '~/Library/Application Support/Nextcloud'
 trash $LOGGED_IN_USER '~/Library/Caches/Nextcloud'
+trash $LOGGED_IN_USER '~/Library/Containers/com.nextcloud.desktopclient.FileProviderExt'
 trash $LOGGED_IN_USER '~/Library/Containers/com.nextcloud.desktopclient.FinderSyncExt'
 trash $LOGGED_IN_USER '~/Library/Group Containers/com.nextcloud.desktopclient'
+trash $LOGGED_IN_USER '~/Library/Group Containers/NKUJUXUJ3B.com.nextcloud.desktopclient'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.nextcloud.desktopclient.plist'
 trash $LOGGED_IN_USER '~/Library/Preferences/Nextcloud'

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

=== Install Script (no changes) ===
=== Uninstall // 6c8b1ba5 -> ddfd228c ===

--- /tmp/old.hJCpyE	2026-07-29 08:59:29.590820263 +0000
+++ /tmp/new.R7u58R	2026-07-29 08:59:29.590820263 +0000
@@ -55,5 +55,6 @@
 sudo rm -rf "$APPDIR/OmniOutliner.app"
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6.Thumbnails'
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.omnigroup.omnioutliner*.sfl*'
 trash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6'
 trash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6.Thumbnails'

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

=== Install Script (no changes) ===
=== Uninstall // 966b2fa5 -> 20883323 ===

--- /tmp/old.bInhc9	2026-07-29 08:59:29.634820363 +0000
+++ /tmp/new.yx2GKb	2026-07-29 08:59:29.634820363 +0000
@@ -53,6 +53,7 @@
 }
 
 sudo rm -rf "$APPDIR/Postman.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.postmanlabs.mac.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/com.postmanlabs.mac.ShipIt'
 trash $LOGGED_IN_USER '~/Library/Application Support/Postman'
 trash $LOGGED_IN_USER '~/Library/Caches/com.postmanlabs.mac'

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

=== Install Script (no changes) ===
=== Uninstall // 86416480 -> 6a559637 ===

--- /tmp/old.AgMaYA	2026-07-29 08:59:29.679820464 +0000
+++ /tmp/new.5vCMkw	2026-07-29 08:59:29.679820464 +0000
@@ -53,10 +53,12 @@
 }
 
 sudo rm -rf "$APPDIR/Raindrop.io.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/io.raindrop.macapp.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/Raindrop.io'
 trash $LOGGED_IN_USER '~/Library/Caches/com.apple.Safari/Extensions/Raindrop.io.safariextension'
 trash $LOGGED_IN_USER '~/Library/Cookies/io.raindrop.mac.binarycookies'
 trash $LOGGED_IN_USER '~/Library/Preferences/io.raindrop.mac.helper.plist'
 trash $LOGGED_IN_USER '~/Library/Preferences/io.raindrop.mac.plist'
+trash $LOGGED_IN_USER '~/Library/Preferences/io.raindrop.macapp.plist'
 trash $LOGGED_IN_USER '~/Library/Safari/Extensions/Raindrop.io.safariextz'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/io.raindrop.mac.savedState'

ee/maintained-apps/outputs/remote-desktop-manager/darwin.json

=== Install Script (no changes) ===
=== Uninstall // 4fd2b3fa -> 07fb9072 ===

--- /tmp/old.8uV3eJ	2026-07-29 08:59:29.723820564 +0000
+++ /tmp/new.Wami9A	2026-07-29 08:59:29.723820564 +0000
@@ -58,3 +58,4 @@
 trash $LOGGED_IN_USER '~/Library/Caches/com.devolutions.remotedesktopmanager'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.devolutions.remotedesktopmanager.plist'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/com.devolutions.remotedesktopmanager.savedState'
+trash $LOGGED_IN_USER '~/Library/WebKit/com.devolutions.remotedesktopmanager'

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

=== Install Script (no changes) ===
=== Uninstall // 1b0bcecf -> 9714135f ===

--- /tmp/old.vCSmnq	2026-07-29 08:59:29.769820667 +0000
+++ /tmp/new.JdySIC	2026-07-29 08:59:29.769820667 +0000
@@ -58,6 +58,7 @@
 trash $LOGGED_IN_USER '~/Library/Application Support/com.rightfontapp.RightFont*'
 trash $LOGGED_IN_USER '~/Library/Application Support/RightFont'
 trash $LOGGED_IN_USER '~/Library/Caches/com.rightfontapp.RightFont*'
+trash $LOGGED_IN_USER '~/Library/HTTPStorages/com.rightfontapp.RightFont5'
 trash $LOGGED_IN_USER '~/Library/Logs/RightFont*'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.rightfontapp.RightFont*.plist'
 trash $LOGGED_IN_USER '~/Library/WebKit/com.rightfontapp.RightFont*'

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

=== Install // 92413a45 -> 06b9111a ===

--- /tmp/old.3O0BXn	2026-07-29 08:59:29.823820790 +0000
+++ /tmp/new.sZKeaQ	2026-07-29 08:59:29.824820792 +0000
@@ -96,5 +96,5 @@
 
 # install pkg files
 quit_and_track_application 'io.tailscale.ipn.macsys'
-sudo installer -pkg "$TMPDIR/Tailscale-1.98.9-macos.pkg" -target /
+sudo installer -pkg "$TMPDIR/Tailscale-1.98.10-macos.pkg" -target /
 relaunch_application 'io.tailscale.ipn.macsys'

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

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

=== Install Script (no changes) ===
=== Uninstall // d0cdc2d9 -> f00ebab2 ===

--- /tmp/old.Q6a1TI	2026-07-29 08:59:29.874820905 +0000
+++ /tmp/new.71h8k7	2026-07-29 08:59:29.874820905 +0000
@@ -250,6 +250,7 @@
 forget_pkg 'TeamViewerUninstaller'
 sudo rm -rf '/Applications/TeamViewer.app'
 sudo rm -rf '/Library/Preferences/com.teamviewer*'
+trash $LOGGED_IN_USER '/Library/Application Support/TeamViewer'
 trash $LOGGED_IN_USER '~/Library/Application Support/TeamViewer'
 trash $LOGGED_IN_USER '~/Library/Caches/com.teamviewer.TeamViewer'
 trash $LOGGED_IN_USER '~/Library/Caches/TeamViewer'

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

=== Install Script (no changes) ===
=== Uninstall // 88749f85 -> 907ef18f ===

--- /tmp/old.LC1bM7	2026-07-29 08:59:29.919821006 +0000
+++ /tmp/new.g1PVMv	2026-07-29 08:59:29.919821006 +0000
@@ -55,6 +55,7 @@
 sudo rm -rf "$APPDIR/Thunderbird.app"
 sudo rmdir '~/Library/Caches/Mozilla'
 trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/org.mozilla.thunderbird*.sfl*'
+trash $LOGGED_IN_USER '~/Library/Application Support/Thunderbird'
 trash $LOGGED_IN_USER '~/Library/Caches/Mozilla/updates/Applications/Thunderbird*'
 trash $LOGGED_IN_USER '~/Library/Caches/Thunderbird'
 trash $LOGGED_IN_USER '~/Library/Preferences/org.mozilla.thunderbird*.plist'

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

=== Install Script (no changes) ===
=== Uninstall // d196a4c8 -> 6ca374e8 ===

--- /tmp/old.OFPFzy	2026-07-29 08:59:29.967821115 +0000
+++ /tmp/new.n31dsz	2026-07-29 08:59:29.967821115 +0000
@@ -53,6 +53,7 @@
 }
 
 sudo rm -rf "$APPDIR/WebCatalog.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.webcatalog.jordan.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/WebCatalog'
 trash $LOGGED_IN_USER '~/Library/Caches/com.webcatalog.jordan'
 trash $LOGGED_IN_USER '~/Library/Caches/com.webcatalog.jordan.ShipIt'

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

=== Install Script (no changes) ===
=== Uninstall // 18d6be1e -> a5e852b0 ===

--- /tmp/old.aSHXOB	2026-07-29 08:59:30.022821239 +0000
+++ /tmp/new.tfJ7xl	2026-07-29 08:59:30.022821239 +0000
@@ -96,6 +96,7 @@
 sudo rm -rf "$APPDIR/WeChat.app"
 trash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat.IPCHelper'
+trash $LOGGED_IN_USER '~/Library/Application Scripts/5A4RE8SF68.com.tencent.xinWeChat'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.MiniProgram'
 trash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.WeChatMacShare'
@@ -106,5 +107,6 @@
 trash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat.WeChatMacShare'
 trash $LOGGED_IN_USER '~/Library/Cookies/com.tencent.xinWeChat.binarycookies'
 trash $LOGGED_IN_USER '~/Library/Group Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat'
+trash $LOGGED_IN_USER '~/Library/Group Containers/5A4RE8SF68.com.tencent.xinWeChat'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.tencent.xinWeChat.plist'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/com.tencent.xinWeChat.savedState'

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

=== Install Script (no changes) ===
=== Uninstall // effe5345 -> 71fe81c3 ===

--- /tmp/old.aPID25	2026-07-29 08:59:30.075821359 +0000
+++ /tmp/new.2GTnzL	2026-07-29 08:59:30.075821359 +0000
@@ -53,6 +53,8 @@
 }
 
 sudo rm -rf "$APPDIR/WorkFlowy.app"
+trash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.workflowy.desktop.sfl*'
 trash $LOGGED_IN_USER '~/Library/Application Support/WorkFlowy'
+trash $LOGGED_IN_USER '~/Library/Logs/WorkFlowy'
 trash $LOGGED_IN_USER '~/Library/Preferences/com.workflowy.desktop.plist'
 trash $LOGGED_IN_USER '~/Library/Saved Application State/com.workflowy.desktop.savedState'

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Updated maintained-app metadata for AnyBurn, ExifCleaner, Firefox Nightly, Postman, and Tailscale. Revised macOS uninstall script references and embedded scripts for multiple applications, adding or changing cleanup of application data, shared recent-document lists, containers, preferences, caches, HTTP storage, logs, services, receipts, and app bundles.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is far too brief and does not follow the required template or checklist sections. Expand it to include the required Related issue line and the checklist/testing sections from the repository template.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches 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-2607290854

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: 4

🤖 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/omnioutliner/darwin.json`:
- Line 20: Update the recent-document glob passed to trash in the OmniOutliner
cleanup script to use the correctly cased bundle identifier,
com.omnigroup.OmniOutliner6*.sfl*. Leave the surrounding cleanup paths and trash
function unchanged.

In `@ee/maintained-apps/outputs/rightfont/darwin.json`:
- Line 20: Update the RightFont cleanup command in the uninstall script to
target /Users/$LOGGED_IN_USER/RightFont instead of the single-quoted
'~/RightFont' literal. Preserve the existing rmdir behavior while using the
already-captured LOGGED_IN_USER value.

In `@ee/maintained-apps/outputs/teamviewer/darwin.json`:
- Line 20: Update the TeamViewer system-preference removal command near the
cleanup calls so the com.teamviewer* wildcard is expanded by a shell running
with sudo rather than passed as a quoted literal to rm. Preserve removal of all
matching entries under /Library/Preferences, and keep the existing cleanup scope
unchanged.

In `@ee/maintained-apps/outputs/wechat/darwin.json`:
- Line 20: Resolve the TeamIdentifierPrefix value before invoking trash for the
affected Application Scripts, Containers, and Group Containers paths. Update the
call sites around the trash function so the paths contain the concrete prefix
rather than the literal $(TeamIdentifierPrefix) text, while preserving the
existing cleanup behavior.
🪄 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 Plus

Run ID: c1999e17-39ca-42c6-8cac-583a29b9d7e2

📥 Commits

Reviewing files that changed from the base of the PR and between 5983f9d and 56bcf2d.

📒 Files selected for processing (23)
  • ee/maintained-apps/outputs/anyburn/windows.json
  • ee/maintained-apps/outputs/bartender/darwin.json
  • ee/maintained-apps/outputs/cmake-app/darwin.json
  • ee/maintained-apps/outputs/etrecheckpro/darwin.json
  • ee/maintained-apps/outputs/exifcleaner/darwin.json
  • ee/maintained-apps/outputs/firefox@nightly/darwin.json
  • ee/maintained-apps/outputs/gdevelop/darwin.json
  • ee/maintained-apps/outputs/granola/darwin.json
  • ee/maintained-apps/outputs/jamovi/darwin.json
  • ee/maintained-apps/outputs/kaleidoscope/darwin.json
  • ee/maintained-apps/outputs/malwarebytes/darwin.json
  • ee/maintained-apps/outputs/nextcloud/darwin.json
  • ee/maintained-apps/outputs/omnioutliner/darwin.json
  • ee/maintained-apps/outputs/postman/darwin.json
  • ee/maintained-apps/outputs/raindropio/darwin.json
  • ee/maintained-apps/outputs/remote-desktop-manager/darwin.json
  • ee/maintained-apps/outputs/rightfont/darwin.json
  • ee/maintained-apps/outputs/tailscale-app/darwin.json
  • ee/maintained-apps/outputs/teamviewer/darwin.json
  • ee/maintained-apps/outputs/thunderbird/darwin.json
  • ee/maintained-apps/outputs/webcatalog/darwin.json
  • ee/maintained-apps/outputs/wechat/darwin.json
  • ee/maintained-apps/outputs/workflowy/darwin.json

"refs": {
"535cbe5c": "#!/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\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.omnigroup.OmniOutliner6'\nif [ -d \"$APPDIR/OmniOutliner.app\" ]; then\n\tsudo mv \"$APPDIR/OmniOutliner.app\" \"$TMPDIR/OmniOutliner.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/OmniOutliner.app\" \"$APPDIR\"\nrelaunch_application 'com.omnigroup.OmniOutliner6'\n",
"6c8b1ba5": "#!/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/OmniOutliner.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6.Thumbnails'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6.Thumbnails'\n"
"ddfd228c": "#!/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/OmniOutliner.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.omnigroup.OmniOutliner6.Thumbnails'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.omnigroup.omnioutliner*.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.omnigroup.OmniOutliner6.Thumbnails'\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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file location =="
git ls-files | grep 'ee/maintained-apps/outputs/omnioutliner/darwin.json' || true

echo "== relevant lines =="
if [ -f ee/maintained-apps/outputs/omnioutliner/darwin.json ]; then
  cat -n ee/maintained-apps/outputs/omnioutliner/darwin.json | sed -n '15,35p'
fi

echo "== all OmniOutliner references =="
rg -n "omnioutliner|OmniOutliner|com\.omnigroup|sfl" ee/maintained-apps/outputs/omnioutliner Darwin || true

Repository: fleetdm/fleet

Length of output: 13196


🌐 Web query:

macOS com.apple.sharedfilelist ApplicationRecentDocuments bundle identifier recent documents filename casing sfl

💡 Result:

On macOS, the directory ~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/ stores recent document lists for individual applications [1][2][3]. These lists are maintained by the sharedfilelistd daemon [4]. Technical details regarding these files include: 1. Filename Casing and Identity: The files within this directory are typically named using the application's bundle identifier followed by an extension (e.g., com.example.appname.sfl2) [3][5]. While bundle identifiers are technically case-sensitive, they are conventionally written in lowercase [5]. When programmatically interacting with these files, it is standard practice to treat the bundle identifier as the primary key [3][5]. 2. File Format: These files are binary property lists, specifically formatted as NSKeyedArchiver archives [6][7]. While historically referred to as.sfl files (introduced in OS X 10.11), they have evolved into.sfl2 and.sfl3 formats in later versions of macOS [1][8][9]. Despite the extension change, they remain encoded property lists that contain serialized objects, such as SFLListItems, which store metadata and Bookmarks (binary BLOBs) pointing to the actual files [1][8][6]. 3. Content: The files do not store human-readable file paths directly; instead, they use persistent Bookmarks that the system resolves to locate files [1][8]. Because they are binary archives, they cannot be read directly as text files and require specialized tools or unarchiving via the NSKeyedUnarchiver class to extract their contents [10][6][7]. If you are encountering issues such as a failure to populate the "Recent Documents" menu, it is often due to the corruption of these specific files [11]. Removing the associated.sfl/.sfl2/.sfl3 file for the affected application and restarting the sharedfilelistd daemon (via the command killall sharedfilelistd) is a common troubleshooting step [3][11].

Citations:


🌐 Web query:

site:eclecticlight.co macOS recent documents bundle identifier casing Omniscript .sfl

💡 Result:

On macOS, the "Recent Items" and "Open Recent" lists are managed by the Launch Services framework, which utilizes Shared File List (.sfl,.sfl2,.sfl3) property list files [1][2][3][4]. These files are stored in the user's Library directory at: ~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/ In this directory, individual.sfl files are named according to the application's bundle identifier [1]. For example, an application with the bundle ID com.example.app would correspond to a file named com.example.app.sfl (or.sfl2/3 in newer versions) [1][5]. Regarding your query about "Omniscript" and casing: 1. Bundle Identifier Casing: macOS bundle identifiers are conventionally written in reverse domain name notation using lowercase letters (e.g., com.apple.textedit) [6]. While the file system on macOS is typically case-insensitive (but case-preserving), bundle identifiers themselves are treated as case-sensitive in many system contexts. It is standard practice to maintain them in lowercase to ensure compatibility and avoid resolution issues. 2. Omniscript: There is no recognized macOS system component, file type, or standard technology known as "Omniscript" associated with.sfl files or the Launch Services framework [1][2][3][4]. It is possible this term is a misremembered name for an application, a third-party scripting tool, or perhaps a confusion with the term "OpenScript" or a similar utility. The.sfl files themselves are property lists (plists) that contain Base-64 encoded "Bookmarks"—a specialized macOS data structure used to track file locations even if they are moved or renamed on the same volume [2][3][7][4]. They do not contain plain-text file paths [2][3]. If you are encountering a file with this name or suspect it is affecting your recent documents, it is likely a third-party tool or an incorrectly named file, as it does not align with native macOS naming conventions for Launch Services data [1][5].

Citations:


🌐 Web query:

"~/.Trash" "ApplicationRecentDocuments" ".sfl2" "OmniOutliner"

💡 Result:

The file path you mentioned, ~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/, is the standard directory where macOS stores Shared File List (SFL) files that track recently opened documents for individual applications [1][2][3]. The files within this folder typically have an.sfl2 extension (or occasionally.sfl in older macOS versions) [1][3][4]. Regarding OmniOutliner, a file named with the application's bundle identifier followed by.sfl2 in that directory contains the list of documents recently opened by OmniOutliner [2][3]. Key characteristics of these files include: 1. Function: They are property list files managed by the macOS system service sharedfilelistd [1][2]. They enable the "Open Recent" functionality found in most macOS applications [1][5]. 2. Content: These files do not contain plain-text file paths [1][6]. Instead, they store macOS "Bookmark" data, which are opaque, binary-encoded objects that resolve to file system paths [1][6][4]. 3. Management: The operating system updates these files automatically when an application is opened or when a document is accessed [2]. Manually editing or deleting them is generally unnecessary and can be handled by the system [2][5]. 4. Accessing Information: Because they contain binary-encoded Bookmarks, they cannot be read directly with a standard text editor [1][6]. To view the contents, you must use specialized tools or scripts (such as those using Swift's NSKeyedUnarchiver) capable of decoding the stored Bookmark data into usable file paths [1][7][6]. If you were looking at these files in the ~/.Trash folder, it suggests they were recently deleted from the Application Support directory [1][2]. You can restore them to their original location if you need to recover OmniOutliner's "Recent Documents" history, or simply leave them in the Trash to be permanently removed [2].

Citations:


🌐 Web query:

Google "OmniOutliner recent documents shared file list case-sensitive"

💡 Result:

OmniOutliner documentation and user resources do not indicate that the "Recent Documents" or shared file list functionality is case-sensitive [1][2][3]. The application's interface for managing files, such as the Resource Browser on macOS or the Document Browser on iOS, generally handles file indexing and searching based on system-level file system behaviors [1][2][4]. On Apple platforms (macOS, iOS, iPadOS), the underlying file systems (APFS/HFS+) are typically case-insensitive but case-preserving, which informs how applications interact with file paths and names [5]. While there have been historical reports from users regarding case-sensitive behavior in specific, older in-app filtering contexts (such as certain column-specific filters) [6], there is no evidence to suggest that the Recent Documents list itself employs case-sensitive matching for identifying or displaying files. If you are encountering issues where files are not appearing as expected, it is likely related to file path indexing or synchronization across locations (such as iCloud Drive or local storage) rather than case-sensitivity in the file list UI itself [2][3]. You can typically use the search field provided within the Resource Browser or Document Browser to filter through your recent documents, and this search is intended to assist in finding files regardless of exact case matching [1][2][7].

Citations:


Preserve the bundle identifier’s casing in the recent-document glob.

The shared recent-document file is named from the bundle ID, so use com.omnigroup.OmniOutliner6*.sfl* instead of the lowercase pattern to avoid failing on case-sensitive macOS volumes.

🤖 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/omnioutliner/darwin.json` at line 20, Update the
recent-document glob passed to trash in the OmniOutliner cleanup script to use
the correctly cased bundle identifier, com.omnigroup.OmniOutliner6*.sfl*. Leave
the surrounding cleanup paths and trash function unchanged.

"1b0bcecf": "#!/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/RightFont.app\"\nsudo rmdir '~/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.rightfontapp.rightfont*.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Logs/RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.rightfontapp.RightFont*.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.rightfontapp.RightFont*'\n",
"40969d32": "#!/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.rightfontapp.RightFont5'\nif [ -d \"$APPDIR/RightFont.app\" ]; then\n\tsudo mv \"$APPDIR/RightFont.app\" \"$TMPDIR/RightFont.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/RightFont.app\" \"$APPDIR\"\nrelaunch_application 'com.rightfontapp.RightFont5'\n"
"40969d32": "#!/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.rightfontapp.RightFont5'\nif [ -d \"$APPDIR/RightFont.app\" ]; then\n\tsudo mv \"$APPDIR/RightFont.app\" \"$TMPDIR/RightFont.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/RightFont.app\" \"$APPDIR\"\nrelaunch_application 'com.rightfontapp.RightFont5'\n",
"9714135f": "#!/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/RightFont.app\"\nsudo rmdir '~/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.rightfontapp.rightfont*.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.rightfontapp.RightFont5'\ntrash $LOGGED_IN_USER '~/Library/Logs/RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.rightfontapp.RightFont*.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.rightfontapp.RightFont*'\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:

#!/bin/bash
set -euo pipefail
file="ee/maintained-apps/outputs/rightfont/darwin.json"
echo "file exists: $(test -f "$file" && echo yes || echo no)"
echo "relevant diff/file snippets:"
if [ -f "$file" ]; then
  rg -n "sudo rmdir|APPDIR|LOGGED_IN_USER|trash\\(" "$file" -C 2 || true
  python3 - <<'PY'
import json
path="ee/maintained-apps/outputs/rightfont/darwin.json"
with open(path) as f: obj=json.load(f)
print("json type:", type(obj).__name__)
print("keys:", obj.keys() if isinstance(obj, dict) else list(obj)[:5])
text=str(obj) if isinstance(obj, (dict,list)) else obj
idx=text.index("sudo rmdir") if isinstance(text,str) else -1
print("text snippet around sudo rmdir:")
print(text[max(0,idx-200):idx+400] if idx!=-1 else "sudo rmdir not found in string form")
PY
fi
echo "shell-tilde-expansion snippets modeled via set -x simulation:"
sh -c 'APPDIR="/Applications/"; LOGGED_IN_USER="alice"; set -- "$(sudo rm -f /tmp/rightfont-audit 2>/dev/null; sudo rmdir '\''~/RightFont'\'' 2>/dev/null || true; sudo rmdir /Users/$LOGGED_IN_USER/RightFont 2>/dev/null || true; echo ok)" >&2'

Repository: fleetdm/fleet

Length of output: 7023


🌐 Web query:

Shell tilde expansion single quotes documentation

💡 Result:

In POSIX-compliant shells, including Bash, tilde expansion does not occur when the tilde () character is enclosed within single quotes [1][2][3]. Tilde expansion is strictly defined as an operation performed on an unquoted tilde-prefix [4][5]. According to the POSIX Shell Command Language specification, a tilde-prefix must be unquoted to be recognized and expanded by the shell [4][6]. When you wrap a string in single quotes, the shell preserves the literal value of every character inside those quotes, which explicitly prevents any expansion, including tilde expansion [1][2]. If you need to include the user's home directory in a path while also protecting the path from word splitting (for example, if the path contains spaces), you can quote only the portion of the path that contains spaces, leaving the tilde unquoted [7]. For example: - Incorrect (tilde is quoted): "/Documents/My File.txt" (The shell looks for a literal file named ~) - Correct (tilde is unquoted): ~/"Documents/My File.txt" - Correct (tilde is unquoted): ~/'Documents/My File.txt' Alternatively, you can use the $HOME environment variable, which can be expanded inside double quotes [3]. Using "$HOME/Documents/My File.txt" will correctly expand to the full path while preserving the space-containing filename [7][3].

Citations:


Use the logged-in user’s path for RightFont.

'~/RightFont' is passed literally because the tilde is inside single quotes, so /Users/<user>/RightFont can be missed during uninstall. Use the value already captured in LOGGED_IN_USER for cleanup.

Proposed fix
- sudo rmdir '~/RightFont'
+ sudo rmdir "/Users/$LOGGED_IN_USER/RightFont"
📝 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
"9714135f": "#!/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/RightFont.app\"\nsudo rmdir '~/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.rightfontapp.rightfont*.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.rightfontapp.RightFont5'\ntrash $LOGGED_IN_USER '~/Library/Logs/RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.rightfontapp.RightFont*.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.rightfontapp.RightFont*'\n"
"9714135f": "#!/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/RightFont.app\"\nsudo rmdir \"/Users/$LOGGED_IN_USER/RightFont\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.ApplicationRecentDocuments/com.rightfontapp.rightfont*.sfl*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/RightFont'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.rightfontapp.RightFont*'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.rightfontapp.RightFont5'\ntrash $LOGGED_IN_USER '~/Library/Logs/RightFont*'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.rightfontapp.RightFont*.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.rightfontapp.RightFont*'\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/rightfont/darwin.json` at line 20, Update the
RightFont cleanup command in the uninstall script to target
/Users/$LOGGED_IN_USER/RightFont instead of the single-quoted '~/RightFont'
literal. Preserve the existing rmdir behavior while using the already-captured
LOGGED_IN_USER value.

"refs": {
"71f9d405": "#!/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.teamviewer.TeamViewer'\nsudo installer -pkg \"$TMPDIR/TeamViewer.pkg\" -target /\nrelaunch_application 'com.teamviewer.TeamViewer'\n",
"d0cdc2d9": "#!/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 # 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\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.teamviewer.desktop'\nremove_launchctl_service 'com.teamviewer.Helper'\nremove_launchctl_service 'com.teamviewer.service'\nremove_launchctl_service 'com.teamviewer.teamviewer'\nremove_launchctl_service 'com.teamviewer.teamviewer_desktop'\nremove_launchctl_service 'com.teamviewer.teamviewer_service'\nremove_launchctl_service 'com.teamviewer.UninstallerHelper'\nremove_launchctl_service 'com.teamviewer.UninstallerWatcher'\nquit_application 'com.teamviewer.TeamViewer'\nquit_application 'com.teamviewer.TeamViewerUninstaller'\nremove_pkg_files 'com.teamviewer.AuthorizationPlugin'\nforget_pkg 'com.teamviewer.AuthorizationPlugin'\nremove_pkg_files 'com.teamviewer.AuthorizationResources'\nforget_pkg 'com.teamviewer.AuthorizationResources'\nremove_pkg_files 'com.teamviewer.remoteaudiodriver'\nforget_pkg 'com.teamviewer.remoteaudiodriver'\nremove_pkg_files 'com.teamviewer.teamviewer.*'\nforget_pkg 'com.teamviewer.teamviewer.*'\nremove_pkg_files 'TeamViewerUninstaller'\nforget_pkg 'TeamViewerUninstaller'\nsudo rm -rf '/Applications/TeamViewer.app'\nsudo rm -rf '/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Cookies/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.teamviewer.TeamViewer.savedState'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.teamviewer.TeamViewer'\n"
"f00ebab2": "#!/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 # 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\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.teamviewer.desktop'\nremove_launchctl_service 'com.teamviewer.Helper'\nremove_launchctl_service 'com.teamviewer.service'\nremove_launchctl_service 'com.teamviewer.teamviewer'\nremove_launchctl_service 'com.teamviewer.teamviewer_desktop'\nremove_launchctl_service 'com.teamviewer.teamviewer_service'\nremove_launchctl_service 'com.teamviewer.UninstallerHelper'\nremove_launchctl_service 'com.teamviewer.UninstallerWatcher'\nquit_application 'com.teamviewer.TeamViewer'\nquit_application 'com.teamviewer.TeamViewerUninstaller'\nremove_pkg_files 'com.teamviewer.AuthorizationPlugin'\nforget_pkg 'com.teamviewer.AuthorizationPlugin'\nremove_pkg_files 'com.teamviewer.AuthorizationResources'\nforget_pkg 'com.teamviewer.AuthorizationResources'\nremove_pkg_files 'com.teamviewer.remoteaudiodriver'\nforget_pkg 'com.teamviewer.remoteaudiodriver'\nremove_pkg_files 'com.teamviewer.teamviewer.*'\nforget_pkg 'com.teamviewer.teamviewer.*'\nremove_pkg_files 'TeamViewerUninstaller'\nforget_pkg 'TeamViewerUninstaller'\nsudo rm -rf '/Applications/TeamViewer.app'\nsudo rm -rf '/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '/Library/Application Support/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Application Support/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Cookies/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.teamviewer.TeamViewer.savedState'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.teamviewer.TeamViewer'\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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== target file =="
if [ -f ee/maintained-apps/outputs/teamviewer/darwin.json ]; then
  wc -l ee/maintained-apps/outputs/teamviewer/darwin.json
  sed -n '80,120p' ee/maintained-apps/outputs/teamviewer/darwin.json | cat -n
else
  fd -a 'darwin\.json$' ee/maintained-apps/outputs/teamviewer || true
fi

echo
echo "== matching removal lines in all maintained teamviewer outputs =="
rg -n "sudo rm -rf '.*/Preferences/com\.teamviewer|rm -rf '/Library/Preferences/com\.teamviewer|/Library/Preferences/com\.teamviewer" ee/maintained-apps/outputs/teamviewer || true

echo
echo "== shell expansion/argument behavior probe =="
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
mkdir -p "$tmpdir/dir with spaces"
touch "$tmpdir/dir with spaces/com.teamviewer.pref"
(cd "$tmpdir" && bash -c 'echo "quoted:"; for x in "/tmp/thisdirwithspaces/com.teamviewer*"; do echo "$x"; done' bash)
(cd "$tmpdir" && sh -c 'echo "unquoted:"; for x in /tmp/thisdirwithspaces/com.teamviewer*; do echo "$x"; done')
(cd "$tmpdir" && sh -c 'echo "single-quoted:"; for x in '\''/tmp/thisdirwithspaces/com.teamviewer*'\''; do echo "$x"; done')

Repository: fleetdm/fleet

Length of output: 10212


🌐 Web query:

Apple rm -rf remove preference files /Library/Preferences command no glob documentation

💡 Result:

To remove or manage preference files in /Library/Preferences on macOS, the recommended method is the built-in defaults command rather than direct file deletion with rm [1][2][3]. 1. Using the defaults command: The defaults command safely handles macOS preference domains and integrates with the cfprefsd caching service [1][2][4]. To delete a preference domain, use: sudo defaults delete For example, to delete a domain in /Library/Preferences: sudo defaults delete /Library/Preferences/com.apple.example.plist This approach is safer than using rm because it notifies the system of the change, which helps prevent cached preferences from persisting [2][5][4]. 2. Limitations of direct deletion with rm: The rm command is a standard Unix utility for removing files, not a specialized preference management tool [6]. It does not have a "no glob" mode; globbing (such as ) is handled by the shell (e.g., zsh or bash), not the command itself [7][8]. To prevent the shell from expanding wildcards, you must quote the path, but this prevents rm from identifying the files, as it will look for a literal filename containing the asterisk [7][9]. 3. Recommended alternative for mass deletion: If you must delete files using wildcards (e.g., to clear multiple.plist files), use the find command instead of rm directly [10][9]. The find command allows you to locate files based on patterns and execute rm safely [7]. Example: sudo find /Library/Preferences -name "com.apple.example..plist" -exec rm -v {} ; Always ensure you have properly quit the associated application and consider flushing the preference cache by running sudo killall cfprefsd after manual deletions to ensure the system recognizes the removal [11][5][4]. Direct manipulation of system library files carries risks; if you are not certain of the target, use backups [2][5].

Citations:


🌐 Web query:

site:apple.stackexchange.com sudo rm -rf /Library/Preferences/com.example* glob shell expansion

💡 Result:

When you run a command like sudo rm /Library/Preferences/com.example*, the shell performs path expansion (globbing) before the command is ever executed [1][2]. Here is how the process works: 1. Shell Expansion: The shell (such as zsh or bash) scans the directory /Library/Preferences/ for any files or directories that begin with com.example [1]. 2. Argument Substitution: The shell replaces the pattern /Library/Preferences/com.example* with a list of all matching file paths [1][2]. 3. Execution: The shell then executes the sudo rm command, passing that expanded list of files as individual arguments to rm [1]. Because the expansion happens before the command runs, the rm command itself never sees the wildcard character (); it only receives the specific list of files that the shell found [2]. Important Considerations: - Zsh Behavior: By default, if the zsh shell (the default on modern macOS) cannot find any files matching the pattern, it will throw an error (no matches found) and refuse to execute the command at all [3][4]. - Bash Behavior: In older shells or configurations (like bash), if no match is found, the shell may pass the literal string (e.g., /Library/Preferences/com.example) to the command, which will cause rm to report an error because the file literally named com.example* does not exist [4]. - Safety Tip: To safely verify what will be deleted, always run the command with ls first to see the list of files that will be passed to rm [1][5][6]: ls /Library/Preferences/com.example* - Risk: Using sudo rm with wildcards is inherently risky, as a typo or unexpected expansion can lead to the accidental deletion of unintended files [6]. Always ensure you are targeting the exact directory and files you intend to remove [7].

Citations:


Do not quote the TeamViewer system preference glob.

sudo sh -c 'rm -rf /Library/Preferences/com.teamviewer*' expands the wildcard in the shell before rm receives the matching files, so these preference removals actually run instead of rm waiting for a literal file named com.teamviewer*.

Proposed fix
- sudo rm -rf '/Library/Preferences/com.teamviewer*'
+ sudo sh -c 'rm -rf /Library/Preferences/com.teamviewer*'
📝 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
"f00ebab2": "#!/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 # 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\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.teamviewer.desktop'\nremove_launchctl_service 'com.teamviewer.Helper'\nremove_launchctl_service 'com.teamviewer.service'\nremove_launchctl_service 'com.teamviewer.teamviewer'\nremove_launchctl_service 'com.teamviewer.teamviewer_desktop'\nremove_launchctl_service 'com.teamviewer.teamviewer_service'\nremove_launchctl_service 'com.teamviewer.UninstallerHelper'\nremove_launchctl_service 'com.teamviewer.UninstallerWatcher'\nquit_application 'com.teamviewer.TeamViewer'\nquit_application 'com.teamviewer.TeamViewerUninstaller'\nremove_pkg_files 'com.teamviewer.AuthorizationPlugin'\nforget_pkg 'com.teamviewer.AuthorizationPlugin'\nremove_pkg_files 'com.teamviewer.AuthorizationResources'\nforget_pkg 'com.teamviewer.AuthorizationResources'\nremove_pkg_files 'com.teamviewer.remoteaudiodriver'\nforget_pkg 'com.teamviewer.remoteaudiodriver'\nremove_pkg_files 'com.teamviewer.teamviewer.*'\nforget_pkg 'com.teamviewer.teamviewer.*'\nremove_pkg_files 'TeamViewerUninstaller'\nforget_pkg 'TeamViewerUninstaller'\nsudo rm -rf '/Applications/TeamViewer.app'\nsudo rm -rf '/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '/Library/Application Support/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Application Support/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Caches/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Cookies/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.teamviewer.TeamViewer.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Logs/TeamViewer'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.teamviewer*'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.teamviewer.TeamViewer.savedState'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.teamviewer.TeamViewer'\n"
sudo sh -c 'rm -rf /Library/Preferences/com.teamviewer*'
🤖 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/teamviewer/darwin.json` at line 20, Update the
TeamViewer system-preference removal command near the cleanup calls so the
com.teamviewer* wildcard is expanded by a shell running with sudo rather than
passed as a quoted literal to rm. Preserve removal of all matching entries under
/Library/Preferences, and keep the existing cleanup scope unchanged.

"18d6be1e": "#!/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\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 'com.tencent.xinWeChat'\nsudo rm -rf \"$APPDIR/WeChat.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat.IPCHelper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.MiniProgram'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.WeChatMacShare'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat.IPCHelper'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat.MiniProgram'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat.WeChatMacShare'\ntrash $LOGGED_IN_USER '~/Library/Cookies/com.tencent.xinWeChat.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.tencent.xinWeChat.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.tencent.xinWeChat.savedState'\n",
"57f30b74": "#!/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\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.tencent.xinWeChat'\nif [ -d \"$APPDIR/WeChat.app\" ]; then\n\tsudo mv \"$APPDIR/WeChat.app\" \"$TMPDIR/WeChat.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/WeChat.app\" \"$APPDIR\"\nrelaunch_application 'com.tencent.xinWeChat'\n"
"57f30b74": "#!/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\nMOUNT_POINT=$(mktemp -d /tmp/dmg_mount_XXXXXX)\nyes | hdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\" || exit 1\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\" || true\n# copy to the applications folder\nquit_and_track_application 'com.tencent.xinWeChat'\nif [ -d \"$APPDIR/WeChat.app\" ]; then\n\tsudo mv \"$APPDIR/WeChat.app\" \"$TMPDIR/WeChat.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/WeChat.app\" \"$APPDIR\"\nrelaunch_application 'com.tencent.xinWeChat'\n",
"a5e852b0": "#!/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\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 'com.tencent.xinWeChat'\nsudo rm -rf \"$APPDIR/WeChat.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat.IPCHelper'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/5A4RE8SF68.com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.MiniProgram'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.tencent.xinWeChat.WeChatMacShare'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat.IPCHelper'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat.MiniProgram'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.tencent.xinWeChat.WeChatMacShare'\ntrash $LOGGED_IN_USER '~/Library/Cookies/com.tencent.xinWeChat.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Group Containers/5A4RE8SF68.com.tencent.xinWeChat'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.tencent.xinWeChat.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/com.tencent.xinWeChat.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.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt =="
cat -n ee/maintained-apps/outputs/wechat/darwin.json | sed -n '1,80p'

echo
echo "== parse JSON output key =="
python3 - <<'PY'
import json
from pathlib import Path
p = Path("ee/maintained-apps/outputs/wechat/darwin.json")
data = json.loads(p.read_text())
key, val = next(iter(data.items()))
print("key:", key)
print("lines:", len(val.splitlines()))
for i, line in enumerate(val.splitlines(), 1):
    if "TeamIdentifierPrefix" in line or "trash" in line or "Application Scripts" in line or "Group Containers" in line:
        print(f"{i}: {line}")
PY

echo
echo "== bash substitution behavior for quoted paths in trash function =="
set +e
bash --noprofile --norc -x <<'BASH'
trash() {
  local logged_in_user="$1"
  local target_file="$2"
  echo "trash called with target_file=$target_file"
}
LOGGED_IN_USER=user
trash user '~/Library/Application Scripts/$(TeamIdentifierPrefix)com.tencent.xinWeChat'
trap - EXIT
BASH

Repository: fleetdm/fleet

Length of output: 9358


🌐 Web query:

WeChat macOS TeamIdentifierPrefix application scripts containers group containers uninstall script

💡 Result:

WeChat for macOS utilizes the Apple App Sandbox architecture, which separates the application bundle from its user data containers [1]. Because these data containers are persistent and not removed when the application itself is deleted, a standard drag-and-drop uninstallation leaves significant data on the system [1][2]. The TeamIdentifierPrefix variable is a macOS placeholder used in paths for sandboxed apps and app groups [3][4]. When specific to WeChat (Bundle ID: com.tencent.xinWeChat), paths often include this prefix to ensure the sandbox container is uniquely tied to the developer's Team ID [3][5]. Below are the key locations where WeChat data resides and what should be targeted for a complete manual uninstallation [3]. Core Data and Sandbox Locations: 1. Application Sandbox: ~/Library/Containers/com.tencent.xinWeChat/ 2. Application Scripts: ~/Library/Application Scripts/com.tencent.xinWeChat/ 3. Group Containers: ~/Library/Group Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat/ 4. Caches: ~/Library/Caches/com.tencent.xinWeChat/ 5. Preferences: ~/Library/Preferences/com.tencent.xinWeChat.plist 6. Saved State: ~/Library/Saved Application State/com.tencent.xinWeChat.savedState/ Comprehensive Uninstall Procedure: To perform a complete removal of WeChat and all its associated data, follow these steps [3][6][2]: 1. Quit WeChat: Ensure the application is completely closed (check the Menu Bar and Dock) [6][7]. 2. Move Application to Trash: Navigate to the Applications folder in Finder and move WeChat.app to the Trash [6][8]. 3. Remove Sandbox and Support Files: Open Finder, press Command+Shift+G, and navigate to the ~/Library folder. Locate and delete the following directories and files if they exist [3][2]: - ~/Library/Containers/com.tencent.xinWeChat/ - ~/Library/Application Scripts/com.tencent.xinWeChat/ - ~/Library/Group Containers/$(TeamIdentifierPrefix)com.tencent.xinWeChat/ (Note: Replace $(TeamIdentifierPrefix) with the actual Team ID folder name if it appears prefixed) - ~/Library/Caches/com.tencent.xinWeChat/ - ~/Library/Preferences/com.tencent.xinWeChat.plist - ~/Library/Saved Application State/com.tencent.xinWeChat.savedState/ 4. Empty Trash: Securely empty your Trash to finalize the removal [6][8]. Important Note: The ~/Library/Containers/com.tencent.xinWeChat/ directory contains your primary chat history, databases, and cached media [9][10]. If you wish to preserve your chat history for future use or migration, back up this directory before performing the deletion [11]. Replacing $(TeamIdentifierPrefix) in file paths is usually handled automatically by the macOS file system; when searching manually, you may see folders named with the actual Team ID string (e.g., a string of alphanumeric characters) followed by the bundle identifier [3][4].

Citations:


🌐 Web query:

macOS TeamIdentifierPrefix command $TeamIdentifierPrefix team identifier application group containers

💡 Result:

$(TeamIdentifierPrefix) is a build-time variable used in Xcode to represent your Apple Developer Team ID [1][2][3]. In the context of macOS application group containers, it is not a literal command-line tool, but rather a placeholder that resolves to your Team ID during the build process to satisfy code-signing entitlement requirements [1][2]. For macOS applications, App Group identifiers must typically be prefixed with your Team ID to ensure uniqueness and security [1][4]. When configuring your App Groups in Xcode, using $(TeamIdentifierPrefix) followed by your chosen group name (e.g., $(TeamIdentifierPrefix)com.example.appname) ensures the correct entitlement value is injected into your app's signature [3][5][4]. Key points regarding its use: 1. Application Group Containers: macOS enforces that application group identifiers start with your Team ID (e.g., ABC1234567.com.example.app) [1][3][4]. If your App Group identifier does not start with your Team ID, the system may prevent access to the shared container, or your submission to the Mac App Store may be rejected with an invalid entitlement error [1][3][5][4]. 2. Resolution: While Xcode suggests using $(TeamIdentifierPrefix) in the Capabilities/Signing & Capabilities tab, developers sometimes experience issues where the variable does not resolve correctly during archiving [1]. In such cases, explicitly hardcoding your 10-character Team ID (found in your Apple Developer account) in place of the variable is a reliable alternative [3]. 3. Runtime Access: When accessing the shared container in your code using NSFileManager, you must provide the full, resolved string. For example, use [fileManager containerURLForSecurityApplicationGroupIdentifier:@"ABC1234567.com.example.app"] rather than using the variable itself, as the string passed to this method must be the final identifier recognized by the system [6][7]. 4. Mac Catalyst Exception: Note that Mac Catalyst apps may follow different rules, often behaving more like iOS apps and using the group. prefix, depending on the macOS version and deployment target [4]. In summary, $(TeamIdentifierPrefix) is a convenience variable for Xcode project settings, not a terminal command. Always ensure your final entitlement configuration uses your actual Team ID to avoid sandbox and code-signing validation failures [1][3][4].

Citations:


🌐 Web query:

site:github.com/Homebrew/homebrew-cask "TeamIdentifierPrefix" "Application Scripts" "Group Containers"

💡 Result:

In the context of macOS sandboxing and Homebrew Cask configurations, TeamIdentifierPrefix refers to the Apple-assigned Team ID prefix required for paths in the ~/Library/Containers and ~/Library/Group Containers directories [1][2]. When a macOS application is sandboxed, it is often assigned a container directory whose name includes its Team ID followed by its bundle identifier (e.g., TEAMID.com.example.app) [1]. Application Scripts for sandboxed apps are similarly located in ~/Library/Application Scripts/TEAMID.com.example.app [1][2]. The $(TeamIdentifierPrefix) notation used in Homebrew Cask files is a placeholder variable [1][2]. It allows the Cask's zap stanza—which handles the removal of leftover application files—to remain generic or adaptable, as the actual Team ID prefix is specific to the developer's signing certificate and can change or be complex to resolve programmatically during uninstallation [1][3]. Because Homebrew Cask does not automatically resolve this variable in all contexts, users or maintainers may sometimes encounter issues where these paths are not correctly cleaned up if the prefix is not manually replaced with the actual 10-character Team ID associated with the application's code-signing identity [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Homebrew Cask zap stanzas containing TeamIdentifierPrefix =="
python3 - <<'PY'
import urllib.request
urls = [
  "https://raw.githubusercontent.com/Homebrew/homebrew-cask/HEAD/Casks/w/wechat.rb",
  "https://raw.githubusercontent.com/Homebrew/homebrew-cask/a8bc8f7f56cbb62963d472c9d4793be0656e2c/Casks/w/wechat.rb",
]
for url in urls:
    print(f"\n-- {url} --")
    data = urllib.request.urlopen(url, timeout=20).read().decode()
    for i, line in enumerate(data.splitlines(), 1):
        if "TeamIdentifierPrefix" in line or "Application Scripts" in line or "Group Containers" in line or "def .zap" in line:
            # include surrounding zap block context
            lines = data.splitlines()
            start=max(1,i-8); end=min(len(lines), i+8)
            for j in range(start,end+1):
                print(f"{j:4}: {lines[j-1]}")
PY

echo
echo "== exact grep in codebase for TeamIdentifierPrefix =="
rg -n "TeamIdentifierPrefix|Application Scripts/\\\$|Group Containers/\\\$" . --glob '!vendor/**' --glob '!node_modules/**' || true

Repository: fleetdm/fleet

Length of output: 2588


🌐 Web query:

github Homebrew homebrew-cask Casks w wechat.rb Group Containers $TeamIdentifierPrefix

💡 Result:

In Homebrew Cask formulas, $(TeamIdentifierPrefix) is a placeholder variable used within the zap stanza to represent the Apple Developer Team ID prefix [1][2][3]. On macOS, applications that share data through App Groups often store files in folders within ~/Library/Group Containers/ and ~/Library/Containers/ [4][5]. These folders are frequently prefixed with the developer's unique Team ID (e.g., 243LU875E5.com.example.app) to ensure uniqueness and security [4][5]. Because this Team ID is specific to the developer who signed the application, Homebrew uses the $(TeamIdentifierPrefix) token in the zap stanza—which defines files to be deleted when a user runs brew uninstall --zap—to dynamically match and remove these container folders regardless of the specific Team ID associated with the installed version of the application [1][3]. The inclusion of this variable in the wechat.rb cask ensures that, upon uninstallation, Homebrew can correctly identify and clean up the various application-specific support folders, caches, and group containers associated with WeChat, even if the underlying folder names include the developer's mandatory Team ID prefix [1][2][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files matching wechat cask names =="
git ls-files | rg -i '(^|/)(wechat|maintained-apps|outputs/.*/.*\.json)$' || true

echo
echo "== repository-wide TeamIdentifierPrefix occurrences =="
git ls-files -z | xargs -0 rg -n "TeamIdentifierPrefix|Application Scripts/\\$\(|Group Containers/\\$\(|CONTAINERIDENTIFIER|SANDSTROM|APPID" 2>/dev/null || true

echo
echo "== nearby json output sections around wechat ref =="
python3 - <<'PY'
import json
from pathlib import Path
p=Path("ee/maintained-apps/outputs/wechat/darwin.json")
d=json.loads(p.read_text())
for section in d:
    print(f"{section}: type={type(d[section]).__name__}")
refs=d["refs"].get("a5e852b0","")
for i,line in enumerate(refs.splitlines(),1):
    if "trash" in line or "TeamIdentifierPrefix" in line or "Application Scripts" in line or "Group Containers" in line or "Containers" in line:
        print(f"{i}: {line}")
PY

Repository: fleetdm/fleet

Length of output: 50369


Resolve $(TeamIdentifierPrefix) before passing these paths to trash.

trash receives a literal argument when this value is single-quoted, so the prefixed Application Scripts, Containers, and Group Containers paths do not match the actual Team ID-based WeChat sandbox folders. Use a concrete prefix before removing these paths to avoid leaving WeChat data behind.

🤖 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/wechat/darwin.json` at line 20, Resolve the
TeamIdentifierPrefix value before invoking trash for the affected Application
Scripts, Containers, and Group Containers paths. Update the call sites around
the trash function so the paths contain the concrete prefix rather than the
literal $(TeamIdentifierPrefix) text, while preserving the existing cleanup
behavior.

@github-actions

Copy link
Copy Markdown
Contributor

Closing in favor of #50138.

@github-actions github-actions Bot closed this Jul 29, 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