CI Pipeline #705
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: CI Pipeline | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| schedule: | |
| - cron: "0 2 * * *" | |
| workflow_dispatch: | |
| jobs: | |
| backend-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v3.14.1 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" | |
| cache: "maven" | |
| - name: Run backend lint (Checkstyle) | |
| run: | | |
| cd backend | |
| mvn checkstyle:check -Dcheckstyle.config.location=checkstyle.xml | |
| backend: | |
| runs-on: ubuntu-latest | |
| needs: backend-lint | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Clean Maven cache | |
| run: | | |
| rm -rf ~/.m2/repository | |
| rm -rf backend/target | |
| - name: Set up JDK 21 | |
| uses: actions/setup-java@v3.14.1 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" | |
| cache: "maven" | |
| # - name: Format code with Google Java Format ✨ | |
| # uses: axel-op/googlejavaformat-action@v3 | |
| # with: | |
| # args: --set-exit-if-changed | |
| # files: backend/**/*.java | |
| - name: Verify with maven and generate coverage report | |
| env: | |
| TEST_TURNSTILE_SECRET_KEY: ${{ secrets.TEST_TURNSTILE_SECRET_KEY }} | |
| run: | | |
| cd backend | |
| touch .env | |
| mvn -Dspring.profiles.active=test clean verify jacoco:report | |
| - name: Upload JaCoCo coverage report as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: jacoco-coverage-report | |
| path: backend/target/site/jacoco/ | |
| retention-days: 7 | |
| frontend-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3.9.1 | |
| with: | |
| node-version: "22" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4.1.0 | |
| with: | |
| version: "10.10.0" | |
| - name: Install dependencies | |
| run: | | |
| cd frontend | |
| pnpm install --frozen-lockfile | |
| - name: Run frontend lint | |
| run: | | |
| cd frontend | |
| rm -f cypress.config.d.ts | |
| rm -f vitest.config.d.ts | |
| pnpm exec eslint . --max-warnings=0 | |
| frontend: | |
| runs-on: ubuntu-latest | |
| needs: frontend-lint | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Clean frontend artifacts | |
| run: | | |
| rm -rf frontend/node_modules | |
| rm -rf frontend/dist | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3.9.1 | |
| with: | |
| node-version: "22" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4.1.0 | |
| with: | |
| version: "10.10.0" | |
| - name: Install dependencies | |
| run: | | |
| cd frontend | |
| pnpm install --force --no-cache | |
| - name: Build frontend | |
| run: | | |
| cd frontend | |
| pnpm run build | |
| - name: Run tests | |
| run: | | |
| cd frontend | |
| pnpm run test:a11y | |
| pnpm run test:coverage -- --coverage.thresholds.lines=80 --coverage.thresholds.functions=80 --coverage.thresholds.branches=80 --coverage.thresholds.statements=80 | |
| - name: Upload frontend coverage report as artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: frontend-coverage-report | |
| path: frontend/coverage/ | |
| retention-days: 7 | |
| frontend-e2e: | |
| runs-on: ubuntu-latest | |
| needs: frontend | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3.9.1 | |
| with: | |
| node-version: "22" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4.1.0 | |
| with: | |
| version: "10.10.0" | |
| - name: Install dependencies | |
| run: | | |
| cd frontend | |
| pnpm install --frozen-lockfile | |
| - name: Build frontend for preview server | |
| run: | | |
| cd frontend | |
| pnpm run build | |
| - name: Run Cypress E2E tests | |
| run: | | |
| cd frontend | |
| pnpm run test:e2e | |
| frontend-e2e-fullstack-smoke: | |
| runs-on: ubuntu-latest | |
| needs: [backend, frontend] | |
| if: github.event_name == 'pull_request' || github.event_name == 'push' | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3.9.1 | |
| with: | |
| node-version: "22" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4.1.0 | |
| with: | |
| version: "10.10.0" | |
| - name: Install frontend dependencies | |
| run: | | |
| cd frontend | |
| pnpm install --frozen-lockfile | |
| - name: Prepare E2E mail hook secret | |
| run: echo "E2E_MAIL_HOOK_SECRET=$(openssl rand -hex 16)" >> "$GITHUB_ENV" | |
| - name: Write backend/.env for Docker Compose | |
| run: | | |
| { | |
| echo "SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/krisefikser" | |
| echo "SPRING_DATASOURCE_USERNAME=root" | |
| echo "SPRING_DATASOURCE_PASSWORD=root" | |
| echo "JWT_SECRET=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | |
| echo "TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA" | |
| echo "FRONTEND_URL=http://127.0.0.1:5173" | |
| echo "MAILTRAP_API_TOKEN=unused-e2e-mail-captured-in-memory" | |
| echo "E2E_MAIL_HOOK_SECRET=${E2E_MAIL_HOOK_SECRET}" | |
| } > backend/.env | |
| - name: Build frontend | |
| env: | |
| VITE_API_URL: http://127.0.0.1:8080 | |
| VITE_WS_URL: ws://127.0.0.1:8080/ws | |
| VITE_TURNSTILE_SITE_KEY: 1x00000000000000000000AA | |
| run: | | |
| cd frontend | |
| pnpm run build | |
| - name: Start database and backend | |
| env: | |
| AUTH_ADMIN_TWO_FACTOR_ENABLED: "false" | |
| SPRING_PROFILES_ACTIVE: dev,e2e | |
| run: | | |
| docker compose up -d db backend | |
| for i in {1..60}; do | |
| STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/auth/me || true) | |
| if [ "$STATUS_CODE" = "401" ] || [ "$STATUS_CODE" = "200" ]; then | |
| echo "Backend is healthy (HTTP $STATUS_CODE)" | |
| exit 0 | |
| fi | |
| echo "Waiting for backend (HTTP $STATUS_CODE)..." | |
| sleep 5 | |
| done | |
| echo "Backend did not become healthy in time" | |
| docker compose logs backend | |
| exit 1 | |
| - name: Run full-stack smoke E2E | |
| env: | |
| CYPRESS_e2eMailHookSecret: ${{ env.E2E_MAIL_HOOK_SECRET }} | |
| run: | | |
| cd frontend | |
| pnpm run test:e2e:fullstack:smoke | |
| frontend-e2e-fullstack-regression: | |
| runs-on: ubuntu-latest | |
| needs: [frontend] | |
| if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' | |
| steps: | |
| - uses: actions/checkout@v4.2.2 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v3.9.1 | |
| with: | |
| node-version: "22" | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4.1.0 | |
| with: | |
| version: "10.10.0" | |
| - name: Install frontend dependencies | |
| run: | | |
| cd frontend | |
| pnpm install --frozen-lockfile | |
| - name: Prepare E2E mail hook secret | |
| run: echo "E2E_MAIL_HOOK_SECRET=$(openssl rand -hex 16)" >> "$GITHUB_ENV" | |
| - name: Write backend/.env for Docker Compose | |
| run: | | |
| { | |
| echo "SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/krisefikser" | |
| echo "SPRING_DATASOURCE_USERNAME=root" | |
| echo "SPRING_DATASOURCE_PASSWORD=root" | |
| echo "JWT_SECRET=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | |
| echo "TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA" | |
| echo "FRONTEND_URL=http://127.0.0.1:5173" | |
| echo "MAILTRAP_API_TOKEN=unused-e2e-mail-captured-in-memory" | |
| echo "E2E_MAIL_HOOK_SECRET=${E2E_MAIL_HOOK_SECRET}" | |
| } > backend/.env | |
| - name: Build frontend with E2E coverage instrumentation | |
| env: | |
| CYPRESS_COVERAGE: "true" | |
| VITE_API_URL: http://127.0.0.1:8080 | |
| VITE_WS_URL: ws://127.0.0.1:8080/ws | |
| VITE_TURNSTILE_SITE_KEY: 1x00000000000000000000AA | |
| run: | | |
| cd frontend | |
| pnpm run build:coverage | |
| - name: Start database and backend | |
| env: | |
| AUTH_ADMIN_TWO_FACTOR_ENABLED: "false" | |
| SPRING_PROFILES_ACTIVE: dev,e2e | |
| run: | | |
| docker compose up -d db backend | |
| for i in {1..60}; do | |
| STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/api/auth/me || true) | |
| if [ "$STATUS_CODE" = "401" ] || [ "$STATUS_CODE" = "200" ]; then | |
| echo "Backend is healthy (HTTP $STATUS_CODE)" | |
| exit 0 | |
| fi | |
| echo "Waiting for backend (HTTP $STATUS_CODE)..." | |
| sleep 5 | |
| done | |
| echo "Backend did not become healthy in time" | |
| docker compose logs backend | |
| exit 1 | |
| - name: Run full-stack regression E2E | |
| env: | |
| CYPRESS_COVERAGE: 'true' | |
| CYPRESS_e2eMailHookSecret: ${{ env.E2E_MAIL_HOOK_SECRET }} | |
| run: | | |
| cd frontend | |
| pnpm run test:e2e:fullstack:regression | |
| - name: Generate E2E coverage report | |
| if: success() || failure() | |
| run: | | |
| cd frontend | |
| if [ -d .nyc_output ] && [ -n "$(ls -A .nyc_output 2>/dev/null)" ]; then | |
| pnpm exec nyc report --report-dir coverage-e2e --reporter=lcov --reporter=html --reporter=text-summary | |
| else | |
| echo "No .nyc_output; skipping nyc report" | |
| fi | |
| - name: Upload E2E coverage report | |
| if: success() || failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: frontend-e2e-coverage-report | |
| path: frontend/coverage-e2e/ | |
| retention-days: 7 | |
| if-no-files-found: ignore |