# Deploy a Gitea Actions self-hosted runner to a Docker host.
#
# We currently run ONE central runner on ana-docker (gitea is local
# there and SSH-from-there to the rest of the fleet already works).
# This playbook is parameterized so the same file can stand up
# site-local runners later without copy-paste — see
# stacks/gitea-runner/README.md "Topology" for when to do that.
#
# Usage:
#   # Central runner (defaults match ana-docker)
#   scripts/elway ana-docker --playbook playbooks/deploy-gitea-runner.yaml
#
#   # Site-local runner — override identity at the CLI; the .env on
#   # the host carries the registration token + image pin.
#   scripts/elway nh3-docker --playbook playbooks/deploy-gitea-runner.yaml \
#     --var runner_name=nh3-docker-runner \
#     --var runner_labels=pfi-fleet,nh3-docker
#
#   scripts/elway esh-docker-vm --playbook playbooks/deploy-gitea-runner.yaml \
#     --var runner_name=esh-runner \
#     --var runner_labels=pfi-fleet,esh
#
# Prereqs on the target host:
#   - Docker + compose plugin
#   - traefik-net network exists (external)
#   - /opt/docker/{compose,conf} convention (any PFI host already has this)
#
# Prereqs in gitea (do once before the first run):
#   - Generate a registration token at the appropriate scope
#     (admin / org / repo). Paste into .env on the target host
#     under GITEA_RUNNER_REGISTRATION_TOKEN before this playbook runs
#     `docker compose up -d`. See stacks/gitea-runner/README.md.

vars:
  compose_dir: /opt/docker/compose/gitea-runner
  data_dir: /opt/docker/conf/gitea-runner/data
  # Optional overrides — leave blank to take whatever's already in the
  # host-side .env. Setting these here patches the .env in place,
  # which is how site-local runners get unique names/labels without
  # editing files on the host by hand.
  runner_name: ""
  runner_labels: ""

steps:
  # ── host-side directory prep ────────────────────────────────────────
  - name: Ensure compose dir exists
    shell: mkdir -p {{ compose_dir }}
    creates: "{{ compose_dir }}"

  - name: Ensure data dir exists
    shell: mkdir -p {{ data_dir }}
    creates: "{{ data_dir }}"

  # ── compose + config files ──────────────────────────────────────────
  - name: Upload compose.yaml
    upload:
      src: stacks/gitea-runner/compose.yaml
      dest: "{{ compose_dir }}/compose.yaml"
      mode: "0644"

  - name: Upload runner config.yaml
    upload:
      src: stacks/gitea-runner/conf/config.yaml
      dest: "{{ data_dir }}/config.yaml"
      mode: "0644"

  - name: Seed .env from template (only if absent)
    upload:
      src: stacks/gitea-runner/.env.example
      dest: "{{ compose_dir }}/.env"
      mode: "0644"
    when: "[ ! -f {{ compose_dir }}/.env ]"

  # ── optional CLI-driven identity overrides ──────────────────────────
  # If runner_name was passed as --var, patch the line in the host's
  # .env so subsequent compose-up uses it. Idempotent: changed_when
  # checks whether the value differs from what's already there.
  - name: Patch GITEA_RUNNER_NAME in .env (when --var runner_name=)
    shell: sed -i 's|^GITEA_RUNNER_NAME=.*|GITEA_RUNNER_NAME={{ runner_name }}|' {{ compose_dir }}/.env
    when: '[ -n "{{ runner_name }}" ]'
    changed_when: '[ -n "{{ runner_name }}" ] && ! grep -q "^GITEA_RUNNER_NAME={{ runner_name }}$" {{ compose_dir }}/.env'

  - name: Patch GITEA_RUNNER_LABELS in .env (when --var runner_labels=)
    shell: sed -i 's|^GITEA_RUNNER_LABELS=.*|GITEA_RUNNER_LABELS={{ runner_labels }}|' {{ compose_dir }}/.env
    when: '[ -n "{{ runner_labels }}" ]'
    changed_when: '[ -n "{{ runner_labels }}" ] && ! grep -q "^GITEA_RUNNER_LABELS={{ runner_labels }}$" {{ compose_dir }}/.env'

  # ── safety: surface missing registration token before bring-up ──────
  - name: Verify registration token is present (or runner is already registered)
    # Either the .env carries a non-empty token (first run) or
    # data_dir/.runner exists (already registered). If neither, the
    # container will start but spin in a registration loop — fail
    # fast with a useful message instead.
    shell: |
      if [ -f {{ data_dir }}/.runner ]; then
        echo "already registered (.runner present)"
        exit 0
      fi
      tok=$(grep -E '^GITEA_RUNNER_REGISTRATION_TOKEN=' {{ compose_dir }}/.env | cut -d= -f2-)
      if [ -z "$tok" ]; then
        echo "ERROR: no .runner cached and GITEA_RUNNER_REGISTRATION_TOKEN is empty in {{ compose_dir }}/.env"
        echo "Generate one at https://gitea.phasefinal.com/-/admin/actions/runners and paste it into the .env, then re-run."
        exit 1
      fi
      echo "registration token present"
    changed_when: "false"

  # ── bring up + wait for runner to come online ───────────────────────
  - name: docker compose up -d
    shell: cd {{ compose_dir }} && docker compose up -d

  - name: Wait for runner to register / start polling
    # The runner logs "Runner registered" on first start, then enters
    # a polling loop ("Listening for tasks" / "polling"). Either is a
    # success signal.
    shell: |
      for i in $(seq 1 30); do
        if docker logs gitea-runner 2>&1 | grep -qE "Runner registered|Listening for tasks|poll|Starting runner"; then
          exit 0
        fi
        sleep 2
      done
      echo "--- last 80 lines of gitea-runner logs ---"
      docker logs gitea-runner --tail 80
      exit 1
    changed_when: "false"

verify:
  - name: Container is running
    shell: docker inspect gitea-runner --format '{{.State.Status}}' | grep -q running
    changed_when: "false"

  - name: Runner credentials are cached on disk
    shell: test -f {{ data_dir }}/.runner
    changed_when: "false"

  - name: Runner can reach gitea (no TLS / auth errors in last 50 log lines)
    shell: '! docker logs gitea-runner --tail 50 2>&1 | grep -qiE "tls|x509|unauthorized|forbidden|connection refused"'
    changed_when: "false"
