Luigit
repositories / bugabinga.net

bugabinga.net

personal infrastructure for bugabinga!

owned by admin

scripts/local/luci-manual-e2e.sh

Raw
#!/usr/bin/env bash
set -euo pipefail

ROOT="$(mktemp -d /tmp/luci-manual-XXXXXX)"
LUCI="${LUCI_BIN:-${ROOT}/luci}"
HTTP_ADDR="${LUCI_HTTP_ADDR:-127.0.0.1:18080}"
CHROME="${CHROME:-}"
PODMAN_SOCKET="${LUCI_PODMAN_SOCKET:-unix://${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/podman/podman.sock}"
PODMAN_PID=""
LUCI_PID=""

cleanup() {
  if [[ -n "${LUCI_PID}" ]]; then
    kill "${LUCI_PID}" 2>/dev/null || true
  fi
  if [[ -n "${PODMAN_PID}" ]]; then
    kill "${PODMAN_PID}" 2>/dev/null || true
  fi
  echo "manual e2e root: ${ROOT}"
}
trap cleanup EXIT

find_chrome() {
  if [[ -n "${CHROME}" ]]; then
    printf '%s\n' "${CHROME}"
    return
  fi
  for candidate in google-chrome-stable google-chrome chromium chromium-browser /opt/google/chrome/chrome; do
    if command -v "${candidate}" >/dev/null 2>&1 || [[ -x "${candidate}" ]]; then
      printf '%s\n' "${candidate}"
      return
    fi
  done
  echo "Chrome/Chromium not found" >&2
  exit 1
}

ensure_podman_socket() {
  local socket_path="${PODMAN_SOCKET#unix://}"
  if [[ -S "${socket_path}" ]]; then
    return
  fi
  mkdir -p "$(dirname "${socket_path}")"
  podman system service --time=0 "${PODMAN_SOCKET}" >"${ROOT}/podman-service.log" 2>&1 &
  PODMAN_PID="$!"
  for _ in {1..50}; do
    [[ -S "${socket_path}" ]] && return
    sleep 0.1
  done
  cat "${ROOT}/podman-service.log" >&2 || true
  echo "Podman socket did not start: ${socket_path}" >&2
  exit 1
}

wait_http() {
  for _ in {1..80}; do
    curl -fsS "http://${HTTP_ADDR}/" >"${ROOT}/dashboard.html" && return
    sleep 0.1
  done
  echo "Luci HTTP did not start" >&2
  exit 1
}

wait_log() {
  local wanted="$1"
  for _ in {1..200}; do
    if find "${ROOT}/data/logs" -type f -name '*.zst' -print0 | xargs -0 -r zstdcat 2>/dev/null | grep -q "${wanted}"; then
      return
    fi
    sleep 0.1
  done
  echo "Luci log did not contain ${wanted}" >&2
  exit 1
}

build_luci() {
  if [[ -x "${LUCI}" ]]; then
    return
  fi
  (cd services/luci && go build -o "${LUCI}" ./cmd/luci)
}

write_repo() {
  local work="${ROOT}/work-repo"
  git init "${work}" >/dev/null
  git -C "${work}" config user.email test@example.invalid
  git -C "${work}" config user.name Test
  mkdir -p "${work}/.ci"
  cat >"${work}/.ci/ci.kdl" <<'KDL'
job "push-main" {
  image "docker.io/library/golang:1.26"
  run "git rev-parse --is-inside-work-tree | grep -q true"
  run "git ls-files | grep -qx README.md"
  run "echo PUSH_OK GIT_OK"
  trigger {
    push {
      branch "main"
    }
  }
}
job "push-path" {
  image "docker.io/library/alpine:latest"
  run "echo PATH_OK"
  trigger {
    push {
      branch "main"
      include "README.md"
    }
  }
}
job "push-ignore" {
  image "docker.io/library/alpine:latest"
  run "echo SHOULD_NOT_RUN"
  trigger {
    push {
      include "docs/**"
    }
  }
}
job "matrix" {
  image "docker.io/library/alpine:latest"
  run "echo MATRIX_${axis}"
  trigger {
    push {
      branch "main"
    }
  }
  matrix {
    axis "one" "two"
  }
}
job "tag-release" {
  image "docker.io/library/alpine:latest"
  run "echo TAG_OK"
  trigger {
    push {
      tag "v*"
    }
  }
}
job "scheduled" {
  image "docker.io/library/alpine:latest"
  run "echo SCHEDULE_OK"
  trigger {
    schedule "* * * * *"
  }
}
job "manual-rev" {
  image "docker.io/library/alpine:latest"
  run "grep -q second README.md && echo MANUAL_REV_OK"
}
job "manual-secret" {
  image "docker.io/library/alpine:latest"
  run "test $TOKEN = s3cr3t && test -f .secrets/key && echo MANUAL_SECRET_OK"
  secret "TOKEN" env="TOKEN"
  secret "KEY" file=".secrets/key"
}
KDL
  echo first >"${work}/README.md"
  git -C "${work}" add . >/dev/null
  git -C "${work}" commit -m initial >/dev/null
  git -C "${work}" branch -M main
  git clone --bare "${work}" "${ROOT}/repos/repo.git" >/dev/null 2>&1
  git -C "${work}" remote add origin "${ROOT}/repos/repo.git"
}

