Replace CI PHP suite execution script with Composer commands and integrate Edge Gateway Agent stack artifacts

This commit is contained in:
Jeppe Bundgaard
2026-04-22 12:03:08 +02:00
parent a842edea97
commit 9cfb19a3ba
26 changed files with 1722 additions and 487 deletions
-184
View File
@@ -1,184 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
suite="${1:-}"
if [[ -z "$suite" ]]; then
echo "Usage: $0 <unit|integration|api>" >&2
exit 1
fi
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$repo_root"
if [[ -f .env ]]; then
set -a
# shellcheck disable=SC1091
. ./.env
set +a
fi
export AUTO_COMPOSER_INSTALL=false
cleanup() {
docker compose down -v --remove-orphans || true
}
wait_for_container_running() {
local name="$1"
local status=""
for _ in $(seq 1 90); do
status="$(docker inspect -f '{{.State.Status}}' "$name" 2>/dev/null || true)"
if [[ "$status" == "running" ]]; then
return 0
fi
sleep 1
done
docker logs "$name" || true
echo "Container $name did not reach running state." >&2
exit 1
}
wait_for_container_healthy() {
local name="$1"
local status=""
for _ in $(seq 1 90); do
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$name" 2>/dev/null || true)"
if [[ "$status" == "healthy" || "$status" == "running" ]]; then
return 0
fi
sleep 2
done
docker logs "$name" || true
echo "Container $name did not reach a healthy state." >&2
exit 1
}
docker_exec_php1() {
docker exec "$@" php1 sh -lc "cd /var/www/html && ${PHP_COMMAND}"
}
run_php_command() {
local -a env_args=()
while [[ $# -gt 0 ]]; do
env_args+=(-e "$1")
shift
done
docker exec "${env_args[@]}" php1 sh -lc "cd /var/www/html && ${PHP_COMMAND}"
}
sync_php_app_into_container() {
docker exec php1 sh -lc 'mkdir -p /var/www/html && find /var/www/html -mindepth 1 -maxdepth 1 -exec rm -rf {} +'
docker cp "${repo_root}/services/nginx/app/." php1:/var/www/html/
}
install_php_dependencies() {
PHP_COMMAND="composer install --no-interaction --prefer-dist"
docker_exec_php1
docker exec php1 sh -lc '
module_dir="/var/www/html/modules/washcertificates"
if [ -f "$module_dir/composer.json" ]; then
cd "$module_dir"
composer install --no-interaction --prefer-dist
fi
'
}
docker compose down -v --remove-orphans || true
trap cleanup EXIT
services=(redis edge-broker php1)
if [[ "$suite" != "unit" ]]; then
services+=(mysql-debug)
fi
docker compose up -d --build "${services[@]}"
wait_for_container_running php1
wait_for_container_healthy redis
if [[ "$suite" != "unit" ]]; then
wait_for_container_healthy mysql-debug
fi
sync_php_app_into_container
install_php_dependencies
case "$suite" in
unit)
PHP_COMMAND="composer test:unit"
docker_exec_php1
PHP_COMMAND="composer test:coverage"
docker_exec_php1
mkdir -p "${repo_root}/services/nginx/app/build/logs"
docker cp php1:/var/www/html/build/logs/clover.xml "${repo_root}/services/nginx/app/build/logs/clover.xml" 2>/dev/null || true
;;
integration)
PHP_COMMAND="composer test:integration"
run_php_command \
"RUN_INTEGRATION_TESTS=1" \
"USE_ENV=true" \
"DEBUG=0" \
"ENCRYPTION_KEY=test-key" \
"CORS=*" \
"CONFIG_TIMEZONE=Europe/Copenhagen" \
"ECONOMIC_API_APP_ACCESS_GRANT=test" \
"ECONOMIC_API_APP_ACCESS_GRANT2=test" \
"ECONOMIC_API_APP_SECRET_TOKEN=test" \
"WORDPRESS_STATIC_TOKEN=" \
"EMAIL_WASH_CERTIFICATE_TOKEN=" \
"WORDPRESS_API_URL=http://localhost" \
"MINIO_ENDPOINT=" \
"MINIO_ACCESS_KEY=" \
"MINIO_SECRET_KEY=" \
"SLACK_DEFAULT_WEBHOOK=" \
"REDIS_CONFIG_HOST=redis" \
"REDIS_CONFIG_DATABASE=0" \
"REDIS_CONFIG_PASSWORD=" \
"REDIS_CONFIG_PORT=6379" \
"CONFIG_DB_TARGET=debug" \
"CONFIG_DB_DEBUG_HOST=mysql-debug" \
"CONFIG_DB_DEBUG_USER=root" \
"CONFIG_DB_DEBUG_PASSWORD=${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}" \
"CONFIG_DB_DEBUG_DATABASE=${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}" \
"CONFIG_DB_DEBUG_PORT=3306"
;;
api)
PHP_COMMAND="composer test:api"
run_php_command \
"RUN_API_TESTS=1" \
"API_TEST_BOOTSTRAP_SCHEMA=1" \
"USE_ENV=true" \
"DEBUG=0" \
"ENCRYPTION_KEY=test-key" \
"CORS=*" \
"CONFIG_TIMEZONE=Europe/Copenhagen" \
"ECONOMIC_API_APP_ACCESS_GRANT=test" \
"ECONOMIC_API_APP_ACCESS_GRANT2=test" \
"ECONOMIC_API_APP_SECRET_TOKEN=test" \
"WORDPRESS_STATIC_TOKEN=" \
"EMAIL_WASH_CERTIFICATE_TOKEN=" \
"WORDPRESS_API_URL=http://localhost" \
"MINIO_ENDPOINT=" \
"MINIO_ACCESS_KEY=" \
"MINIO_SECRET_KEY=" \
"SLACK_DEFAULT_WEBHOOK=" \
"REDIS_CONFIG_HOST=redis" \
"REDIS_CONFIG_DATABASE=0" \
"REDIS_CONFIG_PASSWORD=" \
"REDIS_CONFIG_PORT=6379" \
"CONFIG_DB_TARGET=debug" \
"CONFIG_DB_DEBUG_HOST=mysql-debug" \
"CONFIG_DB_DEBUG_USER=root" \
"CONFIG_DB_DEBUG_PASSWORD=${CONFIG_DB_DEBUG_PASSWORD:-debug_root_password}" \
"CONFIG_DB_DEBUG_DATABASE=${CONFIG_DB_DEBUG_DATABASE:-nnks_db_debug}" \
"CONFIG_DB_DEBUG_PORT=3306"
;;
*)
echo "Unsupported suite: $suite" >&2
exit 1
;;
esac
+16 -2
View File
@@ -14,6 +14,9 @@ export const DEFAULT_HOST_API_URL = "http://localhost/api";
export const DEFAULT_CONTAINER_API_URL = "http://caddy";
export const DEFAULT_CONTAINER_BROKER_URL = "http://edge-broker:4300";
export const DEFAULT_INSTALL_DIR = "/opt/truckwash-edge-agent";
export const DEFAULT_RUNTIME_DIR = `${DEFAULT_INSTALL_DIR}/runtime`;
export const DEFAULT_STATE_DATABASE_PATH = `${DEFAULT_RUNTIME_DIR}/gateway-state.sqlite`;
export const DEFAULT_STACK_SERVICE_NAME = "truckwash-edge-gateway-stack.service";
export const DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 15;
export const DEFAULT_INSTALLED_VERSION = "php-agent-v1";
const DEFAULT_COMPOSE_SERVICES = ["traefik", "redis", "mysql-debug", "edge-broker", "caddy"];
@@ -91,15 +94,24 @@ export function buildGatewayConfig({
installedVersion,
targetVersion,
installDir: DEFAULT_INSTALL_DIR,
runtimeDir: DEFAULT_RUNTIME_DIR,
stateDatabasePath: DEFAULT_STATE_DATABASE_PATH,
agentPath: `${DEFAULT_INSTALL_DIR}/agent.php`,
lanWorkerPath: `${DEFAULT_INSTALL_DIR}/lan-worker.php`,
serviceUnitPath: `${DEFAULT_INSTALL_DIR}/truckwash-edge-agent.service`,
stackServiceUnitPath: `${DEFAULT_INSTALL_DIR}/${DEFAULT_STACK_SERVICE_NAME}`,
serviceName: String(existingConfig.serviceName || hostname || DEFAULT_CONTAINER_NAME),
stackServiceName: String(existingConfig.stackServiceName || DEFAULT_STACK_SERVICE_NAME),
composeFileName: String(existingConfig.composeFileName || "docker-compose.gateway.yml"),
composeProjectName: String(existingConfig.composeProjectName || "truckwash-edge-gateway"),
launcherScriptName: String(existingConfig.launcherScriptName || "gateway-launcher.sh"),
workerBaseUrl: String(existingConfig.workerBaseUrl || "http://lan-worker:8090"),
updateWindow: String(existingConfig.updateWindow || "02:00-04:00"),
runtimeMode: String(existingConfig.runtimeMode || "compose"),
restartMode: "spawn",
heartbeatIntervalSeconds,
commandPollTimeoutSeconds: Number(existingConfig.commandPollTimeoutSeconds || 20),
shellActionPollTimeoutSeconds: Number(existingConfig.shellActionPollTimeoutSeconds || 20),
commandPollRetryDelayMs: Number(existingConfig.commandPollRetryDelayMs || 1000),
shellActionPollRetryDelayMs: Number(existingConfig.shellActionPollRetryDelayMs || 1000),
brokerReconnectDelayMs: Number(existingConfig.brokerReconnectDelayMs || 1500),
};
}
@@ -387,8 +399,10 @@ async function printStatus({ imageTag, configDir, containerName }) {
configFilePath,
gatewayId: config.gatewayId ?? null,
installDir: config.installDir ?? null,
runtimeDir: config.runtimeDir ?? null,
agentPath: config.agentPath ?? null,
serviceUnitPath: config.serviceUnitPath ?? null,
stackServiceUnitPath: config.stackServiceUnitPath ?? null,
containerStatus: container?.State?.Status ?? "missing",
running: Boolean(container?.State?.Running),
image: container?.Config?.Image ?? imageTag,