diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 5411c94a..72edfe3e 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,24 @@ +## 2026-06-28 — 20:30 188 non-110 user runner prepare-only 實作與 readback + +**完成內容**: +- 新增 `ops/runner/install-awoooi-non110-user-runner.sh`,支援 188 `ollama` user-level Docker runner;預設 `PREPARE_ONLY=1` 時只安裝 config、user systemd service 與 rollback unit,不啟動、不註冊、不讀 token。 +- 新增 `ops/runner/awoooi-non110-runner.user.service.example` 與 `ops/runner/awoooi-non110-runner-rollback.user.service.example`,補齊無 passwordless sudo 時的 user service / rollback source-of-truth。 +- `ops/runner/check-awoooi-non110-runner-readiness.sh` 擴充 188 實際路徑:`/home/ollama/awoooi-non110-runner/data/config.yaml`、`/home/ollama/awoooi-non110-runner/data/.runner`,並接受 `gitea/act_runner:latest` Docker image 作為 runner runtime evidence;仍要求 `.runner` registration metadata 存在且 service active 才能 `AWOOOI_NON110_RUNNER_READY=1`。 +- 已在 188 執行 `PREPARE_ONLY=1 bash -s < ops/runner/install-awoooi-non110-user-runner.sh`,readback `AWOOOI_NON110_USER_RUNNER_INSTALL_OK prepare_only=1 register_now=0 start_now=0`。 + +**188 readback**: +- `READY_CONFIG_COUNT=2`、`READY_BINARY_COUNT=2`、`READY_SERVICE_COUNT=1`、rollback unit installed `1`。 +- `ACTIVE_ACTION_CONTAINERS=0`、`HEAVY_PROCESS_COUNT=0`、load/core 約 `0.12-0.14`,host selector 正確在 `192.168.0.188`。 +- 仍為 `AWOOOI_NON110_RUNNER_READY=0`;剩餘 blockers:`runner_registration_missing`、`runner_service_not_active:awoooi-non110-runner.service`、`no_active_runner_service`。 +- safe next step 變成 `complete_runner_registration_without_printing_token_then_enable_service_and_rerun_this_verifier`。 + +**驗證結果**: +- `bash -n ops/runner/check-awoooi-non110-runner-readiness.sh ops/runner/install-awoooi-non110-user-runner.sh scripts/reboot-recovery/enforce-110-runner-failclosed.sh scripts/reboot-recovery/awoooi-enforce-runner-failclosed-110.sh`:通過。 +- `python3.11 -m pytest ops/runner/test_guard_gitea_runner_pressure.py ops/runner/test_check_awoooi_non110_runner_readiness.py -q`:`6 passed`。 +- `python3 ops/runner/guard-gitea-runner-pressure.py --root .`:`GITEA_RUNNER_PRESSURE_GUARD_OK workflow_files=10 scheduled_workflows=3 auto_branch_events_on_110=0 generic_runner_labels=0`。 + +**邊界**:沒有讀 `.runner` 內容、runner token、secret、raw session、SQLite、auth 或 `.env`;沒有啟動 runner service;沒有重啟 Docker / Nginx / firewall / K3s / DB;沒有使用 GitHub;沒有 force push。 + ## 2026-06-28 — 20:28 production deploy readback snapshot refresh **完成內容**: diff --git a/ops/runner/awoooi-non110-runner-rollback.user.service.example b/ops/runner/awoooi-non110-runner-rollback.user.service.example new file mode 100644 index 00000000..54f3b739 --- /dev/null +++ b/ops/runner/awoooi-non110-runner-rollback.user.service.example @@ -0,0 +1,10 @@ +[Unit] +Description=Rollback AWOOOI non-110 user Gitea runner to inactive + +[Service] +Type=oneshot +ExecStart=-/usr/bin/systemctl --user stop awoooi-non110-runner.service +ExecStart=-/usr/bin/systemctl --user disable awoooi-non110-runner.service +ExecStart=-/usr/bin/systemctl --user reset-failed awoooi-non110-runner.service +ExecStart=-/usr/bin/docker rm -f awoooi-non110-runner +RemainAfterExit=no diff --git a/ops/runner/awoooi-non110-runner.user.service.example b/ops/runner/awoooi-non110-runner.user.service.example new file mode 100644 index 00000000..c4ae1ee6 --- /dev/null +++ b/ops/runner/awoooi-non110-runner.user.service.example @@ -0,0 +1,27 @@ +[Unit] +Description=AWOOOI non-110 Gitea runner, user Docker, capacity 1 +Documentation=file:/opt/awoooi/ops/runner/check-awoooi-non110-runner-readiness.sh +After=default.target +ConditionPathExists=/home/ollama/awoooi-non110-runner/data/.runner + +[Service] +Type=simple +WorkingDirectory=/home/ollama/awoooi-non110-runner +Environment=CONFIG_FILE=/data/config.yaml +ExecStartPre=-/usr/bin/docker rm -f awoooi-non110-runner +ExecStart=/usr/bin/docker run --rm --name awoooi-non110-runner -v /home/ollama/awoooi-non110-runner/data:/data -v /var/run/docker.sock:/var/run/docker.sock -e CONFIG_FILE=/data/config.yaml gitea/act_runner:latest +ExecStop=/usr/bin/docker stop -t 3700 awoooi-non110-runner +ExecStopPost=-/usr/bin/docker rm -f awoooi-non110-runner +Restart=always +RestartSec=20 +KillSignal=SIGINT +TimeoutStartSec=120 +TimeoutStopSec=3700 +CPUQuota=200% +MemoryHigh=6G +MemoryMax=8G +TasksMax=512 +NoNewPrivileges=true + +[Install] +WantedBy=default.target diff --git a/ops/runner/check-awoooi-non110-runner-readiness.sh b/ops/runner/check-awoooi-non110-runner-readiness.sh index 8c00cf5b..7dcd89b0 100755 --- a/ops/runner/check-awoooi-non110-runner-readiness.sh +++ b/ops/runner/check-awoooi-non110-runner-readiness.sh @@ -8,9 +8,11 @@ set -euo pipefail TARGET_HOST_IP="${TARGET_HOST_IP:-192.168.0.188}" FORBIDDEN_HOST_IPS="${FORBIDDEN_HOST_IPS:-192.168.0.110}" -RUNNER_CONFIG_PATHS="${RUNNER_CONFIG_PATHS:-/home/wooo/act-runner-awoooi/config.yaml /home/wooo/awoooi-act-runner/config.yaml /home/wooo/awoooi-non110-runner/config.yaml /home/wooo/act-runner/config.yaml /home/ollama/act-runner-awoooi/config.yaml /home/ollama/awoooi-non110-runner/config.yaml}" -RUNNER_BINARY_PATHS="${RUNNER_BINARY_PATHS:-/home/wooo/act-runner-awoooi/act_runner /home/wooo/awoooi-act-runner/act_runner /home/wooo/awoooi-non110-runner/act_runner /home/wooo/act-runner/act_runner /home/ollama/act-runner-awoooi/act_runner /home/ollama/awoooi-non110-runner/act_runner}" -RUNNER_REGISTRATION_PATHS="${RUNNER_REGISTRATION_PATHS:-/home/wooo/act-runner-awoooi/.runner /home/wooo/awoooi-act-runner/.runner /home/wooo/awoooi-non110-runner/.runner /home/wooo/act-runner/.runner /home/ollama/act-runner-awoooi/.runner /home/ollama/awoooi-non110-runner/.runner}" +RUNNER_HOME="${RUNNER_HOME:-${HOME:-/home/wooo}}" +RUNNER_CONFIG_PATHS="${RUNNER_CONFIG_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/data/config.yaml ${RUNNER_HOME}/awoooi-non110-runner/config.yaml ${RUNNER_HOME}/act-runner-awoooi/config.yaml /home/wooo/act-runner-awoooi/config.yaml /home/wooo/awoooi-act-runner/config.yaml /home/wooo/awoooi-non110-runner/config.yaml /home/wooo/act-runner/config.yaml}" +RUNNER_BINARY_PATHS="${RUNNER_BINARY_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/act_runner ${RUNNER_HOME}/act-runner-awoooi/act_runner /home/wooo/act-runner-awoooi/act_runner /home/wooo/awoooi-act-runner/act_runner /home/wooo/awoooi-non110-runner/act_runner /home/wooo/act-runner/act_runner}" +RUNNER_DOCKER_IMAGES="${RUNNER_DOCKER_IMAGES:-gitea/act_runner:latest}" +RUNNER_REGISTRATION_PATHS="${RUNNER_REGISTRATION_PATHS:-${RUNNER_HOME}/awoooi-non110-runner/data/.runner ${RUNNER_HOME}/awoooi-non110-runner/.runner ${RUNNER_HOME}/act-runner-awoooi/.runner /home/wooo/act-runner-awoooi/.runner /home/wooo/awoooi-act-runner/.runner /home/wooo/awoooi-non110-runner/.runner /home/wooo/act-runner/.runner}" RUNNER_SERVICE_NAMES="${RUNNER_SERVICE_NAMES:-awoooi-non110-runner.service gitea-act-runner-awoooi.service gitea-act-runner-host.service}" ALLOWED_LABEL_NAMES="${ALLOWED_LABEL_NAMES:-awoooi-non110-host awoooi-non110-ubuntu awoooi-host awoooi-ubuntu}" FORBIDDEN_LABEL_RE="${FORBIDDEN_LABEL_RE:-^(ubuntu-latest|ubuntu-[0-9].*|self-hosted|stockplatform.*|stock-platform.*|headless.*|playwright.*)$}" @@ -244,8 +246,8 @@ check_configs() { } check_binaries() { - section "runner binary metadata" - local binary kind binary_ok + section "runner runtime metadata" + local binary image kind binary_ok image_ok for binary in $RUNNER_BINARY_PATHS; do binary_ok=0 if [ -x "$binary" ] && [ -f "$binary" ]; then @@ -258,8 +260,24 @@ check_binaries() { printf 'RUNNER_BINARY path=%s executable=%s kind=%s\n' "$binary" "$binary_ok" "$kind" done + if command_exists docker; then + for image in $RUNNER_DOCKER_IMAGES; do + image_ok=0 + if docker image inspect "$image" >/dev/null 2>&1; then + image_ok=1 + kind="$(docker image inspect "$image" --format '{{.Id}} {{.Created}}' 2>/dev/null || echo present)" + READY_BINARY_COUNT=$((READY_BINARY_COUNT + 1)) + else + kind="missing" + fi + printf 'RUNNER_DOCKER_IMAGE image=%s present=%s kind=%s\n' "$image" "$image_ok" "$kind" + done + else + printf 'RUNNER_DOCKER_IMAGE docker_available=0\n' + fi + if [ "$READY_BINARY_COUNT" -eq 0 ]; then - blocker "runner_binary_missing" + blocker "runner_runtime_missing" fi } diff --git a/ops/runner/install-awoooi-non110-user-runner.sh b/ops/runner/install-awoooi-non110-user-runner.sh new file mode 100755 index 00000000..fa8c4a23 --- /dev/null +++ b/ops/runner/install-awoooi-non110-user-runner.sh @@ -0,0 +1,209 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Controlled prepare-only installer for the 192.168.0.188 user-level AWOOOI +# Gitea runner. It never reads, prints, stores, or passes runner registration +# tokens. Registration must happen through an external credentialed channel. + +TARGET_HOST_IP="${TARGET_HOST_IP:-192.168.0.188}" +FORBIDDEN_HOST_IPS="${FORBIDDEN_HOST_IPS:-192.168.0.110}" +RUNNER_DIR="${RUNNER_DIR:-${HOME}/awoooi-non110-runner}" +DATA_DIR="${DATA_DIR:-${RUNNER_DIR}/data}" +SERVICE_NAME="${SERVICE_NAME:-awoooi-non110-runner.service}" +ROLLBACK_SERVICE_NAME="${ROLLBACK_SERVICE_NAME:-awoooi-non110-runner-rollback.service}" +RUNNER_CONTAINER_NAME="${RUNNER_CONTAINER_NAME:-awoooi-non110-runner}" +ACT_RUNNER_IMAGE="${ACT_RUNNER_IMAGE:-gitea/act_runner:latest}" +RUNNER_LABELS="${RUNNER_LABELS:-awoooi-non110-host:host,awoooi-non110-ubuntu:docker://192.168.0.110:5000/awoooi/ci-runner:act-22.04}" +REGISTER_NOW="${REGISTER_NOW:-0}" +START_NOW="${START_NOW:-0}" +PREPARE_ONLY="${PREPARE_ONLY:-0}" + +if [ "$PREPARE_ONLY" = "1" ]; then + REGISTER_NOW=0 + START_NOW=0 +fi + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +host_ips() { + if command_exists ip; then + ip -o -4 addr show 2>/dev/null | awk '{print $4}' | sed 's#/.*##' | sort -u + return 0 + fi + hostname -I 2>/dev/null | tr ' ' '\n' | awk 'NF' | sort -u || true +} + +host_has_ip() { + local ip="$1" + host_ips | grep -qx "$ip" +} + +active_action_containers() { + docker ps --format '{{.Names}}' 2>/dev/null | grep -Ec '^GITEA-ACTIONS-TASK-' || true +} + +heavy_process_count() { + { + pgrep -f '(^|/)(chrome|chromium|chromium-browser)( |$)' 2>/dev/null || true + pgrep -f 'playwright|stockplatform.*smoke|next build|turbo build|vite build' 2>/dev/null || true + } | sort -u | wc -l | tr -d ' ' +} + +write_config() { + local config_file="${DATA_DIR}/config.yaml" + mkdir -p "$DATA_DIR" + umask 077 + cat >"$config_file" <"${user_unit_dir}/${SERVICE_NAME}" <"${user_unit_dir}/${ROLLBACK_SERVICE_NAME}" </dev/null 2>&1 || docker pull "$ACT_RUNNER_IMAGE" + +if [ "$(active_action_containers)" != "0" ]; then + echo "BLOCKER active_action_containers_present" + exit 1 +fi + +if [ "$(heavy_process_count)" != "0" ]; then + echo "BLOCKER heavy_processes_present" + exit 1 +fi + +mkdir -p "$RUNNER_DIR" "$DATA_DIR" +write_config +write_user_units + +if [ "$REGISTER_NOW" = "1" ]; then + register_runner +else + echo "RUNNER_REGISTRATION skipped=true content_read=false" +fi + +if [ "$START_NOW" = "1" ]; then + systemctl --user enable "$SERVICE_NAME" >/dev/null + systemctl --user restart "$SERVICE_NAME" +else + echo "RUNNER_START skipped=true" +fi + +echo "AWOOOI_NON110_USER_RUNNER_INSTALL_OK prepare_only=${PREPARE_ONLY} register_now=${REGISTER_NOW} start_now=${START_NOW}"