Skip to content

Improve macOS deployment script with proper error handling, logging, and relay config#599

Open
jordaneunson wants to merge 1 commit intorustdesk:masterfrom
jordaneunson:improve-macos-deployment-script
Open

Improve macOS deployment script with proper error handling, logging, and relay config#599
jordaneunson wants to merge 1 commit intorustdesk:masterfrom
jordaneunson:improve-macos-deployment-script

Conversation

@jordaneunson
Copy link
Copy Markdown

@jordaneunson jordaneunson commented Mar 30, 2026

Summary

Replaces the existing macOS Bash deployment script in the client deployment docs with a production-tested version used across managed Mac fleets.

What changed

The previous script had minimal error handling, used fixed sleeps, downloaded to the current working directory, and relied on a config string. This version addresses all of those issues:

Improvements

  • Timestamped logging to /tmp/rustdesk-install.log for troubleshooting
  • Proper error handling with exit codes and diagnostic output on failure
  • Download validation — verifies the download link was found and the file is not empty
  • Console user detection — uses launchctl asuser to run user-context operations correctly
  • Config directory wait loop — polls for the config directory to appear (user-owned) instead of a fixed sleep
  • Direct relay server TOML configuration — writes RustDesk2.toml directly with relay server, key, and options (no config string needed)
  • LaunchAgent fallback — for DEP/MDM enrollments where no user is logged in at install time, creates a LaunchAgent that runs the config script on next login
  • Server/client mode toggle — configurable headless (server) or GUI (client) launch mode
  • DMG cleanup — removes the downloaded DMG after installation
  • Downloads to /tmp — instead of the current working directory

Configuration

Users set three variables at the top of the script:

  • RELAY_SERVER — their RustDesk relay server address
  • RELAY_KEY — their relay key
  • MSP — organization name (used for LaunchAgent labeling)

This approach is more explicit and less error-prone than the config string method, especially for MSPs managing multiple relay servers.

Summary by CodeRabbit

Documentation

  • Significantly expanded macOS self-hosting deployment documentation with configurable relay server parameters.
  • Added timestamped logging for improved troubleshooting and monitoring.
  • Enhanced validation and error handling throughout the deployment process.
  • Introduced enterprise deployment support including automated configuration upon first user login and managed device scenarios.

…and relay config

Replace the existing macOS Bash deployment script with a production-tested version that adds:

- Timestamped logging to /tmp/rustdesk-install.log
- Proper error handling with exit codes and diagnostics
- Download validation (link found, file not empty)
- Console user detection and user-context execution via launchctl
- Config directory initialization wait loop (vs fixed sleep)
- Direct relay server TOML configuration (no config string needed)
- LaunchAgent fallback for DEP/MDM enrollments with no logged-in user
- Server/client mode toggle
- Clean DMG cleanup on completion
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

The macOS Bash deployment documentation was significantly expanded to detail a complete RustDesk installation and configuration lifecycle. The script now includes configurable relay server parameters, comprehensive logging, DMG validation, console user detection, and fallback LaunchAgent creation for headless deployment scenarios.

Changes

