From 5341447acbbe3c982f1ca6e6f52d926adecb6d40 Mon Sep 17 00:00:00 2001 From: S Date: Sun, 1 Mar 2026 08:24:33 -0500 Subject: [PATCH] feat: add support for boot flag in native runner configuration --- manage_runner.sh | 81 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/manage_runner.sh b/manage_runner.sh index 5f11a1e..a9d0fb9 100755 --- a/manage_runner.sh +++ b/manage_runner.sh @@ -41,7 +41,7 @@ EOF # Parse a runner entry from runners.conf (INI format) by section name. # Sets globals: RUNNER_NAME, RUNNER_HOST, RUNNER_TYPE, RUNNER_DATA_PATH, # RUNNER_LABELS, RUNNER_DEFAULT_IMAGE, RUNNER_REPOS, RUNNER_CAPACITY, -# RUNNER_CPU, RUNNER_MEMORY +# RUNNER_CPU, RUNNER_MEMORY, RUNNER_BOOT # Also resolves: RUNNER_SSH_HOST, RUNNER_SSH_USER, RUNNER_SSH_PORT, # RUNNER_SSH_KEY (from .env or custom section keys) # Returns 1 if not found. @@ -70,6 +70,10 @@ parse_runner_entry() { RUNNER_CAPACITY=$(ini_get "$RUNNERS_CONF" "$target_name" "capacity" "${RUNNER_DEFAULT_CAPACITY:-1}") RUNNER_CPU=$(ini_get "$RUNNERS_CONF" "$target_name" "cpu" "") RUNNER_MEMORY=$(ini_get "$RUNNERS_CONF" "$target_name" "memory" "") + # boot: controls launchd install location for native runners. + # "true" → /Library/LaunchDaemons/ (starts at boot, requires sudo) + # "false" (default) → ~/Library/LaunchAgents/ (starts at login) + RUNNER_BOOT=$(ini_get "$RUNNERS_CONF" "$target_name" "boot" "false") # --- Host resolution --- case "$RUNNER_HOST" in @@ -342,7 +346,17 @@ add_native_runner() { export RUNNER_DATA_PATH local plist_name="com.gitea.runner.${RUNNER_NAME}.plist" - local plist_path="$HOME/Library/LaunchAgents/${plist_name}" + + # Route plist to LaunchDaemons (boot) or LaunchAgents (login) based on boot flag. + # LaunchDaemons start at boot before any user logs in — useful for headless Macs. + # LaunchAgents start when the user logs in — no elevated privileges needed. + local plist_dir + if [[ "$RUNNER_BOOT" == "true" ]]; then + plist_dir="/Library/LaunchDaemons" + else + plist_dir="$HOME/Library/LaunchAgents" + fi + local plist_path="${plist_dir}/${plist_name}" # Check if launchd service is already loaded if launchctl list 2>/dev/null | grep -q "com.gitea.runner.${RUNNER_NAME}"; then @@ -393,12 +407,28 @@ add_native_runner() { cp "$tmpfile" "${RUNNER_DATA_PATH}/config.yaml" rm -f "$tmpfile" - # Render launchd plist + # Render launchd plist. + # When boot=true, insert a UserName entry so the daemon runs as + # the deploying user instead of root (LaunchDaemons default to root). + # When boot=false (LaunchAgent), the block is empty — agents run as the user. + if [[ "$RUNNER_BOOT" == "true" ]]; then + RUNNER_PLIST_USERNAME_BLOCK="$(printf ' UserName\n %s\n' "$(whoami)")" + else + RUNNER_PLIST_USERNAME_BLOCK="" + fi + export RUNNER_PLIST_USERNAME_BLOCK + tmpfile=$(mktemp) render_template "${SCRIPT_DIR}/templates/com.gitea.runner.plist.tpl" "$tmpfile" \ - "\${RUNNER_NAME} \${RUNNER_DATA_PATH}" - mkdir -p "$HOME/Library/LaunchAgents" - cp "$tmpfile" "$plist_path" + "\${RUNNER_NAME} \${RUNNER_DATA_PATH} \${RUNNER_PLIST_USERNAME_BLOCK}" + mkdir -p "$plist_dir" + + # LaunchDaemons lives in a system directory — requires sudo to write. + if [[ "$RUNNER_BOOT" == "true" ]]; then + sudo cp "$tmpfile" "$plist_path" + else + cp "$tmpfile" "$plist_path" + fi rm -f "$tmpfile" # Install newsyslog config for log rotation (daily, 5 archives, 50 MB max each). @@ -414,9 +444,15 @@ add_native_runner() { log_success "Log rotation installed: $newsyslog_conf" fi - # Load the launchd service - launchctl load "$plist_path" - log_success "Native runner '${RUNNER_NAME}' loaded via launchd" + # Load the launchd service. + # LaunchDaemons require sudo to load/unload; LaunchAgents do not. + if [[ "$RUNNER_BOOT" == "true" ]]; then + sudo launchctl load "$plist_path" + log_success "Native runner '${RUNNER_NAME}' loaded via launchd (boot daemon — starts at boot)" + else + launchctl load "$plist_path" + log_success "Native runner '${RUNNER_NAME}' loaded via launchd (login agent — starts at login)" + fi } # --------------------------------------------------------------------------- @@ -444,15 +480,34 @@ remove_native_runner() { RUNNER_DATA_PATH="${RUNNER_DATA_PATH/#\~/$HOME}" local plist_name="com.gitea.runner.${RUNNER_NAME}.plist" - local plist_path="$HOME/Library/LaunchAgents/${plist_name}" + + # The plist may live in either LaunchDaemons (boot=true) or LaunchAgents (boot=false). + # Check both directories so removal works regardless of how the runner was originally + # deployed — the runner.conf boot flag may have changed since deployment. + local plist_path="" + local needs_sudo=false + if [[ -f "/Library/LaunchDaemons/${plist_name}" ]]; then + plist_path="/Library/LaunchDaemons/${plist_name}" + needs_sudo=true + elif [[ -f "$HOME/Library/LaunchAgents/${plist_name}" ]]; then + plist_path="$HOME/Library/LaunchAgents/${plist_name}" + fi if launchctl list 2>/dev/null | grep -q "com.gitea.runner.${RUNNER_NAME}"; then - launchctl unload "$plist_path" 2>/dev/null || true + if $needs_sudo; then + sudo launchctl unload "$plist_path" 2>/dev/null || true + else + launchctl unload "${plist_path:-$HOME/Library/LaunchAgents/${plist_name}}" 2>/dev/null || true + fi log_success "Launchd service unloaded" fi - if [[ -f "$plist_path" ]]; then - rm -f "$plist_path" + if [[ -n "$plist_path" ]] && [[ -f "$plist_path" ]]; then + if $needs_sudo; then + sudo rm -f "$plist_path" + else + rm -f "$plist_path" + fi log_success "Plist removed: $plist_path" fi