Files
rustdesk/ci/runners/macos/provision.sh
T
mike 47f0d0fff2
build-linux / build-linux-x64 (push) Successful in 5m23s
build-macos / build-macos-x64 (push) Successful in 9m4s
build-windows / build-windows-x64 (push) Successful in 10m13s
Implement CI workflow for Gitea. Include provision scripts for Gitea runners.
2026-05-07 09:39:23 +02:00

292 lines
11 KiB
Bash
Executable File

#!/usr/bin/env bash
# Provisions a macOS host (Apple Silicon, macOS 14+) as a Gitea Actions runner
# for RustDesk desktop (.dmg) builds. Idempotent: safe to re-run.
#
# Versions are pinned to .gitea/workflows/build-macos.yml. Bump them there and
# here together.
#
# Usage:
# sudo ./provision.sh \
# --gitea-url https://gitea.example.com \
# --runner-token <token>
#
# Toolchains land in /opt/* (chowned to the runner user). Service is installed
# as a LaunchDaemon running as that user.
set -euo pipefail
# ---- pinned versions (mirror .gitea/workflows/build-macos.yml env block) ----
RUST_VERSION="1.81.0" # MAC_RUST_VERSION upstream (cidre crate needs >=1.81)
FLUTTER_VERSION="3.24.5" # used for `flutter build macos`
FLUTTER_BRIDGE_VERSION="3.22.3" # used for `flutter pub get` + flutter_rust_bridge_codegen
VCPKG_COMMIT="120deac3062162151622ca4860575a33844ba10b"
NASM_VERSION="2.16.03" # 3.x has incompatible CLI; aom/dav1d need 2.x
RUNNER_VERSION="0.2.11"
# ---- defaults ----
RUNNER_NAME="$(hostname -s)-rustdesk"
RUNNER_LABELS=""
SERVICE_USER="gitea-runner"
GITEA_URL=""
RUNNER_TOKEN=""
# ---- arg parse ----
while [[ $# -gt 0 ]]; do
case "$1" in
--gitea-url) GITEA_URL="$2"; shift 2 ;;
--runner-token) RUNNER_TOKEN="$2"; shift 2 ;;
--runner-name) RUNNER_NAME="$2"; shift 2 ;;
--runner-labels) RUNNER_LABELS="$2"; shift 2 ;;
--service-user) SERVICE_USER="$2"; shift 2 ;;
-h|--help)
sed -n '2,15p' "$0"
exit 0 ;;
*) echo "Unknown arg: $1" >&2; exit 2 ;;
esac
done
[[ "$EUID" -eq 0 ]] || { echo "Run as root (use sudo)." >&2; exit 1; }
[[ -n "$GITEA_URL" && -n "$RUNNER_TOKEN" ]] \
|| { echo "Missing --gitea-url or --runner-token" >&2; exit 2; }
# ---- arch + macOS version detection ----
ARCH="$(uname -m)"
case "$ARCH" in
arm64) HOMEBREW_PREFIX="/opt/homebrew"; ARCH_LABEL="ARM64" ;;
x86_64) HOMEBREW_PREFIX="/usr/local"; ARCH_LABEL="X64" ;;
*) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
esac
OS_MAJOR="$(sw_vers -productVersion | cut -d. -f1)"
[[ "$OS_MAJOR" -ge 14 ]] || {
echo "WARNING: tested only on macOS 14+. You're on $(sw_vers -productVersion)."
sleep 3
}
DISTRO_LABEL="macos-${OS_MAJOR}"
if [[ -z "$RUNNER_LABELS" ]]; then
RUNNER_LABELS="${DISTRO_LABEL},self-hosted,${ARCH_LABEL},macOS"
fi
log() { printf '\n==> %s\n' "$*"; }
# ---- 1. Xcode Command Line Tools ----
log "Verifying Xcode Command Line Tools"
if ! /usr/bin/xcode-select -p >/dev/null 2>&1; then
echo "Xcode Command Line Tools not installed. Run:" >&2
echo " xcode-select --install" >&2
echo "Then re-run this script." >&2
exit 1
fi
echo " $(xcode-select -p)"
# ---- 2. Homebrew (machine-wide) ----
# Homebrew refuses to install under root (its installer aborts with
# "Don't run this as root!"). It must be installed manually by a regular
# user before this script runs.
log "Verifying Homebrew"
if [[ ! -x "$HOMEBREW_PREFIX/bin/brew" ]]; then
echo "Homebrew not installed at $HOMEBREW_PREFIX." >&2
echo "Install it as your regular user (NOT root), then re-run this script:" >&2
echo " /bin/bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"" >&2
exit 1
fi
export PATH="$HOMEBREW_PREFIX/bin:$PATH"
echo " $(brew --version | head -1)"
# brew install must also run as a non-root user. Determine which user invoked
# sudo so we can drop privileges for brew commands below.
BREW_USER="${SUDO_USER:-}"
if [[ -z "$BREW_USER" || "$BREW_USER" == "root" ]]; then
echo "Could not determine the non-root user that ran sudo (SUDO_USER unset)." >&2
echo "Re-run with: sudo ./provision.sh ..." >&2
exit 1
fi
brew_as_user() { sudo -u "$BREW_USER" -H "$HOMEBREW_PREFIX/bin/brew" "$@"; }
# ---- 3. brew packages ----
log "Installing brew packages"
brew_pkgs=(node cocoapods llvm create-dmg pkg-config cmake ninja yasm autoconf automake libtool wget)
for p in "${brew_pkgs[@]}"; do
if brew_as_user list --versions "$p" >/dev/null 2>&1; then
echo " $p (already installed)"
else
brew_as_user install "$p"
fi
done
# ---- 4. NASM 2.16.x (NOT brew's nasm 3.x; aom/dav1d need 2.x) ----
if ! /usr/local/bin/nasm --version 2>/dev/null | grep -q "version $NASM_VERSION"; then
log "Installing NASM $NASM_VERSION"
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/nasm.zip" \
"https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VERSION}/macosx/nasm-${NASM_VERSION}-macosx.zip"
unzip -q "$tmp/nasm.zip" -d "$tmp"
install -m 0755 "$tmp/nasm-${NASM_VERSION}/nasm" /usr/local/bin/nasm
rm -rf "$tmp"
fi
/usr/local/bin/nasm --version | head -1
# ---- 5. dedicated runner user ----
if ! /usr/bin/id -u "$SERVICE_USER" >/dev/null 2>&1; then
log "Creating user $SERVICE_USER"
# Find an unused UID >= 600
uid=600
while dscl . -list /Users UniqueID | awk '{print $2}' | grep -qx "$uid"; do
uid=$((uid + 1))
done
dscl . -create "/Users/$SERVICE_USER"
dscl . -create "/Users/$SERVICE_USER" UserShell /bin/bash
dscl . -create "/Users/$SERVICE_USER" RealName "Gitea Runner"
dscl . -create "/Users/$SERVICE_USER" UniqueID "$uid"
dscl . -create "/Users/$SERVICE_USER" PrimaryGroupID 20
dscl . -create "/Users/$SERVICE_USER" NFSHomeDirectory "/Users/$SERVICE_USER"
mkdir -p "/Users/$SERVICE_USER"
chown "$SERVICE_USER:staff" "/Users/$SERVICE_USER"
fi
RUNNER_HOME="/Users/$SERVICE_USER"
# ---- 6. Rust (machine-wide) ----
export RUSTUP_HOME=/opt/rustup
export CARGO_HOME=/opt/cargo
mkdir -p "$RUSTUP_HOME" "$CARGO_HOME"
if [[ ! -x "$CARGO_HOME/bin/rustup" ]]; then
log "Installing rustup at $RUSTUP_HOME / $CARGO_HOME"
curl -fsSL https://sh.rustup.rs | RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
sh -s -- -y --default-toolchain none --profile minimal --no-modify-path
fi
"$CARGO_HOME/bin/rustup" toolchain install "$RUST_VERSION" --profile minimal --component rustfmt
"$CARGO_HOME/bin/rustup" target add --toolchain "$RUST_VERSION" aarch64-apple-darwin x86_64-apple-darwin
"$CARGO_HOME/bin/rustup" default "$RUST_VERSION"
# ---- 7. Flutter (two SDKs: 3.24.5 for build, 3.22.3 for bridge gen) ----
# Same rationale as Linux: bridge codegen 1.80.1 + freezed produces broken Dart
# under newer Flutter. Run codegen under 3.22.3, build under 3.24.5.
install_flutter() {
local ver="$1" dir="$2"
if [[ ! -x "$dir/bin/flutter" ]]; then
log "Installing Flutter $ver -> $dir"
local tmp; tmp="$(mktemp -d)"
local parent; parent="$(dirname "$dir")"
# Flutter URL pattern differs between archs: Apple Silicon has an
# `_arm64_` infix, Intel has no arch infix at all.
local flutter_url_base="https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos"
local flutter_url
case "$ARCH" in
arm64) flutter_url="${flutter_url_base}_arm64_${ver}-stable.zip" ;;
x86_64) flutter_url="${flutter_url_base}_${ver}-stable.zip" ;;
esac
curl -fsSL -o "$tmp/flutter.zip" "$flutter_url"
mkdir -p "$parent"
unzip -q "$tmp/flutter.zip" -d "$tmp"
mv "$tmp/flutter" "$dir"
rm -rf "$tmp"
fi
"$dir/bin/flutter" config --no-analytics >/dev/null
"$dir/bin/flutter" precache --macos >/dev/null
}
install_flutter "$FLUTTER_VERSION" /opt/flutter
install_flutter "$FLUTTER_BRIDGE_VERSION" /opt/flutter-bridge
# ---- 8. vcpkg ----
VCPKG_DIR=/opt/vcpkg
if [[ ! -d "$VCPKG_DIR/.git" ]]; then
log "Cloning vcpkg"
git clone https://github.com/microsoft/vcpkg.git "$VCPKG_DIR"
fi
git -C "$VCPKG_DIR" fetch --tags origin
git -C "$VCPKG_DIR" -c advice.detachedHead=false checkout "$VCPKG_COMMIT"
[[ -x "$VCPKG_DIR/vcpkg" ]] || "$VCPKG_DIR/bootstrap-vcpkg.sh" -disableMetrics
mkdir -p /var/cache/vcpkg
chown -R "$SERVICE_USER:staff" /var/cache/vcpkg
# ---- 9. Permissions ----
log "Setting up permissions for $SERVICE_USER"
chown -R "$SERVICE_USER:staff" "$CARGO_HOME" "$RUSTUP_HOME" \
/opt/flutter /opt/flutter-bridge "$VCPKG_DIR"
mkdir -p /opt/cargo-tools
chown -R "$SERVICE_USER:staff" /opt/cargo-tools
git config --system --add safe.directory '*' || true
# ---- 10. act_runner ----
RUNNER_DIR="/usr/local/var/gitea-runner"
mkdir -p "$RUNNER_DIR"
chown -R "$SERVICE_USER:staff" "$RUNNER_DIR"
if [[ ! -x "$RUNNER_DIR/act_runner" ]]; then
log "Downloading act_runner $RUNNER_VERSION"
case "$ARCH" in
arm64) rarch="arm64" ;;
x86_64) rarch="amd64" ;;
esac
curl -fsSL -o "$RUNNER_DIR/act_runner" \
"https://gitea.com/gitea/act_runner/releases/download/v${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-darwin-${rarch}"
chmod +x "$RUNNER_DIR/act_runner"
chown "$SERVICE_USER:staff" "$RUNNER_DIR/act_runner"
fi
if [[ ! -f "$RUNNER_DIR/.runner" ]]; then
log "Registering runner with $GITEA_URL"
sudo -u "$SERVICE_USER" -H bash -c "
cd '$RUNNER_DIR' && \
./act_runner register --no-interactive \
--instance '$GITEA_URL' \
--token '$RUNNER_TOKEN' \
--name '$RUNNER_NAME' \
--labels '$RUNNER_LABELS'
"
fi
# ---- 11. launchd service ----
log "Installing LaunchDaemon"
PLIST=/Library/LaunchDaemons/com.rustdesk.gitea-runner.plist
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.rustdesk.gitea-runner</string>
<key>UserName</key>
<string>${SERVICE_USER}</string>
<key>WorkingDirectory</key>
<string>${RUNNER_DIR}</string>
<key>ProgramArguments</key>
<array>
<string>${RUNNER_DIR}/act_runner</string>
<string>daemon</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>RUSTUP_HOME</key> <string>${RUSTUP_HOME}</string>
<key>CARGO_HOME</key> <string>${CARGO_HOME}</string>
<key>VCPKG_ROOT</key> <string>${VCPKG_DIR}</string>
<key>HOMEBREW_PREFIX</key> <string>${HOMEBREW_PREFIX}</string>
<key>PATH</key>
<string>${CARGO_HOME}/bin:/opt/flutter/bin:/opt/cargo-tools/bin:${HOMEBREW_PREFIX}/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardOutPath</key><string>${RUNNER_DIR}/stdout.log</string>
<key>StandardErrorPath</key><string>${RUNNER_DIR}/stderr.log</string>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key> <integer>65535</integer>
</dict>
</dict>
</plist>
EOF
chmod 0644 "$PLIST"
launchctl bootout system "$PLIST" 2>/dev/null || true
launchctl bootstrap system "$PLIST"
launchctl enable "system/com.rustdesk.gitea-runner"
log "Done."
echo " Verify with: sudo launchctl print system/com.rustdesk.gitea-runner | head"
echo " Tail logs with: tail -F $RUNNER_DIR/stderr.log"
echo " Runner should appear (online) at $GITEA_URL > Site Admin > Actions > Runners"