Files
picoclaw/scripts/run-integration-tests.sh

123 lines
3.3 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BASE_COMPOSE="$ROOT_DIR/integration/docker-compose.runner.yml"
SUITES_DIR="$ROOT_DIR/integration/suites"
if [[ ! -f "$BASE_COMPOSE" ]]; then
echo "missing base compose file: $BASE_COMPOSE" >&2
exit 1
fi
if [[ ! -d "$SUITES_DIR" ]]; then
echo "missing integration suites directory: $SUITES_DIR" >&2
exit 1
fi
sanitize_project_name() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-'
}
collect_suite_dirs() {
if [[ "$#" -gt 0 ]]; then
local suite
for suite in "$@"; do
printf '%s\n' "$SUITES_DIR/$suite"
done
return
fi
find "$SUITES_DIR" -mindepth 1 -maxdepth 1 -type d | sort
}
run_suite() {
local suite_dir="$1"
local suite_name
suite_name="$(basename "$suite_dir")"
local manifest="$suite_dir/suite.env"
if [[ ! -f "$manifest" ]]; then
echo "suite $suite_name is missing manifest: $manifest" >&2
return 1
fi
local compose_args=()
compose_args+=(--project-directory "$ROOT_DIR" -p "picoclaw-int-$(sanitize_project_name "$suite_name")")
compose_args+=(-f "$BASE_COMPOSE")
local compose_files=()
while IFS= read -r compose_file; do
compose_files+=("$compose_file")
compose_args+=(-f "$compose_file")
done < <(find "$suite_dir" -maxdepth 1 -type f \( -name 'docker-compose.yml' -o -name 'docker-compose.*.yml' \) | sort)
if [[ "${#compose_files[@]}" -eq 0 ]]; then
echo "suite $suite_name has no docker-compose file" >&2
return 1
fi
(
set -a
# shellcheck disable=SC1090
source "$manifest"
set +a
export INTEGRATION_REPO_ROOT="$ROOT_DIR"
: "${TEST_COMMAND:?suite $suite_name must define TEST_COMMAND in $manifest}"
runner_service="${RUNNER_SERVICE:-integration-runner}"
cleanup() {
docker compose "${compose_args[@]}" down -v --remove-orphans >/dev/null 2>&1 || true
}
trap cleanup EXIT
echo "==> [$suite_name] resolving services"
local services=()
while IFS= read -r service; do
services+=("$service")
done < <(docker compose "${compose_args[@]}" config --services)
local dependency_services=()
for service in "${services[@]}"; do
if [[ "$service" != "$runner_service" ]]; then
dependency_services+=("$service")
fi
done
if [[ "${#dependency_services[@]}" -gt 0 ]]; then
echo "==> [$suite_name] starting docker services: ${dependency_services[*]}"
docker compose "${compose_args[@]}" up -d --build --wait "${dependency_services[@]}"
fi
echo "==> [$suite_name] running: $TEST_COMMAND"
# integration-runner already uses `bash -c` as its entrypoint, so pass the
# suite command as a single argument for Bash to execute directly.
docker compose "${compose_args[@]}" run --rm "$runner_service" "$TEST_COMMAND"
)
}
main() {
local suite_dirs=()
while IFS= read -r suite_dir; do
suite_dirs+=("$suite_dir")
done < <(collect_suite_dirs "$@")
if [[ "${#suite_dirs[@]}" -eq 0 ]]; then
echo "no integration suites found" >&2
exit 1
fi
local suite_dir
for suite_dir in "${suite_dirs[@]}"; do
if [[ ! -d "$suite_dir" ]]; then
echo "unknown integration suite: $suite_dir" >&2
exit 1
fi
run_suite "$suite_dir"
done
}
main "$@"