#!/usr/bin/env bash set -euo pipefail RUNNER_DIR="${RUNNER_DIR:-/home/wooo/act-runner}" RUNNER_USER="${RUNNER_USER:-wooo}" SERVICE_NAME="${SERVICE_NAME:-gitea-act-runner-host.service}" SHUTDOWN_TIMEOUT="${SHUTDOWN_TIMEOUT:-1h}" LEGACY_RUNNER_STOP_TIMEOUT_SECONDS="${LEGACY_RUNNER_STOP_TIMEOUT_SECONDS:-3700}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SYSTEM_SERVICE_SRC="${SCRIPT_DIR}/gitea-act-runner-host.service" USER_SERVICE_SRC="${SCRIPT_DIR}/gitea-act-runner-host.user.service" CONFIG_FILE="${RUNNER_DIR}/config.yaml" gitea_task_containers_running() { docker ps --format '{{.Names}}' | grep -q '^GITEA-ACTIONS-TASK-' } disable_legacy_docker_runner() { if ! docker ps -a --format '{{.Names}}' | grep -qx 'gitea-runner'; then return 0 fi echo "Disabling legacy docker-wrapped gitea-runner container" docker update --restart=no gitea-runner >/dev/null 2>&1 || true if gitea_task_containers_running; then echo "Active Gitea Actions task containers are running; sending SIGINT to drain gitea-runner" >&2 docker kill --signal=SIGINT gitea-runner >/dev/null 2>&1 || true return 0 fi docker stop -t "${LEGACY_RUNNER_STOP_TIMEOUT_SECONDS}" gitea-runner >/dev/null 2>&1 || true } if [ ! -x "${RUNNER_DIR}/act_runner" ]; then echo "act_runner binary not found: ${RUNNER_DIR}/act_runner" >&2 exit 1 fi if [ ! -f "${CONFIG_FILE}" ]; then echo "act_runner config not found: ${CONFIG_FILE}" >&2 exit 1 fi python3 - "${CONFIG_FILE}" "${SHUTDOWN_TIMEOUT}" <<'PY' import sys from pathlib import Path path = Path(sys.argv[1]) shutdown_timeout = sys.argv[2] lines = path.read_text().splitlines() in_runner = False runner_indent = None insert_at = None found = False updated = [] for idx, line in enumerate(lines): stripped = line.strip() if stripped == "runner:": in_runner = True runner_indent = len(line) - len(line.lstrip()) insert_at = idx + 1 updated.append(line) continue if in_runner: indent = len(line) - len(line.lstrip()) if stripped and indent <= runner_indent: if not found and insert_at is not None: updated.insert(insert_at, f" shutdown_timeout: {shutdown_timeout}") found = True in_runner = False elif stripped.startswith("shutdown_timeout:"): updated.append(f" shutdown_timeout: {shutdown_timeout}") found = True continue elif stripped.startswith("timeout:") or stripped.startswith("capacity:"): insert_at = len(updated) + 1 updated.append(line) if in_runner and not found and insert_at is not None: updated.insert(insert_at, f" shutdown_timeout: {shutdown_timeout}") found = True if not found: raise SystemExit("runner section not found in config") path.write_text("\n".join(updated) + "\n") PY disable_legacy_docker_runner install_system_service() { sudo install -o root -g root -m 0644 "${SYSTEM_SERVICE_SRC}" "/etc/systemd/system/${SERVICE_NAME}" sudo systemctl daemon-reload sudo systemctl enable "${SERVICE_NAME}" >/dev/null } install_user_service() { local user_service_dir="${HOME}/.config/systemd/user" mkdir -p "${user_service_dir}" install -m 0644 "${USER_SERVICE_SRC}" "${user_service_dir}/${SERVICE_NAME}" systemctl --user daemon-reload systemctl --user enable "${SERVICE_NAME}" >/dev/null } stop_existing_runner() { if pgrep -u "${RUNNER_USER}" -f "${RUNNER_DIR}/act_runner daemon" >/dev/null; then pkill -INT -u "${RUNNER_USER}" -f "${RUNNER_DIR}/act_runner daemon" || true for _ in $(seq 1 60); do pgrep -u "${RUNNER_USER}" -f "${RUNNER_DIR}/act_runner daemon" >/dev/null || return 0 sleep 1 done echo "Existing act_runner daemon did not stop within 60s" >&2 return 1 fi } if sudo -n true >/dev/null 2>&1; then SERVICE_SCOPE="system" install_system_service else SERVICE_SCOPE="user" install_user_service fi if [ "${RESTART_NOW:-0}" = "1" ]; then if gitea_task_containers_running; then echo "Refusing to restart: Gitea Actions task containers are running" >&2 exit 1 fi stop_existing_runner if [ "${SERVICE_SCOPE}" = "system" ]; then sudo systemctl restart "${SERVICE_NAME}" else systemctl --user restart "${SERVICE_NAME}" fi elif pgrep -u "${RUNNER_USER}" -f "${RUNNER_DIR}/act_runner daemon" >/dev/null; then echo "Existing act_runner daemon is still running; service will take over after the next safe restart." else if [ "${SERVICE_SCOPE}" = "system" ]; then sudo systemctl start "${SERVICE_NAME}" else systemctl --user start "${SERVICE_NAME}" fi fi if [ "${SERVICE_SCOPE}" = "system" ]; then sudo systemctl --no-pager --full status "${SERVICE_NAME}" | sed -n '1,18p' || true else systemctl --user --no-pager --full status "${SERVICE_NAME}" | sed -n '1,18p' || true fi grep -n 'shutdown_timeout' "${CONFIG_FILE}"