#!/usr/bin/env bash set -e # Default values DEFAULT_REPO_URL="https://github.com/sunnypilot" START_AT_BOOT=false RESTORE_MODE=false RUNNER_VERSION="2.325.0" # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in --start-at-boot) START_AT_BOOT=true shift ;; --token) GITHUB_TOKEN="$2" shift 2 ;; --repo) REPO_URL="$2" shift 2 ;; --restore) RESTORE_MODE=true shift ;; *) if [ -z "$GITHUB_TOKEN" ]; then GITHUB_TOKEN="$1" elif [ -z "$REPO_URL" ]; then REPO_URL="$1" fi shift ;; esac done # Determine BASE_DIR based on mount point if mountpoint -q /data/media; then BASE_DIR="/data/media/0/github" else BASE_DIR="/data/github" fi # Constants RUNNER_USER="github-runner" USER_GROUPS="comma,gpu,gpio,sudo" RUNNER_DIR="${BASE_DIR}/runner" BUILDS_DIR="${BASE_DIR}/builds" LOGS_DIR="${BASE_DIR}/logs" CACHE_DIR="${BASE_DIR}/cache" OPENPILOT_DIR="${BASE_DIR}/openpilot" # Basic utility functions (no dependencies) remount_rw() { sudo mount -o remount,rw / } remount_ro() { sync || true # Try to sync but continue even if it fails sudo mount -o remount,ro / # Always try to remount as read-only } # Always ensure we try to remount as read-only on exit trap remount_ro EXIT setup_runner_user() { sudo useradd --comment 'GitHub Runner' --create-home --home-dir ${BASE_DIR} ${RUNNER_USER} --shell /bin/bash -G ${USER_GROUPS} || sudo usermod -aG ${USER_GROUPS} ${RUNNER_USER} } create_sudoers_entry() { sudo grep -qxF "${RUNNER_USER} ALL=(ALL) NOPASSWD: ALL" /etc/sudoers || echo "${RUNNER_USER} ALL=(ALL) NOPASSWD: ALL" | sudo tee -a /etc/sudoers } set_directory_permissions() { sudo chown -R ${RUNNER_USER}:comma "$BASE_DIR" sudo chmod -R g+rwx "$BASE_DIR" sudo find "$BASE_DIR" -type d -exec chmod g+s {} + } setup_directories() { echo "Creating necessary directories..." sudo mkdir -p "$RUNNER_DIR" "$BUILDS_DIR" "$LOGS_DIR" "$CACHE_DIR" "$OPENPILOT_DIR" mkdir -p "/data/openpilot" sudo chown -R comma:comma "/data/openpilot" sync } wipe_bash_logout() { export BASE_DIR sudo -u ${RUNNER_USER} bash -c "touch ${BASE_DIR}/.bash_logout" sudo -u ${RUNNER_USER} bash -c "truncate -s 0 '${BASE_DIR}/.bash_logout'" } # System configuration functions (depends on basic utility functions) setup_system_configs() { echo "Setting up system configurations..." remount_rw setup_runner_user create_sudoers_entry remount_ro set_directory_permissions wipe_bash_logout } # Runner setup functions install_runner() { echo "Downloading and setting up runner..." cd "$RUNNER_DIR" curl -o actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz -L https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz sudo -u ${RUNNER_USER} tar -xzf ./actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz sudo rm ./actions-runner-linux-arm64-${RUNNER_VERSION}.tar.gz sudo chmod +x ./config.sh } configure_runner() { remount_rw echo "Configuring runner..." cd "$RUNNER_DIR" sudo -u ${RUNNER_USER} ./config.sh --url "$REPO_URL" --token "$GITHUB_TOKEN" --name $(hostname) --runnergroup "tici-tizi" --labels "tici" --work "$BUILDS_DIR" --unattended remount_ro } create_service_template() { echo "Creating service template..." cat < "$RUNNER_DIR/bin/actions.runner.service.template" [Unit] Description={{Description}} After=network-online.target nss-lookup.target time-sync.target Wants=network-online.target nss-lookup.target time-sync.target StartLimitInterval=5 StartLimitBurst=10 [Service] Type=simple User=root ExecStart=/usr/bin/unshare -m -- /bin/bash -c 'mount --bind ${OPENPILOT_DIR} /data/openpilot && setpriv --reuid={{User}} --regid={{User}} --init-groups env HOME=${BASE_DIR} USER={{User}} LOGNAME={{User}} MAIL=/var/mail/{{User}} {{RunnerRoot}}/runsvc.sh' WorkingDirectory={{RunnerRoot}} KillMode=process KillSignal=SIGTERM TimeoutStopSec=5min Restart=always RestartSec=120 [Install] WantedBy=multi-user.target EOL } install_service() { local service_name if [ -f "${RUNNER_DIR}/.service" ]; then service_name=$(cat "${RUNNER_DIR}/.service") else service_name="actions.runner.sunnypilot.$(uname -n)" fi create_service_template remount_rw local service_path="/etc/systemd/system/${service_name}" echo "Installing systemd service..." if [ -f "${service_path}" ]; then echo "Service ${service_path} found in systemd, we will delete it" sudo rm -f "${service_path}" fi cd "$RUNNER_DIR" sudo ./svc.sh install $RUNNER_USER if [ "$START_AT_BOOT" = false ]; then sudo systemctl disable "${service_name}" fi remount_ro } check_restore_prerequisites() { local can_restore=false local service_name="" # Check if base runner directory exists if [ ! -d "${RUNNER_DIR}" ]; then echo "ERROR: Runner directory ${RUNNER_DIR} does not exist" echo "This directory is required for restore operations" exit 1 fi # First check if we have the required files for restoration if [ -f "${RUNNER_DIR}/.credentials" ] && [ -f "${RUNNER_DIR}/.service" ]; then can_restore=true service_name=$(cat "${RUNNER_DIR}/.service") echo "Found required runner configuration files" else echo "Missing required runner configuration files" echo "Required: .credentials and .service files in ${RUNNER_DIR}" exit 1 fi if ! id "${RUNNER_USER}" &>/dev/null; then echo "User ${RUNNER_USER} does not exist" fi # Only proceed if we can restore AND need to restore if [ "$can_restore" = true ]; then echo "Restoration is possible" return 0 else echo "No restoration possible" exit 0 fi } perform_restore() { echo "Starting runner restoration..." setup_directories setup_system_configs install_service echo "Runner restoration completed successfully" } perform_install() { echo "Starting fresh installation..." setup_directories setup_system_configs install_runner set_directory_permissions configure_runner install_service echo "Installation completed successfully" } main() { if [ "$RESTORE_MODE" = true ]; then echo "Running in restore mode - will only restore system configurations..." check_restore_prerequisites perform_restore else # Check required arguments for normal installation if [ -z "$GITHUB_TOKEN" ]; then echo "Usage: $0 [--start-at-boot] [--token ] [--repo ] [--restore]" echo "Required argument (except for --restore): github_token" echo "Optional arguments:" echo " --start-at-boot Enable auto-start at boot (default: false)" echo " --repo Repository URL (default: ${DEFAULT_REPO_URL})" echo " --restore Restore existing runner configuration" exit 1 fi # Set repository URL if not provided REPO_URL="${REPO_URL:-$DEFAULT_REPO_URL}" perform_install fi echo "Starting runner service..." cd "$RUNNER_DIR" sudo ./svc.sh start } main