Cohort / File(s) Summary
macOS Bash Deployment Documentation
content/self-host/client-deployment/_index.en.md
Expanded macOS deployment script from minimal template to full lifecycle installer with relay server configuration, timestamped logging, DMG validation, user-context setup, and DEP/MDM-compatible LaunchAgent fallback for unconfigured login scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A script hops forth with purpose true,
Configuring RustDesk, old made new—
With relays, users, and logs so bright,
macOS deploys from dawn to night!
From GUI to server, the rabbit's delight! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve macOS deployment script with proper error handling, logging, and relay config' directly and accurately summarizes the main changes: error handling, logging, and relay configuration improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@content/self-host/client-deployment/_index.en.md`:
- Around line 334-337: The script currently aborts when CONSOLE_USER is missing
(the block checking CONSOLE_USER and writing to LOG), which prevents the
LaunchAgent fallback for DEP/MDM no-user installs; instead of calling exit 1 in
that conditional, log the error to LOG and set a marker (e.g., export
NO_CONSOLE=true or leave CONSOLE_USER empty) so execution continues into the
LaunchAgent fallback flow; update the conditional around CONSOLE_USER (the block
that echoes to LOG) to not exit, and ensure later code that configures the
RustDesk directory/LaunchAgent checks NO_CONSOLE or an empty CONSOLE_USER to
take the fallback path.
- Around line 246-248: The log file is created world-writable via touch "$LOG"
and chmod 666 "$LOG" (and repeated in the other occurrences), and secrets
(RustDesk password and relay key) are being written to it; change the log
creation to set restrictive permissions (e.g., owner-only 600 or 640) and ensure
the file owner is the intended service account, and stop writing sensitive
values to the log — redact or omit the RustDesk password and relay key from any
logging statements (or store them in a secure vault/secure file with limited
access) and update all instances that use touch "$LOG"/chmod 666 "$LOG" to use
the secure permission pattern and remove secret output.
- Around line 320-321: The script currently assumes the copy succeeded by
running cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null and
immediately echoing a success line to $LOG; change this to check cp's exit
status (or use an if cp ...; then ... else ...) so failures are detected: run cp
without silencing stderr (or redirect stderr to the $LOG), then test the command
result and on success write the timestamped success message to $LOG, otherwise
write a timestamped error with the cp exit code and stderr and exit non‑zero to
stop the installer; update the lines referencing cp -R
"$mount_point/RustDesk.app" "/Applications/" and the subsequent echo to
implement this check.
- Around line 286-290: The grep -Eo regexes used when setting rd_link and
dmg_file (symbols: rd_link and dmg_file) use \d which is not POSIX ERE; update
both patterns for aarch64 and x86_64 to use [0-9]+ with escaped dots (e.g.,
replace occurrences like "\d{1}\.\d{1,2}\.\d{1,2}" and the full filename
patterns using \d with corresponding [0-9]+ quantifiers) so grep -Eo reliably
extracts version numbers and the rustdesk .dmg filenames on macOS/BSD and other
environments.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: ac6d5847-0fc1-4b6b-bc9f-db1317a043d6

📥 Commits

Reviewing files that changed from the base of the PR and between e99aa52 and 8873229.

📒 Files selected for processing (1)
  • content/self-host/client-deployment/_index.en.md

Comment on lines +246 to +248
# Create log file with world-writable permissions so the user-context subscript can write to it
touch "$LOG"
chmod 666 "$LOG"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Log file permissions and secret logging expose credentials.

World-writable /tmp/rustdesk-install.log plus logging of RustDesk password and relay key leaks sensitive data and allows tampering.

🔐 Suggested hardening
-touch "$LOG"
-chmod 666 "$LOG"
+touch "$LOG"
+chmod 600 "$LOG"
...
-echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk Password: $rustdesk_pw" | tee -a "$LOG"
+echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk Password: [REDACTED]" | tee -a "$LOG"
...
-echo "Relay Key: \$RELAY_KEY"
+echo "Relay Key: [REDACTED]"

Also applies to: 385-387, 466-468

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/self-host/client-deployment/_index.en.md` around lines 246 - 248, The
log file is created world-writable via touch "$LOG" and chmod 666 "$LOG" (and
repeated in the other occurrences), and secrets (RustDesk password and relay
key) are being written to it; change the log creation to set restrictive
permissions (e.g., owner-only 600 or 640) and ensure the file owner is the
intended service account, and stop writing sensitive values to the log — redact
or omit the RustDesk password and relay key from any logging statements (or
store them in a secure vault/secure file with limited access) and update all
instances that use touch "$LOG"/chmod 666 "$LOG" to use the secure permission
pattern and remove secret output.

