feat: add runner conversion scripts and strengthen cutover automation
This commit is contained in:
476
runners-conversion/periodVault/test-infra-runners.sh
Executable file
476
runners-conversion/periodVault/test-infra-runners.sh
Executable file
@@ -0,0 +1,476 @@
|
||||
#!/usr/bin/env bash
|
||||
# test-infra-runners.sh — Integration tests for self-hosted CI runner infrastructure.
|
||||
#
|
||||
# Tests cover:
|
||||
# 1. Shell script syntax (bash -n) for all infrastructure scripts
|
||||
# 2. runner.sh argument parsing and help output
|
||||
# 3. setup.sh cross-platform dispatch logic
|
||||
# 4. Docker image builds (slim + full) with content verification
|
||||
# 5. Docker Compose configuration validation
|
||||
# 6. ci.yml runner variable expression syntax
|
||||
# 7. lib.sh headless emulator function structure
|
||||
# 8. entrypoint.sh env validation logic
|
||||
#
|
||||
# Usage: ./scripts/test-infra-runners.sh [--skip-docker]
|
||||
#
|
||||
# --skip-docker Skip Docker image build tests (useful in CI without Docker)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
PASS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
SKIP_COUNT=0
|
||||
SKIP_DOCKER=false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
log() { echo "[test-infra] $*"; }
|
||||
pass() { PASS_COUNT=$((PASS_COUNT + 1)); log "PASS: $*"; }
|
||||
fail() { FAIL_COUNT=$((FAIL_COUNT + 1)); log "FAIL: $*"; }
|
||||
skip() { SKIP_COUNT=$((SKIP_COUNT + 1)); log "SKIP: $*"; }
|
||||
|
||||
assert_file_exists() {
|
||||
local path="$1" label="$2"
|
||||
if [[ -f "$path" ]]; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label — file not found: $path"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_file_executable() {
|
||||
local path="$1" label="$2"
|
||||
if [[ -x "$path" ]]; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label — not executable: $path"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1" needle="$2" label="$3"
|
||||
if echo "$haystack" | grep -qF -- "$needle"; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label — expected to contain: $needle"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_contains() {
|
||||
local haystack="$1" needle="$2" label="$3"
|
||||
if ! echo "$haystack" | grep -qF -- "$needle"; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label — should NOT contain: $needle"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_exit_code() {
|
||||
local expected="$1" label="$2"
|
||||
shift 2
|
||||
local actual
|
||||
set +e
|
||||
"$@" >/dev/null 2>&1
|
||||
actual=$?
|
||||
set -e
|
||||
if [[ "$actual" -eq "$expected" ]]; then
|
||||
pass "$label"
|
||||
else
|
||||
fail "$label — expected exit $expected, got $actual"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Parse args
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--skip-docker) SKIP_DOCKER=true; shift ;;
|
||||
*) echo "Unknown arg: $1"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ===========================================================================
|
||||
# Section 1: File existence and permissions
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 1: File existence and permissions ==="
|
||||
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/Dockerfile" "Dockerfile exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/docker-compose.yml" "docker-compose.yml exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/entrypoint.sh" "entrypoint.sh exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/.env.example" "env.example exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/envs/periodvault.env.example" "periodvault.env.example exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/.gitignore" ".gitignore exists"
|
||||
assert_file_exists "$PROJECT_ROOT/infra/runners/README.md" "runners README exists"
|
||||
assert_file_exists "$PROJECT_ROOT/scripts/runner.sh" "runner.sh exists"
|
||||
assert_file_exists "$PROJECT_ROOT/scripts/setup.sh" "setup.sh exists"
|
||||
assert_file_exists "$PROJECT_ROOT/.github/workflows/build-runner-image.yml" "build-runner-image workflow exists"
|
||||
|
||||
assert_file_executable "$PROJECT_ROOT/infra/runners/entrypoint.sh" "entrypoint.sh is executable"
|
||||
assert_file_executable "$PROJECT_ROOT/scripts/runner.sh" "runner.sh is executable"
|
||||
assert_file_executable "$PROJECT_ROOT/scripts/setup.sh" "setup.sh is executable"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 2: Shell script syntax validation (bash -n)
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 2: Shell script syntax ==="
|
||||
|
||||
for script in \
|
||||
"$PROJECT_ROOT/scripts/runner.sh" \
|
||||
"$PROJECT_ROOT/scripts/setup.sh" \
|
||||
"$PROJECT_ROOT/infra/runners/entrypoint.sh"; do
|
||||
name="$(basename "$script")"
|
||||
if bash -n "$script" 2>/dev/null; then
|
||||
pass "bash -n $name"
|
||||
else
|
||||
fail "bash -n $name — syntax error"
|
||||
fi
|
||||
done
|
||||
|
||||
# ===========================================================================
|
||||
# Section 3: runner.sh argument parsing
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 3: runner.sh argument parsing ==="
|
||||
|
||||
# --help should exit 0 and print usage
|
||||
HELP_OUT="$("$PROJECT_ROOT/scripts/runner.sh" --help 2>&1)" || true
|
||||
assert_contains "$HELP_OUT" "Usage:" "runner.sh --help shows usage"
|
||||
assert_contains "$HELP_OUT" "--mode" "runner.sh --help mentions --mode"
|
||||
assert_contains "$HELP_OUT" "build-image" "runner.sh --help mentions build-image"
|
||||
assert_exit_code 0 "runner.sh --help exits 0" "$PROJECT_ROOT/scripts/runner.sh" --help
|
||||
|
||||
# Missing --mode should fail
|
||||
assert_exit_code 1 "runner.sh without --mode exits 1" "$PROJECT_ROOT/scripts/runner.sh"
|
||||
|
||||
# Invalid mode should fail
|
||||
assert_exit_code 1 "runner.sh --mode invalid exits 1" "$PROJECT_ROOT/scripts/runner.sh" --mode invalid
|
||||
|
||||
# ===========================================================================
|
||||
# Section 4: setup.sh platform dispatch
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 4: setup.sh structure ==="
|
||||
|
||||
SETUP_CONTENT="$(cat "$PROJECT_ROOT/scripts/setup.sh")"
|
||||
assert_contains "$SETUP_CONTENT" "Darwin" "setup.sh handles macOS"
|
||||
assert_contains "$SETUP_CONTENT" "Linux" "setup.sh handles Linux"
|
||||
assert_contains "$SETUP_CONTENT" "setup-dev-environment.sh" "setup.sh dispatches to setup-dev-environment.sh"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 5: entrypoint.sh validation logic
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 5: entrypoint.sh structure ==="
|
||||
|
||||
ENTRY_CONTENT="$(cat "$PROJECT_ROOT/infra/runners/entrypoint.sh")"
|
||||
assert_contains "$ENTRY_CONTENT" "GITHUB_PAT" "entrypoint.sh validates GITHUB_PAT"
|
||||
assert_contains "$ENTRY_CONTENT" "REPO_URL" "entrypoint.sh validates REPO_URL"
|
||||
assert_contains "$ENTRY_CONTENT" "RUNNER_NAME" "entrypoint.sh validates RUNNER_NAME"
|
||||
assert_contains "$ENTRY_CONTENT" "--ephemeral" "entrypoint.sh uses ephemeral mode"
|
||||
assert_contains "$ENTRY_CONTENT" "trap cleanup" "entrypoint.sh traps for cleanup"
|
||||
assert_contains "$ENTRY_CONTENT" "registration-token" "entrypoint.sh generates registration token"
|
||||
assert_contains "$ENTRY_CONTENT" "remove-token" "entrypoint.sh handles removal token"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 6: Dockerfile structure
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 6: Dockerfile structure ==="
|
||||
|
||||
DOCKERFILE="$(cat "$PROJECT_ROOT/infra/runners/Dockerfile")"
|
||||
assert_contains "$DOCKERFILE" "FROM ubuntu:24.04 AS base" "Dockerfile has base stage"
|
||||
assert_contains "$DOCKERFILE" "FROM base AS slim" "Dockerfile has slim stage"
|
||||
assert_contains "$DOCKERFILE" "FROM slim AS full" "Dockerfile has full stage"
|
||||
assert_contains "$DOCKERFILE" "openjdk-17-jdk-headless" "Dockerfile installs JDK 17"
|
||||
assert_contains "$DOCKERFILE" "platforms;android-34" "Dockerfile installs Android SDK 34"
|
||||
assert_contains "$DOCKERFILE" "build-tools;34.0.0" "Dockerfile installs build-tools 34"
|
||||
assert_contains "$DOCKERFILE" "system-images;android-34;google_apis;x86_64" "Full stage includes system images"
|
||||
assert_contains "$DOCKERFILE" "avdmanager create avd" "Full stage pre-creates AVD"
|
||||
assert_contains "$DOCKERFILE" "kvm" "Full stage sets up KVM group"
|
||||
assert_contains "$DOCKERFILE" "HEALTHCHECK" "Dockerfile has HEALTHCHECK"
|
||||
assert_contains "$DOCKERFILE" "ENTRYPOINT" "Dockerfile has ENTRYPOINT"
|
||||
assert_contains "$DOCKERFILE" 'userdel -r ubuntu' "Dockerfile removes ubuntu user (GID 1000 conflict fix)"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 7: docker-compose.yml structure
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 7: docker-compose.yml structure ==="
|
||||
|
||||
COMPOSE="$(cat "$PROJECT_ROOT/infra/runners/docker-compose.yml")"
|
||||
assert_contains "$COMPOSE" "registry:" "Compose has registry service"
|
||||
assert_contains "$COMPOSE" "runner-slim-1:" "Compose has runner-slim-1"
|
||||
assert_contains "$COMPOSE" "runner-slim-2:" "Compose has runner-slim-2"
|
||||
assert_contains "$COMPOSE" "runner-emulator:" "Compose has runner-emulator"
|
||||
assert_contains "$COMPOSE" "registry:2" "Registry uses official image"
|
||||
assert_contains "$COMPOSE" "/dev/kvm" "Emulator gets KVM device"
|
||||
assert_contains "$COMPOSE" "no-new-privileges" "Security: no-new-privileges"
|
||||
assert_contains "$COMPOSE" "init: true" "Uses tini (init: true)"
|
||||
assert_contains "$COMPOSE" "stop_grace_period" "Emulator has stop grace period"
|
||||
assert_contains "$COMPOSE" "android-emulator" "Emulator runner has android-emulator label"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 8: ci.yml runner variable expressions
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 8: ci.yml runner variable expressions ==="
|
||||
|
||||
CI_YML="$(cat "$PROJECT_ROOT/.github/workflows/ci.yml")"
|
||||
assert_contains "$CI_YML" 'vars.CI_RUNS_ON_MACOS' "ci.yml uses CI_RUNS_ON_MACOS variable"
|
||||
assert_contains "$CI_YML" 'vars.CI_RUNS_ON_ANDROID' "ci.yml uses CI_RUNS_ON_ANDROID variable"
|
||||
assert_contains "$CI_YML" 'vars.CI_RUNS_ON ' "ci.yml uses CI_RUNS_ON variable"
|
||||
assert_contains "$CI_YML" 'fromJSON(' "ci.yml uses fromJSON() for runner targeting"
|
||||
|
||||
# Verify fallback values are present (safe default = current macOS runner)
|
||||
assert_contains "$CI_YML" '"self-hosted","macOS","periodvault"' "ci.yml has macOS fallback"
|
||||
|
||||
# Verify parallelism: test-ios-simulator should NOT depend on test-android-emulator
|
||||
# Extract test-ios-simulator needs line
|
||||
IOS_SECTION="$(awk '/test-ios-simulator:/,/runs-on:/' "$PROJECT_ROOT/.github/workflows/ci.yml")"
|
||||
assert_not_contains "$IOS_SECTION" "test-android-emulator" "test-ios-simulator does NOT depend on test-android-emulator (parallel)"
|
||||
assert_contains "$IOS_SECTION" "test-shared" "test-ios-simulator depends on test-shared"
|
||||
|
||||
# Verify audit-quality-gate waits for both platform tests
|
||||
AUDIT_SECTION="$(awk '/audit-quality-gate:/,/runs-on:/' "$PROJECT_ROOT/.github/workflows/ci.yml")"
|
||||
assert_contains "$AUDIT_SECTION" "test-android-emulator" "audit-quality-gate waits for android emulator"
|
||||
assert_contains "$AUDIT_SECTION" "test-ios-simulator" "audit-quality-gate waits for ios simulator"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 9: lib.sh headless emulator support
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 9: lib.sh headless emulator support ==="
|
||||
|
||||
LIB_SH="$(cat "$PROJECT_ROOT/scripts/lib.sh")"
|
||||
assert_contains "$LIB_SH" "start_emulator_headless()" "lib.sh defines start_emulator_headless()"
|
||||
assert_contains "$LIB_SH" "-no-window" "Headless emulator uses -no-window"
|
||||
assert_contains "$LIB_SH" "-no-audio" "Headless emulator uses -no-audio"
|
||||
assert_contains "$LIB_SH" "swiftshader_indirect" "Headless emulator uses swiftshader GPU"
|
||||
|
||||
# Verify OS-aware dispatch in ensure_android_emulator
|
||||
assert_contains "$LIB_SH" '"$(uname -s)" == "Linux"' "ensure_android_emulator detects Linux"
|
||||
assert_contains "$LIB_SH" 'start_emulator_headless' "ensure_android_emulator calls headless on Linux"
|
||||
assert_contains "$LIB_SH" 'start_emulator_windowed' "ensure_android_emulator calls windowed on macOS"
|
||||
|
||||
# Verify headless zombie kill is macOS-only
|
||||
ZOMBIE_LINE="$(grep -n 'is_emulator_headless' "$PROJECT_ROOT/scripts/lib.sh" | grep 'Darwin' || true)"
|
||||
if [[ -n "$ZOMBIE_LINE" ]]; then
|
||||
pass "Headless zombie kill is guarded by Darwin check"
|
||||
else
|
||||
fail "Headless zombie kill should be macOS-only (Darwin guard)"
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# Section 10: .gitignore protects secrets
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 10: .gitignore protects secrets ==="
|
||||
|
||||
GITIGNORE="$(cat "$PROJECT_ROOT/infra/runners/.gitignore")"
|
||||
assert_contains "$GITIGNORE" ".env" ".gitignore excludes .env"
|
||||
assert_contains "$GITIGNORE" "!.env.example" ".gitignore keeps .example files"
|
||||
|
||||
# ===========================================================================
|
||||
# Section 11: Docker image builds (requires Docker)
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 11: Docker image builds ==="
|
||||
|
||||
if $SKIP_DOCKER; then
|
||||
skip "Docker image build tests (--skip-docker)"
|
||||
elif ! command -v docker &>/dev/null; then
|
||||
skip "Docker image build tests (docker not found)"
|
||||
elif ! docker info >/dev/null 2>&1; then
|
||||
skip "Docker image build tests (docker daemon not running)"
|
||||
else
|
||||
DOCKER_PLATFORM="linux/amd64"
|
||||
|
||||
# --- Build slim ---
|
||||
log "Building slim image (this may take a few minutes)..."
|
||||
if docker build --platform "$DOCKER_PLATFORM" --target slim \
|
||||
-t periodvault-runner-test:slim "$PROJECT_ROOT/infra/runners/" >/dev/null 2>&1; then
|
||||
pass "Docker build: slim target succeeds"
|
||||
|
||||
# Verify slim image contents
|
||||
SLIM_JAVA="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
java -version 2>&1 | head -1)" || true
|
||||
if echo "$SLIM_JAVA" | grep -q "17"; then
|
||||
pass "Slim image: Java 17 is installed"
|
||||
else
|
||||
fail "Slim image: Java 17 not found — got: $SLIM_JAVA"
|
||||
fi
|
||||
|
||||
SLIM_SDK="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
bash -c 'ls $ANDROID_HOME/platforms/' 2>&1)" || true
|
||||
if echo "$SLIM_SDK" | grep -q "android-34"; then
|
||||
pass "Slim image: Android SDK 34 is installed"
|
||||
else
|
||||
fail "Slim image: Android SDK 34 not found — got: $SLIM_SDK"
|
||||
fi
|
||||
|
||||
SLIM_RUNNER="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
bash -c 'ls /home/runner/actions-runner/run.sh' 2>&1)" || true
|
||||
if echo "$SLIM_RUNNER" | grep -q "run.sh"; then
|
||||
pass "Slim image: GitHub Actions runner agent is installed"
|
||||
else
|
||||
fail "Slim image: runner agent not found"
|
||||
fi
|
||||
|
||||
SLIM_USER="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
whoami 2>&1)" || true
|
||||
if [[ "$SLIM_USER" == "runner" ]]; then
|
||||
pass "Slim image: runs as 'runner' user"
|
||||
else
|
||||
fail "Slim image: expected user 'runner', got '$SLIM_USER'"
|
||||
fi
|
||||
|
||||
SLIM_ENTRY="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
bash -c 'test -x /home/runner/entrypoint.sh && echo ok' 2>&1)" || true
|
||||
if [[ "$SLIM_ENTRY" == "ok" ]]; then
|
||||
pass "Slim image: entrypoint.sh is present and executable"
|
||||
else
|
||||
fail "Slim image: entrypoint.sh not executable"
|
||||
fi
|
||||
|
||||
# Verify slim does NOT have emulator
|
||||
SLIM_EMU="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:slim \
|
||||
bash -c 'command -v emulator || echo not-found' 2>&1)" || true
|
||||
if echo "$SLIM_EMU" | grep -q "not-found"; then
|
||||
pass "Slim image: does NOT include emulator (expected)"
|
||||
else
|
||||
fail "Slim image: unexpectedly contains emulator"
|
||||
fi
|
||||
else
|
||||
fail "Docker build: slim target failed"
|
||||
fi
|
||||
|
||||
# --- Build full ---
|
||||
log "Building full image (this may take several minutes)..."
|
||||
if docker build --platform "$DOCKER_PLATFORM" --target full \
|
||||
-t periodvault-runner-test:full "$PROJECT_ROOT/infra/runners/" >/dev/null 2>&1; then
|
||||
pass "Docker build: full target succeeds"
|
||||
|
||||
# Verify full image has emulator
|
||||
FULL_EMU="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:full \
|
||||
bash -c 'command -v emulator && echo found' 2>&1)" || true
|
||||
if echo "$FULL_EMU" | grep -q "found"; then
|
||||
pass "Full image: emulator is installed"
|
||||
else
|
||||
fail "Full image: emulator not found"
|
||||
fi
|
||||
|
||||
# Verify full image has AVD pre-created
|
||||
FULL_AVD="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:full \
|
||||
bash -c '${ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager list avd 2>/dev/null | grep "Name:" || echo none' 2>&1)" || true
|
||||
if echo "$FULL_AVD" | grep -q "phone"; then
|
||||
pass "Full image: AVD 'phone' is pre-created"
|
||||
else
|
||||
fail "Full image: AVD 'phone' not found — got: $FULL_AVD"
|
||||
fi
|
||||
|
||||
# Verify full image has system images
|
||||
FULL_SYSIMG="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:full \
|
||||
bash -c 'ls $ANDROID_HOME/system-images/android-34/google_apis/x86_64/ 2>/dev/null | head -1 || echo none' 2>&1)" || true
|
||||
if [[ "$FULL_SYSIMG" != "none" ]]; then
|
||||
pass "Full image: system-images;android-34;google_apis;x86_64 installed"
|
||||
else
|
||||
fail "Full image: system images not found"
|
||||
fi
|
||||
|
||||
# Verify full image has xvfb
|
||||
FULL_XVFB="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:full \
|
||||
bash -c 'command -v Xvfb && echo found || echo not-found' 2>&1)" || true
|
||||
if echo "$FULL_XVFB" | grep -q "found"; then
|
||||
pass "Full image: Xvfb is installed"
|
||||
else
|
||||
fail "Full image: Xvfb not found"
|
||||
fi
|
||||
|
||||
# Verify kvm group exists and runner is a member
|
||||
FULL_KVM="$(docker run --rm --platform "$DOCKER_PLATFORM" periodvault-runner-test:full \
|
||||
bash -c 'id runner 2>/dev/null' 2>&1)" || true
|
||||
if echo "$FULL_KVM" | grep -q "kvm"; then
|
||||
pass "Full image: runner user is in kvm group"
|
||||
else
|
||||
fail "Full image: runner not in kvm group — got: $FULL_KVM"
|
||||
fi
|
||||
else
|
||||
fail "Docker build: full target failed"
|
||||
fi
|
||||
|
||||
# --- Docker Compose validation ---
|
||||
log "Validating docker-compose.yml..."
|
||||
# Create temp env files for validation
|
||||
cp "$PROJECT_ROOT/infra/runners/.env.example" "$PROJECT_ROOT/infra/runners/.env"
|
||||
cp "$PROJECT_ROOT/infra/runners/envs/periodvault.env.example" "$PROJECT_ROOT/infra/runners/envs/periodvault.env"
|
||||
|
||||
if docker compose -f "$PROJECT_ROOT/infra/runners/docker-compose.yml" config --quiet 2>/dev/null; then
|
||||
pass "docker compose config validates"
|
||||
else
|
||||
fail "docker compose config failed"
|
||||
fi
|
||||
|
||||
# Verify compose defines expected services
|
||||
COMPOSE_SERVICES="$(docker compose -f "$PROJECT_ROOT/infra/runners/docker-compose.yml" config --services 2>/dev/null)"
|
||||
assert_contains "$COMPOSE_SERVICES" "registry" "Compose service: registry"
|
||||
assert_contains "$COMPOSE_SERVICES" "runner-slim-1" "Compose service: runner-slim-1"
|
||||
assert_contains "$COMPOSE_SERVICES" "runner-slim-2" "Compose service: runner-slim-2"
|
||||
assert_contains "$COMPOSE_SERVICES" "runner-emulator" "Compose service: runner-emulator"
|
||||
|
||||
# Clean up temp env files
|
||||
rm -f "$PROJECT_ROOT/infra/runners/.env" "$PROJECT_ROOT/infra/runners/envs/periodvault.env"
|
||||
|
||||
# --- Cleanup test images ---
|
||||
docker rmi periodvault-runner-test:slim periodvault-runner-test:full 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# Section 12: build-runner-image.yml workflow structure
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=== Section 12: build-runner-image.yml structure ==="
|
||||
|
||||
BUILD_WF="$(cat "$PROJECT_ROOT/.github/workflows/build-runner-image.yml")"
|
||||
assert_contains "$BUILD_WF" "slim" "Build workflow includes slim target"
|
||||
assert_contains "$BUILD_WF" "full" "Build workflow includes full target"
|
||||
assert_contains "$BUILD_WF" "matrix" "Build workflow uses matrix strategy"
|
||||
assert_contains "$BUILD_WF" "ghcr.io" "Build workflow pushes to GHCR"
|
||||
|
||||
# ===========================================================================
|
||||
# Results
|
||||
# ===========================================================================
|
||||
|
||||
log ""
|
||||
log "=============================="
|
||||
TOTAL=$((PASS_COUNT + FAIL_COUNT + SKIP_COUNT))
|
||||
log "Results: $PASS_COUNT passed, $FAIL_COUNT failed, $SKIP_COUNT skipped (total: $TOTAL)"
|
||||
log "=============================="
|
||||
|
||||
if [[ $FAIL_COUNT -gt 0 ]]; then
|
||||
log "FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "ALL PASSED"
|
||||
exit 0
|
||||
Reference in New Issue
Block a user