#!/usr/bin/env bash # Provisions an Ubuntu 22.04 LTS or Debian 13 (Trixie) host as a Gitea Actions # runner for RustDesk desktop (.deb) builds. Idempotent: safe to re-run. # # Versions are pinned to .gitea/workflows/build-linux.yml. Bump them there and # here together. # # Build host vs. user host: the resulting .deb links against the host's glibc. # Build on the OLDEST distro your users have, otherwise the .deb won't install # on older systems. # - Ubuntu 22.04 build -> runs on Ubuntu 22.04+, Debian 12+, derivatives # - Debian 13 build -> runs on Debian 13+, Ubuntu 24.04+ only # # Usage: # sudo ./provision.sh \ # --gitea-url https://gitea.example.com \ # --runner-token # # All toolchains land in /opt and are readable by the gitea-runner user. # Service is installed as a systemd unit running as that user. set -euo pipefail # ---- pinned versions (mirror .gitea/workflows/build-linux.yml env block) ---- RUST_VERSION="1.75.0" FLUTTER_VERSION="3.24.5" # used for `flutter build linux` FLUTTER_BRIDGE_VERSION="3.22.3" # used for `flutter pub get` + flutter_rust_bridge_codegen LLVM_VERSION="15.0.6" VCPKG_COMMIT="120deac3062162151622ca4860575a33844ba10b" RUNNER_VERSION="0.2.11" # ---- defaults ---- RUNNER_NAME="$(hostname)-rustdesk" RUNNER_LABELS="" # auto-derived from /etc/os-release if empty 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,18p' "$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; } . /etc/os-release case "${ID}-${VERSION_ID:-}" in ubuntu-22.04) DISTRO_LABEL="ubuntu-22.04" ;; debian-13|debian-trixie) DISTRO_LABEL="debian-13" ;; *) echo "WARNING: tested only on Ubuntu 22.04 and Debian 13. You're on $PRETTY_NAME." echo "Package names may differ; build outputs may not run on user systems." DISTRO_LABEL="${ID}-${VERSION_ID:-unknown}" sleep 3 ;; esac # If --runner-labels wasn't passed, derive a sensible default that includes the # detected distro so workflows can target a specific build host when needed. if [[ -z "$RUNNER_LABELS" ]]; then RUNNER_LABELS="${DISTRO_LABEL},self-hosted,X64,Linux" fi log() { printf '\n==> %s\n' "$*"; } # ---- 1. apt packages ---- log "Installing apt packages" export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y --no-install-recommends \ build-essential clang gcc g++ cmake ninja-build pkg-config nasm yasm \ autoconf automake libtool libtool-bin \ libclang-dev llvm-dev \ libgtk-3-dev libayatana-appindicator3-dev \ libasound2-dev libpulse-dev libpam0g-dev libssl-dev \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ libva-dev libxdo-dev libxfixes-dev \ libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev \ git curl wget zip unzip tar xz-utils ca-certificates \ python3 python3-pip \ rpm tree dpkg-dev sudo # Node.js (act_runner spawns node for JS actions like actions/checkout) if ! command -v node >/dev/null; then log "Installing Node.js LTS" curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y --no-install-recommends nodejs fi # ---- 2. LLVM (binary tarball; libclang-15-dev was dropped from Debian 13) ---- LLVM_DIR="/opt/llvm-${LLVM_VERSION}" if [[ ! -x "$LLVM_DIR/bin/clang" ]]; then log "Installing LLVM $LLVM_VERSION (binary tarball)" arch="$(uname -m)" case "$arch" in x86_64) llvm_arch="x86_64-linux-gnu-ubuntu-18.04" ;; aarch64) llvm_arch="aarch64-linux-gnu" ;; *) echo "Unsupported arch for LLVM tarball: $arch" >&2; exit 1 ;; esac tmp="$(mktemp -d)" curl -fsSL -o "$tmp/llvm.tar.xz" \ "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-${llvm_arch}.tar.xz" mkdir -p "$LLVM_DIR" tar --strip-components=1 -xJf "$tmp/llvm.tar.xz" -C "$LLVM_DIR" rm -rf "$tmp" fi # ---- 3. dedicated runner user ---- if ! id -u "$SERVICE_USER" >/dev/null 2>&1; then log "Creating user $SERVICE_USER" useradd --system --create-home --shell /bin/bash "$SERVICE_USER" fi RUNNER_HOME="$(getent passwd "$SERVICE_USER" | cut -d: -f6)" # ---- 4. 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" x86_64-unknown-linux-gnu "$CARGO_HOME/bin/rustup" default "$RUST_VERSION" # ---- 5. Flutter (two SDKs: 3.24.5 for build, 3.22.3 for bridge gen) ---- # Why two: the bridge codegen (flutter_rust_bridge_codegen 1.80.1 + freezed) # produces broken Dart output when run under newer Flutter SDKs on Linux. # Upstream's bridge.yml uses 3.22.3 specifically; we mirror that. The .deb # build itself uses 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")" curl -fsSL -o "$tmp/flutter.tar.xz" \ "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${ver}-stable.tar.xz" # Tarball extracts into a top-level `flutter/` dir; rename to target. tar -xJf "$tmp/flutter.tar.xz" -C "$tmp" mkdir -p "$parent" mv "$tmp/flutter" "$dir" rm -rf "$tmp" fi "$dir/bin/flutter" config --no-analytics >/dev/null "$dir/bin/flutter" precache --linux >/dev/null } install_flutter "$FLUTTER_VERSION" /opt/flutter install_flutter "$FLUTTER_BRIDGE_VERSION" /opt/flutter-bridge FLUTTER_DIR=/opt/flutter # ---- 6. 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 # vcpkg binary cache (file-backed -- same scheme as build-windows.yml) mkdir -p /var/cache/vcpkg chown -R "$SERVICE_USER:$SERVICE_USER" /var/cache/vcpkg # ---- 7. Permissions ---- log "Setting up permissions for $SERVICE_USER" chown -R "$SERVICE_USER:$SERVICE_USER" "$CARGO_HOME" # rustup state needs to be writable too -- toolchain installs touch it. chown -R "$SERVICE_USER:$SERVICE_USER" "$RUSTUP_HOME" # Flutter SDK: r/x is enough for builds, but `flutter pub get` writes to its # own cache subdir so we make it writable as well. chown -R "$SERVICE_USER:$SERVICE_USER" "$FLUTTER_DIR" chown -R "$SERVICE_USER:$SERVICE_USER" /opt/flutter-bridge # vcpkg: builds write under installed/, buildtrees/, etc. chown -R "$SERVICE_USER:$SERVICE_USER" "$VCPKG_DIR" # LLVM: read+execute is enough; we never write here at build time. chown -R "$SERVICE_USER:$SERVICE_USER" "$LLVM_DIR" # /opt/cargo-tools: workflow installs cargo-expand and flutter_rust_bridge_codegen # here via `cargo install --root`. Pre-create with the right owner so the first # job doesn't try to mkdir under root-owned /opt. mkdir -p /opt/cargo-tools chown -R "$SERVICE_USER:$SERVICE_USER" /opt/cargo-tools # git "dubious ownership": same fix as Windows. Trust system-wide. git config --system --add safe.directory '*' || true # ---- 8. act_runner ---- RUNNER_DIR=/var/lib/gitea-runner mkdir -p "$RUNNER_DIR" chown -R "$SERVICE_USER:$SERVICE_USER" "$RUNNER_DIR" if [[ ! -x "$RUNNER_DIR/act_runner" ]]; then log "Downloading act_runner $RUNNER_VERSION" curl -fsSL -o "$RUNNER_DIR/act_runner" \ "https://gitea.com/gitea/act_runner/releases/download/v${RUNNER_VERSION}/act_runner-${RUNNER_VERSION}-linux-amd64" chmod +x "$RUNNER_DIR/act_runner" chown "$SERVICE_USER:$SERVICE_USER" "$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 # ---- 9. systemd service ---- log "Installing systemd unit" cat > /etc/systemd/system/gitea-act-runner.service < Site Admin > Actions > Runners"