Merge pull request #33 from daedalist/ci-workflow #6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Pull Request CI | ||
| on: | ||
| pull_request: | ||
| branches: [ main ] | ||
| types: [opened, synchronize, reopened] | ||
| # Cancel in-progress runs when a new push is made | ||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| jobs: | ||
| # Install and cache dependencies | ||
| setup: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| cache: 'npm' | ||
| - name: Install dependencies | ||
| run: npm ci | ||
| - name: Cache dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: node_modules | ||
| key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }} | ||
| # Lint the code | ||
| lint: | ||
| runs-on: ubuntu-latest | ||
| needs: setup | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| cache: 'npm' | ||
| - name: Restore dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: node_modules | ||
| key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }} | ||
| - name: Install dependencies if cache miss | ||
| run: npm ci | ||
| - name: Run ESLint | ||
| run: npm run lint | ||
| # Build the application | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| needs: setup | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| cache: 'npm' | ||
| - name: Restore dependencies | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: node_modules | ||
| key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }} | ||
| - name: Install dependencies if cache miss | ||
| run: npm ci | ||
| - name: Build Next.js app | ||
| run: npm run build | ||
| - name: Upload build artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: build-output | ||
| path: ./build | ||
| retention-days: 1 | ||
| # Test deployment (verify build can be served) | ||
| test-deployment: | ||
| runs-on: ubuntu-latest | ||
| needs: build | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| - name: Download build artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: build-output | ||
| path: ./build | ||
| - name: Verify build contents | ||
| run: | | ||
| echo "Build directory contents:" | ||
| ls -la ./build/ | ||
| # Check if index.html exists | ||
| if [ ! -f "./build/index.html" ]; then | ||
| echo "❌ index.html not found in build directory" | ||
| exit 1 | ||
| fi | ||
| echo "✅ Build files verified" | ||
| - name: Test static file serving | ||
| run: | | ||
| # Start serve in background and capture output | ||
| echo "Starting server..." | ||
| npx --yes serve@latest ./build -l 8080 > server.log 2>&1 & | ||
| SERVER_PID=$! | ||
| echo "Server PID: $SERVER_PID" | ||
| # Wait for server to start with better detection | ||
| echo "Waiting for server to start..." | ||
| for i in {1..30}; do | ||
| if curl -s -f http://localhost:8080/ > /dev/null 2>&1; then | ||
| echo "✅ Server is responding after ${i} seconds" | ||
| break | ||
| fi | ||
| if [ $i -eq 30 ]; then | ||
| echo "❌ Server failed to start within 30 seconds" | ||
| echo "Server log:" | ||
| cat server.log | ||
| echo "Process status:" | ||
| ps aux | grep -E "(serve|node)" || echo "No serve/node processes found" | ||
| kill $SERVER_PID 2>/dev/null || true | ||
| exit 1 | ||
| fi | ||
| echo "Attempt $i/30: Server not ready yet, waiting..." | ||
| sleep 1 | ||
| done | ||
| # Test specific endpoints | ||
| echo "Testing specific files..." | ||
| # Test index.html | ||
| if curl -s -f http://localhost:8080/index.html > /dev/null; then | ||
| echo "✅ index.html accessible" | ||
| else | ||
| echo "❌ index.html not accessible" | ||
| echo "Server log:" | ||
| cat server.log | ||
| kill $SERVER_PID 2>/dev/null || true | ||
| exit 1 | ||
| fi | ||
| # Test root path | ||
| if curl -s -f http://localhost:8080/ > /dev/null; then | ||
| echo "✅ Root path accessible" | ||
| else | ||
| echo "❌ Root path not accessible" | ||
| echo "Server log:" | ||
| cat server.log | ||
| kill $SERVER_PID 2>/dev/null || true | ||
| exit 1 | ||
| fi | ||
| echo "✅ All deployment tests passed" | ||
| # Clean up | ||
| kill $SERVER_PID 2>/dev/null || true | ||
| wait $SERVER_PID 2>/dev/null || true | ||
| # Security audit | ||
| security: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '18' | ||
| cache: 'npm' | ||
| - name: Install dependencies | ||
| run: npm ci | ||
| - name: Run security audit | ||
| run: npm audit --audit-level high | ||
| - name: Check for known vulnerabilities | ||
| run: npm audit --audit-level moderate | ||
| # All checks must pass | ||
| all-checks: | ||
| runs-on: ubuntu-latest | ||
| needs: [lint, test, build, test-deployment, security] | ||
| if: always() | ||
| steps: | ||
| - name: Check all jobs | ||
| run: | | ||
| if [[ "${{ needs.lint.result }}" != "success" ]]; then | ||
| echo "❌ Linting failed" | ||
| exit 1 | ||
| fi | ||
| if [[ "${{ needs.test.result }}" != "success" ]]; then | ||
| echo "❌ Tests failed" | ||
| exit 1 | ||
| fi | ||
| if [[ "${{ needs.build.result }}" != "success" ]]; then | ||
| echo "❌ Build failed" | ||
| exit 1 | ||
| fi | ||
| if [[ "${{ needs.test-deployment.result }}" != "success" ]]; then | ||
| echo "❌ Deployment test failed" | ||
| exit 1 | ||
| fi | ||
| if [[ "${{ needs.security.result }}" != "success" ]]; then | ||
| echo "❌ Security audit failed" | ||
| exit 1 | ||
| fi | ||
| echo "✅ All checks passed!" | ||