name: Test, Build & Validate on: push: branches: - main - develop pull_request: branches: - main - develop jobs: test-and-validate: runs-on: ubuntu-latest strategy: matrix: node-version: [20] steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' - name: Install dependencies run: npm ci - name: Run linter run: npm run lint || true continue-on-error: true - name: Run tests run: npm run test:ci env: CI: true - name: Generate coverage report run: npm run test:coverage continue-on-error: true - name: Check coverage threshold run: | COVERAGE=$(npm run test:coverage 2>&1 | grep -oP 'statements.*?(\d+\.\d+)%' | grep -oP '\d+\.\d+') echo "Coverage: ${COVERAGE}%" if (( $(echo "$COVERAGE < 90" | bc -l) )); then echo "❌ Coverage below 90%: ${COVERAGE}%" exit 1 fi echo "✅ Coverage meets threshold: ${COVERAGE}%" - name: Build Next.js application run: npm run build env: NEXT_PUBLIC_API_URL: ${{ secrets.TEST_API_URL || 'http://localhost:8000/api' }} - name: Check build output run: | if [ ! -d ".next" ]; then echo "❌ Build failed - .next directory not found" exit 1 fi echo "✅ Build successful" - name: Validate bundle size run: | echo "Build artifacts generated:" du -sh .next/ || true BUILD_SIZE=$(du -sb .next/ | awk '{print $1}') MAX_SIZE=$((250 * 1024 * 1024)) if [ $BUILD_SIZE -gt $MAX_SIZE ]; then echo "❌ Build size ($((BUILD_SIZE / 1024 / 1024))MB) exceeds limit (250MB)" exit 1 fi echo "✅ Build size ($((BUILD_SIZE / 1024 / 1024))MB) within limits" - name: Test Docker build run: | docker build \ -t hosting-frontend:test \ --build-arg NODE_ENV=production \ . if [ $? -ne 0 ]; then echo "❌ Docker build failed" exit 1 fi echo "✅ Docker build successful" - name: Check Docker image size run: | IMAGE_SIZE=$(docker images hosting-frontend:test --format "{{.Size}}") echo "Docker image size: $IMAGE_SIZE" # Extract numeric value and unit SIZE_NUM=$(echo $IMAGE_SIZE | grep -oP '^\d+(\.\d+)?') SIZE_UNIT=$(echo $IMAGE_SIZE | grep -oP '[MG]B$') # Convert to MB for comparison if [[ $SIZE_UNIT == "GB" ]]; then SIZE_NUM=$(echo "$SIZE_NUM * 1024" | bc) fi if (( $(echo "$SIZE_NUM > 250" | bc -l) )); then echo "❌ Docker image size (${SIZE_NUM}${SIZE_UNIT}) exceeds 250MB limit" exit 1 fi echo "✅ Docker image size (${IMAGE_SIZE}) within limits" - name: Run Docker health check run: | docker run -d \ --name hosting-frontend-test \ -p 3000:3000 \ hosting-frontend:test sleep 5 # Test health endpoint if ! docker exec hosting-frontend-test curl -f http://localhost:3000/ > /dev/null 2>&1; then echo "❌ Docker container health check failed" docker logs hosting-frontend-test docker stop hosting-frontend-test exit 1 fi echo "✅ Docker container health check passed" docker stop hosting-frontend-test - name: Upload coverage reports if: always() run: | if [ -d "coverage" ]; then echo "Coverage report directory found" ls -la coverage/ || true fi - name: Summary if: always() run: | echo "=== CI/CD Validation Summary ===" echo "✅ Checkout complete" echo "✅ Dependencies installed" echo "✅ Tests executed" echo "✅ Coverage validated" echo "✅ Build successful" echo "✅ Bundle size checked" echo "✅ Docker build verified" echo "✅ Docker image size validated" echo "✅ Health checks passed" echo "" echo "Ready for deployment! 🚀"