Comment on lines 286 to +290
rd_link=$(curl -sL https://github.com/rustdesk/rustdesk/releases/latest | grep -Eo "(http|https)://[a-zA-Z0-9./?=_-]*/\d{1}.\d{1,2}.\d{1,2}/rustdesk.\d{1}.\d{1,2}.\d{1,2}.aarch64.dmg")
dmg_file=$(echo $rd_link | grep -Eo "rustdesk.\d{1}.\d{1,2}.\d{1,2}.aarch64.dmg")
curl -L "$rd_link" --output "$dmg_file"
dmg_file="/tmp/$(echo $rd_link | grep -Eo "rustdesk.\d{1}.\d{1,2}.\d{1,2}.aarch64.dmg")"
else
rd_link=$(curl -sL https://github.com/rustdesk/rustdesk/releases/latest | grep -Eo "(http|https)://[a-zA-Z0-9./?=_-]*/\d{1}.\d{1,2}.\d{1,2}/rustdesk.\d{1}.\d{1,2}.\d{1,2}.x86_64.dmg")
dmg_file=$(echo $rd_link | grep -Eo "rustdesk.\d{1}.\d{1,2}.\d{1,2}.x86_64.dmg")
curl -L "$rd_link" --output "$dmg_file"
dmg_file="/tmp/$(echo $rd_link | grep -Eo "rustdesk.\d{1}.\d{1,2}.\d{1,2}.x86_64.dmg")"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify regex behavior used in the script (read-only).
set -euo pipefail

sample='https://github.com/rustdesk/rustdesk/releases/download/1.4.0/rustdesk-1.4.0-aarch64.dmg'

echo "Test 1: current style with \\d and grep -E"
echo "$sample" | grep -Eo '\d{1}\.\d{1,2}\.\d{1,2}' || echo "NO_MATCH"

echo "Test 2: ERE-safe style"
echo "$sample" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' || echo "NO_MATCH"

Repository: rustdesk/doc.rustdesk.com

Length of output: 154


Replace \d with [0-9]+ for portable regex matching with grep -Eo.

The current pattern using \d is not valid POSIX ERE and fails to match version numbers on macOS/BSD (and some Linux configurations). The shell test confirms grep -Eo '\d{1}\.\d{1,2}\.\d{1,2}' returns NO_MATCH while grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' correctly extracts version strings. This causes silent extraction failures and installation aborts.

Replace at lines 286–290 and 294–297:

Affected locations Lines 286–290 (aarch64) and 294–297 (x86_64)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/self-host/client-deployment/_index.en.md` around lines 286 - 290, The
grep -Eo regexes used when setting rd_link and dmg_file (symbols: rd_link and
dmg_file) use \d which is not POSIX ERE; update both patterns for aarch64 and
x86_64 to use [0-9]+ with escaped dots (e.g., replace occurrences like
"\d{1}\.\d{1,2}\.\d{1,2}" and the full filename patterns using \d with
corresponding [0-9]+ quantifiers) so grep -Eo reliably extracts version numbers
and the rustdesk .dmg filenames on macOS/BSD and other environments.

Comment on lines 320 to +321
cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null
echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk.app copied to /Applications/" | tee -a "$LOG"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Copy success is assumed without checking return code.

cp -R ... &> /dev/null can fail silently, but success is logged immediately; this can mask install failures and cause downstream errors.

💡 Suggested fix
-    cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null
-    echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk.app copied to /Applications/" | tee -a "$LOG"
+    if cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null; then
+        echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk.app copied to /Applications/" | tee -a "$LOG"
+    else
+        echo "$(date '+%Y-%m-%d %H:%M:%S') | ERROR: Failed to copy RustDesk.app to /Applications." | tee -a "$LOG"
+        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.

Suggested change
cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null
echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk.app copied to /Applications/" | tee -a "$LOG"
if cp -R "$mount_point/RustDesk.app" "/Applications/" &> /dev/null; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | RustDesk.app copied to /Applications/" | tee -a "$LOG"
else
echo "$(date '+%Y-%m-%d %H:%M:%S') | ERROR: Failed to copy RustDesk.app to /Applications." | tee -a "$LOG"
exit 1
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/self-host/client-deployment/_index.en.md` around lines 320 - 321, The
script currently assumes the copy succeeded by running cp -R
"$mount_point/RustDesk.app" "/Applications/" &> /dev/null and immediately
echoing a success line to $LOG; change this to check cp's exit status (or use an
if cp ...; then ... else ...) so failures are detected: run cp without silencing
stderr (or redirect stderr to the $LOG), then test the command result and on
success write the timestamped success message to $LOG, otherwise write a
timestamped error with the cp exit code and stderr and exit non‑zero to stop the
installer; update the lines referencing cp -R "$mount_point/RustDesk.app"
"/Applications/" and the subsequent echo to implement this check.

Comment on lines +334 to +337
if [[ -z "$CONSOLE_USER" || "$CONSOLE_USER" == "loginwindow" || "$CONSOLE_USER" == "_mbsetupuser" ]]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | ERROR: No valid console user found. Cannot initialize RustDesk config directory." | tee -a "$LOG"
exit 1
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fallback path is currently unreachable for no-user installs.

exit 1 on Line 336 aborts before the LaunchAgent fallback path, so DEP/MDM “no logged-in user” installs never reach the intended recovery flow.

💡 Suggested fix
-if [[ -z "$CONSOLE_USER" || "$CONSOLE_USER" == "loginwindow" || "$CONSOLE_USER" == "_mbsetupuser" ]]; then
-    echo "$(date '+%Y-%m-%d %H:%M:%S') | ERROR: No valid console user found. Cannot initialize RustDesk config directory." | tee -a "$LOG"
-    exit 1
-fi
+if [[ -z "$CONSOLE_USER" || "$CONSOLE_USER" == "loginwindow" || "$CONSOLE_USER" == "_mbsetupuser" ]]; then
+    echo "$(date '+%Y-%m-%d %H:%M:%S') | No valid console user found. Deferring user-scoped configuration to LaunchAgent." | tee -a "$LOG"
+else
+    # existing user-initialization + wait-for-config flow
+fi

Also applies to: 497-516

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/self-host/client-deployment/_index.en.md` around lines 334 - 337, The
script currently aborts when CONSOLE_USER is missing (the block checking
CONSOLE_USER and writing to LOG), which prevents the LaunchAgent fallback for
DEP/MDM no-user installs; instead of calling exit 1 in that conditional, log the
error to LOG and set a marker (e.g., export NO_CONSOLE=true or leave
CONSOLE_USER empty) so execution continues into the LaunchAgent fallback flow;
update the conditional around CONSOLE_USER (the block that echoes to LOG) to not
exit, and ensure later code that configures the RustDesk directory/LaunchAgent
checks NO_CONSOLE or an empty CONSOLE_USER to take the fallback path.

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