Release v1.1.0: CI/CD parity + pipeline UI fixes #590
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 | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| jobs: | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| # with: | |
| # fetch-depth: 0 # Full history for SonarCloud | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: | | |
| 6.0.x | |
| 9.0.x | |
| # - name: Setup Java (for SonarScanner) | |
| # uses: actions/setup-java@v4 | |
| # with: | |
| # distribution: 'temurin' | |
| # java-version: '17' | |
| # - name: Install SonarScanner | |
| # run: dotnet tool install --global dotnet-sonarscanner | |
| - name: Restore dependencies | |
| run: dotnet restore session-sight.sln | |
| - name: Check formatting | |
| run: dotnet format whitespace session-sight.sln --verify-no-changes --verbosity diagnostic | |
| # - name: Begin SonarCloud analysis | |
| # if: env.SONAR_TOKEN != '' | |
| # env: | |
| # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| # run: | | |
| # dotnet sonarscanner begin \ | |
| # /k:"dwight000_session-sight" \ | |
| # /o:"dwight000" \ | |
| # /d:sonar.token="${SONAR_TOKEN}" \ | |
| # /d:sonar.host.url="https://sonarcloud.io" \ | |
| # /d:sonar.cs.opencover.reportsPaths="coverage/**/coverage.opencover.xml" \ | |
| # /d:sonar.exclusions="**/Migrations/**,**/obj/**,**/bin/**,plan/spike/**,infra/**" \ | |
| # /d:sonar.coverage.exclusions="**/Migrations/**,**/AIFoundryClientFactory.cs,**/DocumentIntelligenceParser.cs,**/AzureBlobDocumentStorage.cs,**/AgentLoopRunner.cs,**/DependencyInjection.cs,**/SessionSightDbContext.cs,**/SessionRepository.cs,**/PatientRepository.cs,**/ReviewRepository.cs,**/ProcessIncomingNoteFunction.cs,**/SearchIndexService.cs,**/SearchIndexInitializer.cs,**/EmbeddingService.cs,**/obj/**" | |
| # # Disabled: sonar.qualitygate.wait=true | |
| # # Free tier can't customize quality gate (stuck at 80% new code coverage). | |
| # # Instead we use "Check for high severity SonarCloud issues" step below | |
| # # which queries the API for BLOCKER/CRITICAL issues only. | |
| # # Re-enable this if we get a paid plan with custom quality gate. | |
| - name: Verify analyzer configuration | |
| run: | | |
| # Confirm Recommended mode is set (prevent regression) | |
| grep -q "AnalysisMode>Recommended" Directory.Build.props || (echo "AnalysisMode should be Recommended" && exit 1) | |
| grep -q "TreatWarningsAsErrors>true" Directory.Build.props || (echo "TreatWarningsAsErrors should be true" && exit 1) | |
| echo "Analyzer configuration verified: Recommended mode with warnings as errors" | |
| - name: Build | |
| run: dotnet build session-sight.sln --no-restore --configuration Release | |
| - name: Check for vulnerable packages | |
| run: | | |
| dotnet list session-sight.sln package --vulnerable --include-transitive 2>&1 | tee vuln-report.txt | |
| # Fail on High/Critical, warn on Moderate/Low | |
| if grep -qE "High|Critical" vuln-report.txt; then | |
| echo "::error::High/Critical vulnerabilities found!" | |
| cat vuln-report.txt | |
| exit 1 | |
| elif grep -q "has the following vulnerable packages" vuln-report.txt; then | |
| echo "::warning::Moderate/Low vulnerabilities found (review vuln-report.txt)" | |
| fi | |
| - name: Run security scan | |
| run: | | |
| dotnet tool restore | |
| dotnet security-scan session-sight.sln --excl-proj="*Tests*" 2>&1 | tee security-report.txt | |
| # Fail build if security issues found | |
| if grep -q "Found [1-9]" security-report.txt; then | |
| echo "::error::Security issues found - review security-report.txt" | |
| cat security-report.txt | |
| exit 1 | |
| fi | |
| # Single source of truth: run the same script used locally, with CI-specific settings. | |
| - name: Test + coverage gate (80%) | |
| run: COVERAGE_THRESHOLD=0.80 COVERAGE_THRESHOLD_PERCENT=80 COVERAGE_FORMATS=opencover,cobertura ./scripts/check-backend.sh | |
| # - name: End SonarCloud analysis | |
| # if: env.SONAR_TOKEN != '' | |
| # env: | |
| # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| # run: dotnet sonarscanner end /d:sonar.token="${SONAR_TOKEN}" | |
| # FREE TIER WORKAROUND: Since we can't customize SonarCloud's quality gate on the free plan | |
| # (it requires 80% coverage on new code which is too strict), we bypass the quality gate | |
| # and instead query the API directly for serious issues only. | |
| # This lets us catch serious problems while ignoring minor coverage shortfalls. | |
| # Severity levels: BLOCKER > CRITICAL > MAJOR (High) > MINOR > INFO | |
| # - name: Check for high severity SonarCloud issues | |
| # if: env.SONAR_TOKEN != '' | |
| # env: | |
| # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| # run: | | |
| # # Wait for SonarCloud to process the analysis (no qualitygate.wait means async upload) | |
| # echo "Waiting 5s for SonarCloud to process analysis..." | |
| # sleep 5 | |
| # | |
| # # Query SonarCloud API for BLOCKER, CRITICAL, and MAJOR (High) issues | |
| # RESPONSE=$(curl -s -u "${SONAR_TOKEN}:" \ | |
| # "https://sonarcloud.io/api/issues/search?componentKeys=dwight000_session-sight&severities=BLOCKER,CRITICAL,MAJOR&statuses=OPEN,CONFIRMED,REOPENED&ps=100") | |
| # | |
| # # Filter out excluded rules: | |
| # # - S7684: .NET config env vars in shell scripts (false positive) | |
| # # - S4830: --insecure in dev scripts (intentional for localhost self-signed certs) | |
| # # - S6819: role="img" on SVG (correct pattern for accessible charts) | |
| # EXCLUDED_RULES='["shelldre:S7684", "shell:S4830", "typescript:S6819"]' | |
| # FILTERED=$(echo "$RESPONSE" | jq --argjson excluded "$EXCLUDED_RULES" '{ | |
| # total: ([.issues[] | select(.rule as $r | $excluded | index($r) | not)] | length), | |
| # issues: [.issues[] | select(.rule as $r | $excluded | index($r) | not)] | |
| # }') | |
| # COUNT=$(echo "$FILTERED" | jq '.total') | |
| # echo "Found $COUNT high severity issues (BLOCKER/CRITICAL/MAJOR, excluding ${EXCLUDED_RULES})" | |
| # | |
| # if [[ "$COUNT" -gt 0 ]]; then | |
| # echo "::error::Found $COUNT high severity SonarCloud issues that must be fixed" | |
| # echo "" | |
| # echo "High severity issues:" | |
| # echo "$FILTERED" | jq -r '.issues[] | " \(.severity) [\(.type)]: \(.message)\n File: \(.component | split(":")[1]):\(.line)\n Rule: \(.rule)\n"' | |
| # exit 1 | |
| # fi | |
| - name: Upload coverage artifact | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: coverage-report | |
| path: coverage/report | |
| frontend-tests: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: src/SessionSight.Web | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: src/SessionSight.Web/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: TypeScript type check | |
| run: npx tsc --noEmit | |
| - name: Run Vitest tests with coverage (80%) | |
| run: COVERAGE_THRESHOLD=80 npx vitest run --coverage | |
| - name: Install Playwright browsers | |
| run: npx playwright install --with-deps chromium | |
| - name: Run Playwright smoke tests | |
| run: npx playwright test --project=chromium | |
| - name: Build | |
| run: npx vite build |