#!/usr/bin/env bash set -euo pipefail # Read-only readiness check for splitting the shared 110 Gitea runner pool. # Run on 110, or from a workstation: # ssh 192.168.0.110 'bash -s' < ops/runner/check-runner-isolation-readiness.sh PRIMARY_RUNNER_DIR="${PRIMARY_RUNNER_DIR:-/home/wooo/act-runner}" PRIMARY_SERVICE_NAME="${PRIMARY_SERVICE_NAME:-gitea-act-runner-host.service}" SPLIT_RUNNER_DIRS="${SPLIT_RUNNER_DIRS:-/home/wooo/act-runner-awoooi /home/wooo/act-runner-shared /home/wooo/act-runner-ewoooc}" SPLIT_SERVICE_NAMES="${SPLIT_SERVICE_NAMES:-gitea-act-runner-awoooi.service gitea-act-runner-shared.service gitea-act-runner-ewoooc.service}" section() { printf '\n== %s ==\n' "$1" } unit_exists() { local unit="$1" local load_state load_state="$(systemctl --user show "$unit" -p LoadState --value 2>/dev/null || true)" if [ -n "$load_state" ] && [ "$load_state" != "not-found" ]; then return 0 fi load_state="$(systemctl show "$unit" -p LoadState --value 2>/dev/null || true)" [ -n "$load_state" ] && [ "$load_state" != "not-found" ] } unit_state() { local unit="$1" local load_state load_state="$(systemctl --user show "$unit" -p LoadState --value 2>/dev/null || true)" if [ -n "$load_state" ] && [ "$load_state" != "not-found" ]; then printf 'scope=user ' systemctl --user show "$unit" -p LoadState -p ActiveState -p SubState -p MainPID --no-pager 2>/dev/null | tr '\n' ' ' printf '\n' return 0 fi load_state="$(systemctl show "$unit" -p LoadState --value 2>/dev/null || true)" if [ -n "$load_state" ] && [ "$load_state" != "not-found" ]; then printf 'scope=system ' systemctl show "$unit" -p LoadState -p ActiveState -p SubState -p MainPID --no-pager 2>/dev/null | tr '\n' ' ' printf '\n' return 0 fi printf 'scope=missing ActiveState=missing\n' } extract_labels() { local config_path="$1" awk ' /^[[:space:]]*labels:[[:space:]]*$/ { in_labels=1 next } in_labels && /^[[:space:]]*-[[:space:]]*/ { line=$0 sub(/^[[:space:]]*-[[:space:]]*"/, "", line) sub(/^[[:space:]]*-[[:space:]]*/, "", line) sub(/"[[:space:]]*$/, "", line) print line next } in_labels && /^[^[:space:]]/ { in_labels=0 } ' "$config_path" } label_owner() { local label="$1" local label_name="${label%%:*}" case "$label_name" in awoooi-host) printf 'awoooi_dedicated' ;; ewoooc-host) printf 'foreign_dedicated' ;; ubuntu-latest|ubuntu-22.04|ubuntu-24.04|self-hosted) printf 'shared_queue' ;; *) printf 'unknown_or_custom' ;; esac } print_primary_runner() { section "primary runner" printf 'primary_runner_dir=%s\n' "$PRIMARY_RUNNER_DIR" printf 'primary_service=%s %s\n' "$PRIMARY_SERVICE_NAME" "$(unit_state "$PRIMARY_SERVICE_NAME")" if [ -d "$PRIMARY_RUNNER_DIR" ]; then find "$PRIMARY_RUNNER_DIR" -maxdepth 1 -mindepth 1 \ -printf 'primary_file=%f type=%y mode=%M\n' 2>/dev/null | sort || true else printf 'primary_runner_dir_exists=false\n' fi if [ -f "${PRIMARY_RUNNER_DIR}/.runner" ]; then printf 'primary_registration_file=present\n' else printf 'primary_registration_file=missing\n' fi } print_label_owners() { section "primary label owners" local config_path="${PRIMARY_RUNNER_DIR}/config.yaml" if [ ! -r "$config_path" ]; then printf 'config_readable=false\n' return 0 fi local labels=() while IFS= read -r label; do [ -n "$label" ] && labels+=("$label") done < <(extract_labels "$config_path") if [ "${#labels[@]}" -eq 0 ]; then printf 'labels_found=0\n' return 0 fi local owner local mixed_owner_classes=0 local has_awoooi=0 local has_foreign=0 local has_shared=0 for label in "${labels[@]}"; do owner="$(label_owner "$label")" printf 'label=%s owner=%s\n' "$label" "$owner" case "$owner" in awoooi_dedicated) has_awoooi=1 ;; foreign_dedicated) has_foreign=1 ;; shared_queue) has_shared=1 ;; esac done if [ "$((has_awoooi + has_foreign + has_shared))" -gt 1 ]; then mixed_owner_classes=1 fi printf 'mixed_owner_classes=%s\n' "$mixed_owner_classes" } print_split_targets() { section "split target readiness" local ready_dirs=0 local total_dirs=0 local dir for dir in $SPLIT_RUNNER_DIRS; do total_dirs=$((total_dirs + 1)) if [ -d "$dir" ] && [ -f "${dir}/.runner" ] && [ -f "${dir}/config.yaml" ]; then ready_dirs=$((ready_dirs + 1)) printf 'split_dir=%s status=registered\n' "$dir" elif [ -d "$dir" ]; then printf 'split_dir=%s status=incomplete\n' "$dir" else printf 'split_dir=%s status=missing\n' "$dir" fi done local ready_services=0 local total_services=0 local service for service in $SPLIT_SERVICE_NAMES; do total_services=$((total_services + 1)) if unit_exists "$service"; then ready_services=$((ready_services + 1)) printf 'split_service=%s %s\n' "$service" "$(unit_state "$service")" else printf 'split_service=%s scope=missing ActiveState=missing\n' "$service" fi done printf 'registered_split_dirs=%s/%s\n' "$ready_dirs" "$total_dirs" printf 'installed_split_services=%s/%s\n' "$ready_services" "$total_services" } print_active_tasks() { section "active tasks" if ! command -v docker >/dev/null 2>&1; then printf 'docker=unavailable\n' return 0 fi local tasks tasks="$(docker ps --format '{{.Names}}\t{{.Image}}\t{{.Status}}' 2>/dev/null | awk '$1 ~ /^GITEA-ACTIONS-TASK-/ { print }' || true)" if [ -n "$tasks" ]; then printf '%s\n' "$tasks" else printf 'active_action_containers=none\n' fi } print_verdict() { section "verdict" local config_path="${PRIMARY_RUNNER_DIR}/config.yaml" local mixed=0 if [ ! -r "$config_path" ]; then printf 'isolation_ready=unknown\n' printf 'blocker=primary_config_unreadable\n' printf 'safe_next_step=run_on_110_or_set_PRIMARY_RUNNER_DIR\n' return 0 fi local owners owners="$(extract_labels "$config_path" | while IFS= read -r label; do printf '%s\n' "$(label_owner "$label")"; done | sort -u | wc -l | tr -d ' ')" if [ "${owners:-0}" -gt 1 ]; then mixed=1 fi local split_registered=0 local dir for dir in $SPLIT_RUNNER_DIRS; do if [ -d "$dir" ] && [ -f "${dir}/.runner" ] && [ -f "${dir}/config.yaml" ]; then split_registered=$((split_registered + 1)) fi done if [ "$mixed" -eq 1 ] && [ "$split_registered" -eq 0 ]; then printf 'isolation_ready=false\n' printf 'blocker=single_registered_runner_with_mixed_owner_labels\n' printf 'safe_next_step=register_additional_runner_dirs_before_removing_live_labels\n' elif [ "$mixed" -eq 1 ]; then printf 'isolation_ready=partial\n' printf 'blocker=split_services_not_fully_installed_or_not_verified\n' printf 'safe_next_step=drain_then_move_labels_after_split_runner_smoke\n' else printf 'isolation_ready=true\n' printf 'blocker=none\n' fi } main() { section "audit metadata" printf 'host=%s\n' "$(hostname 2>/dev/null || printf unknown)" printf 'user=%s\n' "$(id -un 2>/dev/null || printf unknown)" printf 'timestamp=%s\n' "$(date -Is 2>/dev/null || date)" printf 'read_only=true\n' print_primary_runner print_label_owners print_split_targets print_active_tasks print_verdict } main "$@"