diff --git a/.github/workflows/quality-checks.yml b/.github/workflows/quality-checks.yml index 0f29833c..d911c461 100644 --- a/.github/workflows/quality-checks.yml +++ b/.github/workflows/quality-checks.yml @@ -1,17 +1,13 @@ name: Quality Checks on: - workflow_dispatch: - inputs: - run_frontend: - description: 'Run frontend checks' - required: false - default: true - type: boolean + push: + branches: [ main, development, 'feature/**' ] pull_request: + branches: [ main, development ] concurrency: - group: ${{ github.workflow }}-${{ github.ref_name }}-${{ github.run_id }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: @@ -19,10 +15,9 @@ permissions: checks: write env: - GO_VERSION: '1.25.7' + GO_VERSION: '1.25.6' NODE_VERSION: '24.12.0' GOTOOLCHAIN: auto - CHARON_MIN_COVERAGE: '85.0' jobs: backend-quality: @@ -30,8 +25,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - ref: ${{ github.sha }} - name: Set up Go uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 @@ -50,37 +43,37 @@ jobs: CGO_ENABLED: 1 run: | bash scripts/go-test-coverage.sh 2>&1 | tee backend/test-output.txt - exit "${PIPESTATUS[0]}" + exit ${PIPESTATUS[0]} - name: Go Test Summary if: always() working-directory: backend run: | - { - echo "## 🔧 Backend Test Results" - if [ "${{ steps.go-tests.outcome }}" == "success" ]; then - echo "✅ **All tests passed**" - PASS_COUNT=$(grep -c "^--- PASS" test-output.txt || echo "0") - echo "- Tests passed: $PASS_COUNT" - else - echo "❌ **Tests failed**" - echo "" - echo "### Failed Tests:" - echo '```' - grep -E "^--- FAIL|FAIL\s+github" test-output.txt || echo "See logs for details" - echo '```' - fi - } >> "$GITHUB_STEP_SUMMARY" + 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` (Docker Build-gated). + # 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: v2.8.0 + version: latest working-directory: backend - args: --config=.golangci-fast.yml --timeout=2m + args: --timeout=5m + continue-on-error: true - name: GORM Security Scanner id: gorm-scan @@ -92,26 +85,24 @@ jobs: - name: GORM Security Scan Summary if: always() run: | - { - echo "## 🔒 GORM Security Scan Results" - if [ "${{ steps.gorm-scan.outcome }}" == "success" ]; then - echo "✅ **No GORM security issues detected**" - echo "" - echo "All models follow secure GORM patterns:" - echo "- ✅ No exposed internal database IDs" - echo "- ✅ No exposed API keys or secrets" - echo "- ✅ Response DTOs properly structured" - else - echo "❌ **GORM security issues found**" - echo "" - echo "Run locally for details:" - echo '```bash' - echo "./scripts/scan-gorm-security.sh --report" - echo '```' - echo "" - echo "See [GORM Security Scanner docs](docs/implementation/gorm_security_scanner_complete.md) for remediation guidance." - fi - } >> "$GITHUB_STEP_SUMMARY" + echo "## 🔒 GORM Security Scan Results" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.gorm-scan.outcome }}" == "success" ]; then + echo "✅ **No GORM security issues detected**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "All models follow secure GORM patterns:" >> $GITHUB_STEP_SUMMARY + echo "- ✅ No exposed internal database IDs" >> $GITHUB_STEP_SUMMARY + echo "- ✅ No exposed API keys or secrets" >> $GITHUB_STEP_SUMMARY + echo "- ✅ Response DTOs properly structured" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **GORM security issues found**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Run locally for details:" >> $GITHUB_STEP_SUMMARY + echo '```bash' >> $GITHUB_STEP_SUMMARY + echo "./scripts/scan-gorm-security.sh --report" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "See [GORM Security Scanner docs](docs/implementation/gorm_security_scanner_complete.md) for remediation guidance." >> $GITHUB_STEP_SUMMARY + fi - name: Annotate GORM Security Issues if: failure() && steps.gorm-scan.outcome == 'failure' @@ -126,19 +117,17 @@ jobs: PERF_MAX_MS_GETSTATUS_P95_PARALLEL: 1500ms PERF_MAX_MS_LISTDECISIONS_P95: 2000ms run: | - echo "## 🔍 Running performance assertions (TestPerf)" >> "$GITHUB_STEP_SUMMARY" + 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]}" + exit ${PIPESTATUS[0]} frontend-quality: name: Frontend (React) runs-on: ubuntu-latest - if: ${{ inputs.run_frontend != false }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: fetch-depth: 0 - ref: ${{ github.sha }} - name: Repo health check run: | @@ -154,82 +143,71 @@ jobs: - name: Check if frontend was modified in PR id: check-frontend run: | - EVENT_NAME="${{ github.event_name }}" - BASE_REF="${{ github.event.pull_request.base.ref }}" - - if [ "$EVENT_NAME" = "push" ]; then - echo "frontend_changed=true" >> "$GITHUB_OUTPUT" + 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. - if [ -n "$BASE_REF" ]; then - git fetch origin "$BASE_REF" --depth=1 || true - fi + 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 - if [ -n "$BASE_REF" ]; then - CHANGED=$(git diff --name-only "origin/${BASE_REF}"...HEAD 2>/dev/null || echo "") - else - CHANGED="" - fi - printf 'Changed files (base ref):\n%s\n' "$CHANGED" + 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 "") - printf 'Changed files (main fallback):\n%s\n' "$CHANGED" + 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 "") - printf 'Changed files (HEAD~10 fallback):\n%s\n' "$CHANGED" + echo "Changed files (HEAD~10 fallback):\n$CHANGED" fi if echo "$CHANGED" | grep -q '^frontend/'; then - echo "frontend_changed=true" >> "$GITHUB_OUTPUT" + echo "frontend_changed=true" >> $GITHUB_OUTPUT else - echo "frontend_changed=false" >> "$GITHUB_OUTPUT" + echo "frontend_changed=false" >> $GITHUB_OUTPUT fi - name: Install dependencies working-directory: frontend - if: ${{ inputs.run_frontend != false && (github.event_name == 'workflow_dispatch' || steps.check-frontend.outputs.frontend_changed == 'true') }} + 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: ${{ inputs.run_frontend != false && (github.event_name == 'workflow_dispatch' || steps.check-frontend.outputs.frontend_changed == 'true') }} + 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]}" + exit ${PIPESTATUS[0]} - name: Frontend Test Summary if: always() working-directory: frontend run: | - { - echo "## ⚛️ Frontend Test Results" - if [ "${{ steps.frontend-tests.outcome }}" == "success" ]; then - echo "✅ **All tests passed**" - # Extract test counts from vitest output - if grep -q "Tests:" test-output.txt; then - grep "Tests:" test-output.txt | tail -1 - fi - else - echo "❌ **Tests failed**" - echo "" - echo "### Failed Tests:" - echo '```' - # Extract failed test info from vitest output - grep -E "FAIL|✕|×|AssertionError|Error:" test-output.txt | head -30 || echo "See logs for details" - echo '```' + 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 - } >> "$GITHUB_STEP_SUMMARY" + 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` (Docker Build-gated). + # Codecov upload moved to `codecov-upload.yml` which is push-only.