diff --git a/package.json b/package.json index e1ec4cc62..822b246b0 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "dev": "turbo dev --parallel", "docker:dev": "docker compose -f ./development/development.docker-compose.yml up", "docker:build": "docker build -t homarr:test .", - "docker:test": "docker build -t homarr:test . && docker run --rm -d --name homarr-test -p 7575:7575 homarr:test && echo 'Waiting for container to start...' && sleep 15 && (curl -sf http://localhost:7575 > /dev/null && echo '✅ Docker container is running and responding on port 7575' || echo '❌ Docker container failed health check') ; docker logs homarr-test 2>&1 | tail -30 ; docker stop homarr-test", + "docker:test": "bash scripts/docker-test.sh", + "docker:test:skip-build": "bash scripts/docker-test.sh --skip-build", "format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache", "format:fix": "turbo format --continue -- --write --cache --cache-location node_modules/.cache/.prettiercache", "lint": "turbo lint --continue -- --cache --cache-location node_modules/.cache/.eslintcache", diff --git a/scripts/docker-test.sh b/scripts/docker-test.sh new file mode 100755 index 000000000..ba9e0a377 --- /dev/null +++ b/scripts/docker-test.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash +# Docker build + run integration test +# Tests that the Docker image builds and starts successfully on the current architecture. +# Works on both x86_64 and arm64 without requiring multi-platform builders. +# +# Usage: ./scripts/docker-test.sh [--skip-build] + +set -euo pipefail + +IMAGE_NAME="homarr:test" +CONTAINER_NAME="homarr-docker-test" +PORT=7575 +HEALTH_ENDPOINT="/api/health/ready" +MAX_WAIT=60 +POLL_INTERVAL=3 + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +cleanup() { + if docker ps -q --filter "name=${CONTAINER_NAME}" | grep -q .; then + echo -e "${YELLOW}Cleaning up container...${NC}" + docker stop "${CONTAINER_NAME}" 2>/dev/null || true + fi + docker rm -f "${CONTAINER_NAME}" 2>/dev/null || true +} + +trap cleanup EXIT + +# Detect architecture +ARCH=$(uname -m) +echo "🏗️ Architecture: ${ARCH}" + +# Step 1: Build +if [ "${1:-}" != "--skip-build" ]; then + echo "🔨 Building Docker image..." + docker build -t "${IMAGE_NAME}" . || { + echo -e "${RED}❌ Docker build failed${NC}" + exit 1 + } + echo -e "${GREEN}✅ Docker build succeeded${NC}" +else + echo "⏭️ Skipping build (--skip-build)" +fi + +# Step 2: Start container +echo "🚀 Starting container..." +cleanup +docker run --rm -d \ + --name "${CONTAINER_NAME}" \ + -p "${PORT}:7575" \ + -e SECRET_ENCRYPTION_KEY="$(head -c 32 /dev/urandom | xxd -p | tr -d '\n')" \ + "${IMAGE_NAME}" + +# Step 3: Wait for health endpoint +echo "⏳ Waiting for health endpoint (max ${MAX_WAIT}s)..." +ELAPSED=0 +while [ $ELAPSED -lt $MAX_WAIT ]; do + if curl -sf "http://localhost:${PORT}${HEALTH_ENDPOINT}" > /dev/null 2>&1; then + echo -e "${GREEN}✅ Health check passed after ${ELAPSED}s${NC}" + break + fi + sleep $POLL_INTERVAL + ELAPSED=$((ELAPSED + POLL_INTERVAL)) +done + +if [ $ELAPSED -ge $MAX_WAIT ]; then + echo -e "${RED}❌ Health check failed after ${MAX_WAIT}s${NC}" + echo "--- Container logs ---" + docker logs "${CONTAINER_NAME}" 2>&1 | tail -50 + exit 1 +fi + +# Step 4: Verify key endpoints +echo "🔍 Verifying endpoints..." + +# Home page +HOME_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/") +if [ "$HOME_STATUS" = "200" ]; then + echo -e "${GREEN} ✅ Home page: ${HOME_STATUS}${NC}" +else + echo -e "${RED} ❌ Home page: ${HOME_STATUS}${NC}" + docker logs "${CONTAINER_NAME}" 2>&1 | tail -30 + exit 1 +fi + +# Health live +LIVE_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/api/health/live") +if [ "$LIVE_STATUS" = "200" ]; then + echo -e "${GREEN} ✅ Health live: ${LIVE_STATUS}${NC}" +else + echo -e "${RED} ❌ Health live: ${LIVE_STATUS}${NC}" + exit 1 +fi + +# Health ready +READY_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${PORT}/api/health/ready") +if [ "$READY_STATUS" = "200" ]; then + echo -e "${GREEN} ✅ Health ready: ${READY_STATUS}${NC}" +else + echo -e "${RED} ❌ Health ready: ${READY_STATUS}${NC}" + exit 1 +fi + +# Step 5: Verify WebSocket upgrade is possible (should get 426 without proper headers, or 101 with them) +WS_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Upgrade: websocket" \ + -H "Connection: Upgrade" \ + -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \ + -H "Sec-WebSocket-Version: 13" \ + "http://localhost:${PORT}/websockets" 2>/dev/null || echo "000") +if [ "$WS_STATUS" = "101" ] || [ "$WS_STATUS" = "426" ] || [ "$WS_STATUS" = "400" ]; then + echo -e "${GREEN} ✅ WebSocket endpoint reachable: ${WS_STATUS}${NC}" +else + echo -e "${YELLOW} ⚠️ WebSocket endpoint status: ${WS_STATUS} (may be expected behind nginx)${NC}" +fi + +# Step 6: Print container info +echo "" +echo "📊 Container info:" +docker inspect "${CONTAINER_NAME}" --format=' Image: {{.Config.Image}}' 2>/dev/null || true +docker inspect "${CONTAINER_NAME}" --format=' Platform: {{.Platform}}' 2>/dev/null || true +echo " Architecture: ${ARCH}" + +echo "" +echo -e "${GREEN}✅ All Docker tests passed!${NC}"