Add Joplin as a macOS and Windows FMA - #46424
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #46424 +/- ##
==========================================
- Coverage 66.88% 66.88% -0.01%
==========================================
Files 2791 2792 +1
Lines 222293 222295 +2
Branches 11469 11334 -135
==========================================
+ Hits 148682 148683 +1
- Misses 60161 60163 +2
+ Partials 13450 13449 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
WalkthroughThis PR adds Joplin as a maintained app with cross-platform support. It introduces Homebrew input configuration and macOS/darwin deployment scripts for installation, Winget input configuration with PowerShell install/uninstall scripts for Windows, registers both platform variants in the app catalog, and adds a frontend icon component with UI integration. Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
frontend/pages/SoftwarePage/components/icons/Joplin.tsx (1)
5-13: ⚡ Quick winConsider converting the base64 PNG to SVG vector paths.
The current implementation embeds a base64-encoded PNG (~3.5KB) inside the SVG. This approach has drawbacks compared to native SVG paths:
- Not resolution-independent (may pixelate when scaled)
- Larger bundle size
- Cannot be styled/themed via CSS
If the Joplin logo can be traced to vector paths, it would improve scalability and reduce bundle size.
🤖 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 `@frontend/pages/SoftwarePage/components/icons/Joplin.tsx` around lines 5 - 13, The Joplin component currently embeds a base64 PNG via an <image> tag; replace that raster blob by importing or inlining the Joplin vector markup and swapping the <image> element for native SVG path/shape elements inside the Joplin function, keep the outer <svg ... {...props}> and width/height/viewBox behavior, optimize the vector with an SVG optimizer (SVGO) to reduce size, ensure colorable parts use currentColor or CSS-friendly attributes so the icon can be themed, and verify scaling and visual parity across typical sizes after the change.
🤖 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/joplin/darwin.json`:
- Line 20: The script currently deletes user profile data by calling trash
$LOGGED_IN_USER '~/Library/Application Support/Joplin' inside the trash() flow;
remove that destructive call and instead only remove the app bundle (sudo rm -rf
"$APPDIR/Joplin.app") and non-user-data artifacts like caches or helper prefs
(e.g., ~/Library/Caches/net.cozic.joplin-desktop and the prefs/helper plist
lines already present). If retaining an option to purge user data is required,
implement it behind an explicit opt-in flag or documented prompt rather than
unconditionally invoking trash on '~/Library/Application Support/Joplin'.
- Line 19: The install script currently moves existing APPDIR/Joplin.app to
TMPDIR/Joplin.app.bkp before verifying the new copy, risking leaving the app
missing on failure; update the flow in the mounting/copy block to: copy the new
Joplin.app from MOUNT_POINT to a TMPDIR staging path (e.g.,
TMPDIR/Joplin.app.new), verify the copy completed and is a valid .app (check
existence and a basic expected file inside), then atomically replace the live
app by renaming/moving the old APPDIR/Joplin.app to a backup name and moving
TMPDIR/Joplin.app.new into APPDIR (and if any step fails restore the backup into
APPDIR), ensure permissions are preserved and cleanup TMPDIR and MOUNT_POINT;
reference functions/vars quit_and_track_application, relaunch_application,
APPDIR, TMPDIR, MOUNT_POINT to locate where to implement these checks and
rollback behavior.
---
Nitpick comments:
In `@frontend/pages/SoftwarePage/components/icons/Joplin.tsx`:
- Around line 5-13: The Joplin component currently embeds a base64 PNG via an
<image> tag; replace that raster blob by importing or inlining the Joplin vector
markup and swapping the <image> element for native SVG path/shape elements
inside the Joplin function, keep the outer <svg ... {...props}> and
width/height/viewBox behavior, optimize the vector with an SVG optimizer (SVGO)
to reduce size, ensure colorable parts use currentColor or CSS-friendly
attributes so the icon can be themed, and verify scaling and visual parity
across typical sizes after the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d147ef89-d52e-4fa1-a3d7-b375e87eabde
⛔ Files ignored due to path filters (1)
website/assets/images/app-icon-joplin-60x60@2x.pngis excluded by!**/*.png
📒 Files selected for processing (9)
ee/maintained-apps/inputs/homebrew/joplin.jsonee/maintained-apps/inputs/winget/joplin.jsonee/maintained-apps/inputs/winget/scripts/joplin_install.ps1ee/maintained-apps/inputs/winget/scripts/joplin_uninstall.ps1ee/maintained-apps/outputs/apps.jsonee/maintained-apps/outputs/joplin/darwin.jsonee/maintained-apps/outputs/joplin/windows.jsonfrontend/pages/SoftwarePage/components/icons/Joplin.tsxfrontend/pages/SoftwarePage/components/icons/index.ts
| } | ||
| ], | ||
| "refs": { | ||
| "1e261b4b": "#!/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)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'net.cozic.joplin-desktop'\nif [ -d \"$APPDIR/Joplin.app\" ]; then\n\tsudo mv \"$APPDIR/Joplin.app\" \"$TMPDIR/Joplin.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Joplin.app\" \"$APPDIR\"\nrelaunch_application 'net.cozic.joplin-desktop'\n", |
There was a problem hiding this comment.
Make the app replacement rollback-safe.
The install script moves the live /Applications/Joplin.app to a backup before the new copy is proven good. If the DMG extraction or final copy fails, the host is left without Joplin and the backup is never restored.
Suggested hardening
-if [ -d "$APPDIR/Joplin.app" ]; then
- sudo mv "$APPDIR/Joplin.app" "$TMPDIR/Joplin.app.bkp"
-fi
-sudo cp -R "$TMPDIR/Joplin.app" "$APPDIR"
+sudo rm -rf "$TMPDIR/Joplin.app.new"
+sudo cp -R "$TMPDIR/Joplin.app" "$TMPDIR/Joplin.app.new"
+
+if [ -d "$APPDIR/Joplin.app" ]; then
+ sudo mv "$APPDIR/Joplin.app" "$TMPDIR/Joplin.app.bkp"
+fi
+
+if sudo mv "$TMPDIR/Joplin.app.new" "$APPDIR/Joplin.app"; then
+ sudo rm -rf "$TMPDIR/Joplin.app.bkp"
+elif [ -d "$TMPDIR/Joplin.app.bkp" ]; then
+ sudo mv "$TMPDIR/Joplin.app.bkp" "$APPDIR/Joplin.app"
+ exit 1
+fi📝 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.
| "1e261b4b": "#!/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)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'net.cozic.joplin-desktop'\nif [ -d \"$APPDIR/Joplin.app\" ]; then\n\tsudo mv \"$APPDIR/Joplin.app\" \"$TMPDIR/Joplin.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Joplin.app\" \"$APPDIR\"\nrelaunch_application 'net.cozic.joplin-desktop'\n", | |
| "1e261b4b": "#!/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)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'net.cozic.joplin-desktop'\nsudo rm -rf \"$TMPDIR/Joplin.app.new\"\nsudo cp -R \"$TMPDIR/Joplin.app\" \"$TMPDIR/Joplin.app.new\"\n\nif [ -d \"$APPDIR/Joplin.app\" ]; then\n\tsudo mv \"$APPDIR/Joplin.app\" \"$TMPDIR/Joplin.app.bkp\"\nfi\n\nif sudo mv \"$TMPDIR/Joplin.app.new\" \"$APPDIR/Joplin.app\"; then\n\tsudo rm -rf \"$TMPDIR/Joplin.app.bkp\"\nelif [ -d \"$TMPDIR/Joplin.app.bkp\" ]; then\n\tsudo mv \"$TMPDIR/Joplin.app.bkp\" \"$APPDIR/Joplin.app\"\n\texit 1\nfi\nrelaunch_application 'net.cozic.joplin-desktop'\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/joplin/darwin.json` at line 19, The install script
currently moves existing APPDIR/Joplin.app to TMPDIR/Joplin.app.bkp before
verifying the new copy, risking leaving the app missing on failure; update the
flow in the mounting/copy block to: copy the new Joplin.app from MOUNT_POINT to
a TMPDIR staging path (e.g., TMPDIR/Joplin.app.new), verify the copy completed
and is a valid .app (check existence and a basic expected file inside), then
atomically replace the live app by renaming/moving the old APPDIR/Joplin.app to
a backup name and moving TMPDIR/Joplin.app.new into APPDIR (and if any step
fails restore the backup into APPDIR), ensure permissions are preserved and
cleanup TMPDIR and MOUNT_POINT; reference functions/vars
quit_and_track_application, relaunch_application, APPDIR, TMPDIR, MOUNT_POINT to
locate where to implement these checks and rollback behavior.
| ], | ||
| "refs": { | ||
| "1e261b4b": "#!/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)\nhdiutil attach -plist -nobrowse -readonly -mountpoint \"$MOUNT_POINT\" \"$INSTALLER_PATH\"\nsudo cp -R \"$MOUNT_POINT\"/* \"$TMPDIR\"\nhdiutil detach \"$MOUNT_POINT\"\n# copy to the applications folder\nquit_and_track_application 'net.cozic.joplin-desktop'\nif [ -d \"$APPDIR/Joplin.app\" ]; then\n\tsudo mv \"$APPDIR/Joplin.app\" \"$TMPDIR/Joplin.app.bkp\"\nfi\nsudo cp -R \"$TMPDIR/Joplin.app\" \"$APPDIR\"\nrelaunch_application 'net.cozic.joplin-desktop'\n", | ||
| "48ef0ce5": "#!/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 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/Joplin.app\"\ntrash $LOGGED_IN_USER '~/Library/Application Support/Joplin'\ntrash $LOGGED_IN_USER '~/Library/Preferences/net.cozic.joplin-desktop.helper.plist'\ntrash $LOGGED_IN_USER '~/Library/Preferences/net.cozic.joplin-desktop.plist'\ntrash $LOGGED_IN_USER '~/Library/Saved Application State/net.cozic.joplin-desktop.savedState'\n" |
There was a problem hiding this comment.
Don't wipe the user's Joplin profile on uninstall.
Removing ~/Library/Application Support/Joplin makes uninstall destructive: that path is the user's app support/profile directory, not just disposable binaries. For a notes app, this risks deleting notebooks and local state during a normal uninstall flow.
🤖 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/joplin/darwin.json` at line 20, The script
currently deletes user profile data by calling trash $LOGGED_IN_USER
'~/Library/Application Support/Joplin' inside the trash() flow; remove that
destructive call and instead only remove the app bundle (sudo rm -rf
"$APPDIR/Joplin.app") and non-user-data artifacts like caches or helper prefs
(e.g., ~/Library/Caches/net.cozic.joplin-desktop and the prefs/helper plist
lines already present). If retaining an option to purge user data is required,
implement it behind an explicit opt-in flag or documented prompt rather than
unconditionally invoking trash on '~/Library/Application Support/Joplin'.
Related issue: Resolves #
Checklist for submitter
If some of the following don't apply, delete the relevant line.
Changes file added for user-visible changes in
changes/,orbit/changes/oree/fleetd-chrome/changes.See Changes files for more information.
Input data is properly validated,
SELECT *is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.Timeouts are implemented and retries are limited to avoid infinite loops
If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes
Testing
Added/updated automated tests
Where appropriate, automated tests simulate multiple hosts and test for host isolation (updates to one hosts's records do not affect another)
QA'd all new/changed functionality manually
For unreleased bug fixes in a release candidate, one of:
Database migrations
COLLATE utf8mb4_unicode_ci).New Fleet configuration settings
If you didn't check the box above, follow this checklist for GitOps-enabled settings:
fleetctl generate-gitopsfleetd/orbit/Fleet Desktop
runtime.GOOSis used as needed to isolate changesSummary by CodeRabbit