add pre and post install script fields to FMAv2 input schema (#27214)

> No issue, follow up work for FMAv2 ingestion

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

<!-- Note that API documentation changes are now addressed by the
product design team. -->

- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Ian Littman <iansltx@gmail.com>
This commit is contained in:
Jahziel Villasana-Espinoza
2025-03-17 19:28:50 -04:00
committed by GitHub
co-authored by Ian Littman
parent bb3fd2b83b
commit 62044bdb3f
6 changed files with 53 additions and 18 deletions
+23 -9
View File
@@ -5,16 +5,24 @@
1. Decide on a source for the app's metadata. We currently support homebrew as a source for macOS apps.
2. Find that app's metadata. For homebrew, you can visit https://formulae.brew.sh/ and find the app there.
3. Create a new file called `$YOUR_APP_NAME.json` in the `inputs/$SOURCE` directory. For
example, if you wanted to add Slack and use homebrew as the source, you would create the
file `inputs/homebrew/slack.json`.
4. Fill out the file according to the [breakdown below](#input-file-schema). For our example Slack app, it would look like this:
example, if you wanted to add Box Drive and use homebrew as the source, you would create the
file `inputs/homebrew/box-drive.json`.
4. Fill out the file according to the [breakdown below](#input-file-schema). For our example Box Drive app, it would look like this:
```json
{
"name": "Slack",
"unique_identifier": "com.tinyspeck.slackmacgap",
"token": "slack",
"installer_format": "dmg",
"slug": "slack/darwin"
"name": "Box Drive",
"slug": "box-drive/darwin",
"unique_identifier": "com.box.desktop",
"token": "box-drive",
"installer_format": "pkg",
"pre_uninstall_scripts": [
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER fileproviderctl domain remove -A com.box.desktop.boxfileprovider)",
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-archive-unsynced-content Box)",
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-preserve-unsynced-content Box)",
"(cd /Users/$LOGGED_IN_USER; defaults delete com.box.desktop)",
"echo \"${LOGGED_IN_USER} ALL = (root) NOPASSWD: /Library/Application\\ Support/Box/uninstall_box_drive_r\" >> /etc/sudoers.d/box_uninstall"
],
"post_uninstall_scripts": ["rm /etc/sudoers.d/box_uninstall"]
}
```
5. Open a PR to the `fleet` repository with the new app file. This will trigger a CI job which will automatically update your PR with the required output files. These files contain important data such as the install and uninstall scripts for the app.
@@ -51,4 +59,10 @@ For the app name part, use `-` to separate words if necessary, for example `adob
The platform part can be any of these values:
- `darwin`
For example, use a `slug` of `slack/darwin` for Slack on macOS.
For example, use a `slug` of `box-drive/darwin` for Box Drive on macOS.
#### `pre_uninstall_scripts`
These are command lines that will be run _before_ the generated uninstall script is executed.
#### `post_uninstall_scripts`
These are command lines that will be run _after_ the generated uninstall script is executed.
@@ -144,6 +144,14 @@ func (i *brewIngester) ingestOne(ctx context.Context, app inputApp) (*maintained
out.SHA256 = cask.SHA256
out.Queries = maintained_apps.FMAQueries{Exists: fmt.Sprintf("SELECT 1 FROM apps WHERE bundle_identifier = '%s';", out.UniqueIdentifier)}
out.Slug = app.Slug
if len(app.PreUninstallScripts) != 0 {
cask.PreUninstallScripts = app.PreUninstallScripts
}
if len(app.PostUninstallScripts) != 0 {
cask.PostUninstallScripts = app.PostUninstallScripts
}
out.UninstallScript = uninstallScriptForApp(&cask)
installScript, err := installScriptForApp(app, &cask)
if err != nil {
@@ -166,8 +174,10 @@ type inputApp struct {
UniqueIdentifier string `json:"unique_identifier"`
// InstallerFormat is the installer format used for installing this app.
InstallerFormat string `json:"installer_format"`
Slug string `json:"slug"`
// Slug is an identifier that combines the app's token and the target OS.
Slug string `json:"slug"`
PreUninstallScripts []string `json:"pre_uninstall_scripts"`
PostUninstallScripts []string `json:"post_uninstall_scripts"`
}
type brewCask struct {
@@ -3,5 +3,13 @@
"slug": "box-drive/darwin",
"unique_identifier": "com.box.desktop",
"token": "box-drive",
"installer_format": "pkg"
"installer_format": "pkg",
"pre_uninstall_scripts": [
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER fileproviderctl domain remove -A com.box.desktop.boxfileprovider)",
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-archive-unsynced-content Box)",
"(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-preserve-unsynced-content Box)",
"(cd /Users/$LOGGED_IN_USER; defaults delete com.box.desktop)",
"echo \"${LOGGED_IN_USER} ALL = (root) NOPASSWD: /Library/Application\\ Support/Box/uninstall_box_drive_r\" >> /etc/sudoers.d/box_uninstall"
],
"post_uninstall_scripts": ["rm /etc/sudoers.d/box_uninstall"]
}
@@ -3,5 +3,8 @@
"slug": "cloudflare-warp/darwin",
"unique_identifier": "com.cloudflare.1dot1dot1dot1.macos",
"token": "cloudflare-warp",
"installer_format": "pkg"
"installer_format": "pkg",
"post_uninstall_scripts": [
"/Applications/Cloudflare\\ WARP.app/Contents/Resources/uninstall.sh"
]
}
@@ -8,12 +8,12 @@
"installer_url": "https://e3.boxcdn.net/desktop/releases/mac/BoxDrive-2.43.205.pkg",
"unique_identifier": "com.box.desktop",
"install_script_ref": "31e1a311",
"uninstall_script_ref": "14b3598b",
"uninstall_script_ref": "d9e98295",
"sha256": "200c5fa6d8eec5516924938d46e7fa6122fdc189044fd935e8c155faa5beaf3d"
}
],
"refs": {
"14b3598b": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil \u003c\u003c\u003c \"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 if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2\u003e/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 \u0026\u0026 \"$console_user\" == \"root\" ]]; 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 \u003c timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" \u003e/dev/null 2\u003e\u00261; then\n if ! pgrep -f \"$bundle_id\" \u003e/dev/null 2\u003e\u00261; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2\u003e/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n 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.box.desktop.helper'\nquit_application 'com.box.Box-Local-Com-Server'\nquit_application 'com.box.desktop'\nquit_application 'com.box.desktop.findersyncext'\nquit_application 'com.box.desktop.helper'\nquit_application 'com.box.desktop.ui'\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '/Library/Application Support/Box/uninstall_box_drive')\nsudo pkgutil --forget 'com.box.desktop.installer.*'\ntrash $LOGGED_IN_USER '~/.Box_*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Box/Box'\ntrash $LOGGED_IN_USER '~/Library/Application Support/FileProvider/com.box.desktop.boxfileprovider'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.box.desktop.findersyncext'\ntrash $LOGGED_IN_USER '~/Library/Logs/Box/Box'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.box.desktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.box.desktop.ui.plist'\n",
"31e1a311": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n\n# install pkg files\nsudo installer -pkg \"$TMPDIR/BoxDrive-2.43.205.pkg\" -target /\n"
"31e1a311": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n\n# install pkg files\nsudo installer -pkg \"$TMPDIR/BoxDrive-2.43.205.pkg\" -target /\n",
"d9e98295": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil \u003c\u003c\u003c \"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 if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2\u003e/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 \u0026\u0026 \"$console_user\" == \"root\" ]]; 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 \u003c timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" \u003e/dev/null 2\u003e\u00261; then\n if ! pgrep -f \"$bundle_id\" \u003e/dev/null 2\u003e\u00261; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2\u003e/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n 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\n(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER fileproviderctl domain remove -A com.box.desktop.boxfileprovider)\n(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-archive-unsynced-content Box)\n(cd /Users/$LOGGED_IN_USER; sudo -u $LOGGED_IN_USER /Applications/Box.app/Contents/MacOS/fpe/streem --remove-fpe-domain-and-preserve-unsynced-content Box)\n(cd /Users/$LOGGED_IN_USER; defaults delete com.box.desktop)\necho \"${LOGGED_IN_USER} ALL = (root) NOPASSWD: /Library/Application\\ Support/Box/uninstall_box_drive_r\" \u003e\u003e /etc/sudoers.d/box_uninstall\nremove_launchctl_service 'com.box.desktop.helper'\nquit_application 'com.box.Box-Local-Com-Server'\nquit_application 'com.box.desktop'\nquit_application 'com.box.desktop.findersyncext'\nquit_application 'com.box.desktop.helper'\nquit_application 'com.box.desktop.ui'\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '/Library/Application Support/Box/uninstall_box_drive')\nsudo pkgutil --forget 'com.box.desktop.installer.*'\nrm /etc/sudoers.d/box_uninstall\ntrash $LOGGED_IN_USER '~/.Box_*'\ntrash $LOGGED_IN_USER '~/Library/Application Support/Box/Box'\ntrash $LOGGED_IN_USER '~/Library/Application Support/FileProvider/com.box.desktop.boxfileprovider'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.box.desktop.findersyncext'\ntrash $LOGGED_IN_USER '~/Library/Logs/Box/Box'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.box.desktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.box.desktop.ui.plist'\n"
}
}
@@ -8,12 +8,12 @@
"installer_url": "https://downloads.cloudflareclient.com/v1/download/macos/version/2025.1.861.0",
"unique_identifier": "com.cloudflare.1dot1dot1dot1.macos",
"install_script_ref": "efb6e303",
"uninstall_script_ref": "2be21918",
"uninstall_script_ref": "b75a2692",
"sha256": "6ecdd09f2cb0b36f16be8272bf77db487dfbce98243b5445491b6fd96e35914d"
}
],
"refs": {
"2be21918": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil \u003c\u003c\u003c \"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 if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2\u003e/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 \u0026\u0026 \"$console_user\" == \"root\" ]]; 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 \u003c timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" \u003e/dev/null 2\u003e\u00261; then\n if ! pgrep -f \"$bundle_id\" \u003e/dev/null 2\u003e\u00261; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2\u003e/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.warp.daemon'\nquit_application 'com.cloudflare.1dot1dot1dot1.macos'\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '/Applications/Cloudflare WARP.app/Contents/Resources/uninstall.sh')\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '%!s(bool=true)')\nsudo pkgutil --forget 'com.cloudflare.1dot1dot1dot1.macos'\nsudo rm -rf '/usr/local/bin/warp-cli'\nsudo rm -rf '/usr/local/bin/warp-dex'\nsudo rm -rf '/usr/local/bin/warp-diag'\ntrash $LOGGED_IN_USER '/Library/LaunchDaemons/com.cloudflare.1dot1dot1dot1.macos.warp.daemon.plist'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.cloudflare.1dot1dot1dot1.macos.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.cloudflare.1dot1dot1dot1.macos'\n",
"b75a2692": "#!/bin/sh\n\n# variables\nLOGGED_IN_USER=$(scutil \u003c\u003c\u003c \"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 if ! osascript -e \"application id \\\"$bundle_id\\\" is running\" 2\u003e/dev/null; then\n return\n fi\n\n local console_user\n console_user=$(stat -f \"%Su\" /dev/console)\n if [[ $EUID -eq 0 \u0026\u0026 \"$console_user\" == \"root\" ]]; 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 \u003c timeout_duration )); do\n if osascript -e \"tell application id \\\"$bundle_id\\\" to quit\" \u003e/dev/null 2\u003e\u00261; then\n if ! pgrep -f \"$bundle_id\" \u003e/dev/null 2\u003e\u00261; then\n echo \"Application '$bundle_id' quit successfully.\"\n quit_success=true\n break\n fi\n fi\n sleep 1\n done\n\n if [[ \"$quit_success\" = false ]]; then\n echo \"Application '$bundle_id' did not quit.\"\n fi\n}\n\n\nremove_launchctl_service() {\n local service=\"$1\"\n local booleans=(\"true\" \"false\")\n local plist_status\n local paths\n local should_sudo\n\n echo \"Removing launchctl service ${service}\"\n\n for should_sudo in \"${booleans[@]}\"; do\n plist_status=$(launchctl list \"${service}\" 2\u003e/dev/null)\n\n if [[ $plist_status == \\{* ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo launchctl remove \"${service}\"\n else\n launchctl remove \"${service}\"\n fi\n sleep 1\n fi\n\n paths=(\n \"/Library/LaunchAgents/${service}.plist\"\n \"/Library/LaunchDaemons/${service}.plist\"\n )\n\n # if not using sudo, prepend the home directory to the paths\n if [[ $should_sudo == \"false\" ]]; then\n for i in \"${!paths[@]}\"; do\n paths[i]=\"${HOME}${paths[i]}\"\n done\n fi\n\n for path in \"${paths[@]}\"; do\n if [[ -e \"$path\" ]]; then\n if [[ $should_sudo == \"true\" ]]; then\n sudo rm -f -- \"$path\"\n else\n rm -f -- \"$path\"\n fi\n fi\n done\n done\n}\n\ntrash() {\n local logged_in_user=\"$1\"\n local target_file=\"$2\"\n local timestamp=\"$(date +%Y-%m-%d-%s)\"\n local rand=\"$(jot -r 1 0 99999)\"\n\n # replace ~ with /Users/$logged_in_user\n if [[ \"$target_file\" == ~* ]]; then\n target_file=\"/Users/$logged_in_user${target_file:1}\"\n fi\n\n local trash=\"/Users/$logged_in_user/.Trash\"\n local file_name=\"$(basename \"${target_file}\")\"\n\n if [[ -e \"$target_file\" ]]; then\n echo \"removing $target_file.\"\n mv -f \"$target_file\" \"$trash/${file_name}_${timestamp}_${rand}\"\n else\n echo \"$target_file doesn't exist.\"\n fi\n}\n\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\nremove_launchctl_service 'com.cloudflare.1dot1dot1dot1.macos.warp.daemon'\nquit_application 'com.cloudflare.1dot1dot1dot1.macos'\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '%!s(bool=true)')\n(cd /Users/$LOGGED_IN_USER \u0026\u0026 sudo -u \"$LOGGED_IN_USER\" '/Applications/Cloudflare WARP.app/Contents/Resources/uninstall.sh')\nsudo pkgutil --forget 'com.cloudflare.1dot1dot1dot1.macos'\nsudo rm -rf '/usr/local/bin/warp-cli'\nsudo rm -rf '/usr/local/bin/warp-dex'\nsudo rm -rf '/usr/local/bin/warp-diag'\n/Applications/Cloudflare\\ WARP.app/Contents/Resources/uninstall.sh\ntrash $LOGGED_IN_USER '/Library/LaunchDaemons/com.cloudflare.1dot1dot1dot1.macos.warp.daemon.plist'\ntrash $LOGGED_IN_USER '~/Library/Application Scripts/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/Application Support/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Caches/com.plausiblelabs.crashreporter.data/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/Containers/com.cloudflare.1dot1dot1dot1.macos.loginlauncherapp'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos'\ntrash $LOGGED_IN_USER '~/Library/HTTPStorages/com.cloudflare.1dot1dot1dot1.macos.binarycookies'\ntrash $LOGGED_IN_USER '~/Library/Preferences/com.cloudflare.1dot1dot1dot1.macos.plist'\ntrash $LOGGED_IN_USER '~/Library/WebKit/com.cloudflare.1dot1dot1dot1.macos'\n",
"efb6e303": "#!/bin/sh\n\n# variables\nAPPDIR=\"/Applications/\"\nTMPDIR=$(dirname \"$(realpath $INSTALLER_PATH)\")\n\n# install pkg files\nsudo installer -pkg \"$TMPDIR/Cloudflare_WARP_2025.1.861.0.pkg\" -target /\n"
}
}