name: Quality Checks on: push: branches: [ main, development, 'feature/**' ] pull_request: branches: [ main, development ] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read checks: write env: GO_VERSION: '1.25.6' NODE_VERSION: '24.12.0' jobs: backend-quality: name: Backend (Go) runs-on: ubuntu-latest steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Set up Go uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 with: go-version: ${{ env.GO_VERSION }} cache-dependency-path: backend/go.sum - name: Repo health check run: | bash scripts/repo_health_check.sh - name: Run Go tests id: go-tests working-directory: ${{ github.workspace }} env: CGO_ENABLED: 1 run: | bash scripts/go-test-coverage.sh 2>&1 | tee backend/test-output.txt exit ${PIPESTATUS[0]} - name: Go Test Summary if: always() working-directory: backend run: | echo "## 🔧 Backend Test Results" >> $GITHUB_STEP_SUMMARY if [ "${{ steps.go-tests.outcome }}" == "success" ]; then echo "✅ **All tests passed**" >> $GITHUB_STEP_SUMMARY PASS_COUNT=$(grep -c "^--- PASS" test-output.txt || echo "0") echo "- Tests passed: $PASS_COUNT" >> $GITHUB_STEP_SUMMARY else echo "❌ **Tests failed**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Failed Tests:" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY grep -E "^--- FAIL|FAIL\s+github" test-output.txt || echo "See logs for details" grep -E "^--- FAIL|FAIL\s+github" test-output.txt >> $GITHUB_STEP_SUMMARY || echo "See logs for details" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY fi # Codecov upload moved to `codecov-upload.yml` which is push-only. - name: Run golangci-lint uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: version: latest working-directory: backend args: --timeout=5m continue-on-error: true - name: Run Perf Asserts working-directory: backend env: # Conservative defaults to avoid flakiness on CI; tune as necessary PERF_MAX_MS_GETSTATUS_P95: 500ms PERF_MAX_MS_GETSTATUS_P95_PARALLEL: 1500ms PERF_MAX_MS_LISTDECISIONS_P95: 2000ms run: | echo "## 🔍 Running performance assertions (TestPerf)" >> $GITHUB_STEP_SUMMARY go test -run TestPerf -v ./internal/api/handlers -count=1 | tee perf-output.txt exit ${PIPESTATUS[0]} frontend-quality: name: Frontend (React) runs-on: ubuntu-latest steps: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 with: fetch-depth: 0 - name: Repo health check run: | bash scripts/repo_health_check.sh - name: Set up Node.js uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Check if frontend was modified in PR id: check-frontend run: | if [ "${{ github.event_name }}" = "push" ]; then echo "frontend_changed=true" >> $GITHUB_OUTPUT exit 0 fi # Try to fetch the PR base ref. This may fail for forked PRs or other cases. git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1 || true # Compute changed files against the PR base ref, fallback to origin/main, then fallback to last 10 commits CHANGED=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD 2>/dev/null || echo "") echo "Changed files (base ref):\n$CHANGED" if [ -z "$CHANGED" ]; then echo "Base ref diff empty or failed; fetching origin/main for fallback..." git fetch origin main --depth=1 || true CHANGED=$(git diff --name-only origin/main...HEAD 2>/dev/null || echo "") echo "Changed files (main fallback):\n$CHANGED" fi if [ -z "$CHANGED" ]; then echo "Still empty; falling back to diffing last 10 commits from HEAD..." CHANGED=$(git diff --name-only HEAD~10...HEAD 2>/dev/null || echo "") echo "Changed files (HEAD~10 fallback):\n$CHANGED" fi if echo "$CHANGED" | grep -q '^frontend/'; then echo "frontend_changed=true" >> $GITHUB_OUTPUT else echo "frontend_changed=false" >> $GITHUB_OUTPUT fi - name: Install dependencies working-directory: frontend if: ${{ github.event_name == 'push' || steps.check-frontend.outputs.frontend_changed == 'true' }} run: npm ci - name: Run frontend tests and coverage id: frontend-tests working-directory: ${{ github.workspace }} if: ${{ github.event_name == 'push' || steps.check-frontend.outputs.frontend_changed == 'true' }} run: | bash scripts/frontend-test-coverage.sh 2>&1 | tee frontend/test-output.txt exit ${PIPESTATUS[0]} - name: Frontend Test Summary if: always() working-directory: frontend run: | echo "## ⚛️ Frontend Test Results" >> $GITHUB_STEP_SUMMARY if [ "${{ steps.frontend-tests.outcome }}" == "success" ]; then echo "✅ **All tests passed**" >> $GITHUB_STEP_SUMMARY # Extract test counts from vitest output if grep -q "Tests:" test-output.txt; then grep "Tests:" test-output.txt | tail -1 >> $GITHUB_STEP_SUMMARY fi else echo "❌ **Tests failed**" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Failed Tests:" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY # Extract failed test info from vitest output grep -E "FAIL|✕|×|AssertionError|Error:" test-output.txt | head -30 >> $GITHUB_STEP_SUMMARY || echo "See logs for details" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY fi # Codecov upload moved to `codecov-upload.yml` which is push-only. - name: Run frontend lint working-directory: frontend run: npm run lint continue-on-error: true