main() {
  local chrome
  chrome="$(find_chrome)"
  ensure_podman_socket
  build_luci
  mkdir -p "${ROOT}/repos"
  write_repo

  export LUCI_DATA_DIR="${ROOT}/data"
  export LUCI_REPO_ROOTS="${ROOT}/repos"
  export LUCI_HTTP_ADDR="${HTTP_ADDR}"
  export LUCI_BASE_URL="http://${HTTP_ADDR}"
  export LUCI_INBOX_DIR="${ROOT}/inbox"
  export LUCI_PODMAN_SOCKET="${PODMAN_SOCKET}"
  export LUCI_DEFAULT_MEMORY="512m"
  export LUCI_DEFAULT_CPUS="1"

  "${LUCI}" env-check >"${ROOT}/env-check.out"
  "${LUCI}" serve --once >"${ROOT}/baseline.out"

  echo second >"${ROOT}/work-repo/README.md"
  git -C "${ROOT}/work-repo" add README.md >/dev/null
  git -C "${ROOT}/work-repo" commit -m update >/dev/null
  local update_rev
  update_rev="$(git -C "${ROOT}/work-repo" rev-parse HEAD)"
  git -C "${ROOT}/work-repo" push origin main >/dev/null 2>&1
  "${LUCI}" serve --once >"${ROOT}/push.out"

  git -C "${ROOT}/work-repo" -c tag.gpgSign=false tag v1.0.0
  git -C "${ROOT}/work-repo" push origin v1.0.0 >/dev/null 2>&1
  "${LUCI}" serve --once >"${ROOT}/tag.out"

  USER=manual "${LUCI}" run repo manual-rev --rev "${update_rev}" >"${ROOT}/manual-rev-id.out"
  "${LUCI}" serve --once >"${ROOT}/manual-rev.out"

  mkdir -p "${ROOT}/data/secrets/repo/manual-secret"
  printf 's3cr3t\n' >"${ROOT}/data/secrets/repo/manual-secret/TOKEN"
  printf 'keydata\n' >"${ROOT}/data/secrets/repo/manual-secret/KEY"
  USER=manual "${LUCI}" run repo manual-secret --ref main >"${ROOT}/manual-id.out"
  "${LUCI}" serve --once >"${ROOT}/manual.out"
  SSH_ORIGINAL_COMMAND='ci env-check' "${LUCI}" ssh >"${ROOT}/ssh.out"

  "${LUCI}" serve >"${ROOT}/http.out" 2>"${ROOT}/http.err" &
  LUCI_PID="$!"
  wait_http
  wait_log SCHEDULE_OK
  curl -fsS "http://${HTTP_ADDR}/llms.txt" >"${ROOT}/llms.txt"

  local chrome_args=("${chrome}" --headless=new --no-sandbox --disable-gpu --user-data-dir="${ROOT}/chrome")
  "${chrome_args[@]}" --dump-dom "http://${HTTP_ADDR}/" >"${ROOT}/chrome-dashboard.html"
  "${chrome_args[@]}" --dump-dom "http://${HTTP_ADDR}/repos/repo" >"${ROOT}/chrome-repo.html"
  local run_id
  run_id="$(grep -o '/runs/[^"<]*' "${ROOT}/chrome-dashboard.html" | head -1 | cut -d/ -f3)"
  "${chrome_args[@]}" --dump-dom "http://${HTTP_ADDR}/runs/${run_id}" >"${ROOT}/chrome-run.html"
  "${chrome_args[@]}" --dump-dom "http://${HTTP_ADDR}/docs" >"${ROOT}/chrome-docs.html"
  "${chrome_args[@]}" --screenshot="${ROOT}/chrome-dashboard.png" "http://${HTTP_ADDR}/" >/dev/null

  : >"${ROOT}/combined.log"
  while IFS= read -r -d '' log; do
    zstdcat "${log}" >>"${ROOT}/combined.log"
  done < <(find "${ROOT}/data/logs" -type f -name '*.zst' -print0)
  for want in PUSH_OK GIT_OK PATH_OK MATRIX_one MATRIX_two TAG_OK MANUAL_REV_OK MANUAL_SECRET_OK SCHEDULE_OK; do
    grep -q "${want}" "${ROOT}/combined.log"
  done
  if grep -q SHOULD_NOT_RUN "${ROOT}/combined.log"; then
    echo 'excluded path job ran' >&2
    exit 1
  fi
  grep -q '<h1>repo</h1>' "${ROOT}/chrome-repo.html"
  grep -q 'Run ' "${ROOT}/chrome-run.html"
  grep -q 'Luci docs' "${ROOT}/chrome-docs.html"
  grep -q '# Luci CI' "${ROOT}/llms.txt"
  if grep -Eiq '<(form|button)\b' "${ROOT}"/chrome-{dashboard,repo,run,docs}.html; then
    echo 'mutating UI controls found' >&2
    exit 1
  fi
  local logs
  logs="$(find "${ROOT}/data/logs" -type f | wc -l)"
  if [[ "${logs}" != 8 ]]; then
    echo "unexpected log count: ${logs}, want 8" >&2
    exit 1
  fi

  printf 'ok: root=%s run=%s logs=%s\n' "${ROOT}" "${run_id}" "${logs}"
}

main "$@"