ci(backend): add combined coverage report generation to CI workflow #22
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: Azure App Service CI/CD | |
| on: | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/backend.yml" | |
| - "src/Directory.Build.props" | |
| - "src/Directory.Packages.props" | |
| - "src/global.json" | |
| - "src/backend/services/TaskAgent/src/**/*.cs" | |
| - "src/backend/services/TaskAgent/src/**/*.csproj" | |
| - "src/backend/services/TaskAgent/src/**/*.json" | |
| - "src/backend/services/TaskAgent/src/**/*.cshtml" | |
| - "src/backend/services/TaskAgent/src/**/*.css" | |
| - "src/backend/services/TaskAgent/src/**/*.js" | |
| - "src/backend/services/TaskAgent/tests/**/*.cs" | |
| - "src/backend/services/TaskAgent/tests/**/*.csproj" | |
| - "src/backend/TaskAgent.ServiceDefaults/**/*.cs" | |
| - "src/backend/TaskAgent.ServiceDefaults/**/*.csproj" | |
| - "src/backend/TaskAgentWeb.sln" | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - "src/backend/**" | |
| - "!src/backend/**/*.md" | |
| env: | |
| AZURE_WEBAPP_NAME: app-taskagent-prod | |
| AZURE_WEBAPP_PACKAGE_PATH: src/backend/services/TaskAgent/src/TaskAgent.WebApi/published | |
| CONFIGURATION: Release | |
| DOTNET_CORE_VERSION: 10.0.x | |
| WORKING_DIRECTORY: src/backend/services/TaskAgent/src/TaskAgent.WebApi | |
| SOLUTION_PATH: src/backend/TaskAgentWeb.sln | |
| TESTS_DIRECTORY: src/backend/services/TaskAgent/tests | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| name: Test | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Setup .NET SDK | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_CORE_VERSION }} | |
| - name: Restore Solution | |
| run: dotnet restore "${{ env.SOLUTION_PATH }}" | |
| - name: Build Test Projects | |
| run: dotnet build "${{ env.SOLUTION_PATH }}" --configuration ${{ env.CONFIGURATION }} --no-restore | |
| - name: Run Unit Tests with Coverage | |
| run: | | |
| dotnet test "${{ env.TESTS_DIRECTORY }}/TaskAgent.Domain.UnitTests" \ | |
| --configuration ${{ env.CONFIGURATION }} \ | |
| --no-build \ | |
| --logger "trx;LogFileName=domain-tests.trx" \ | |
| --collect:"XPlat Code Coverage" \ | |
| --results-directory ./TestResults/Domain | |
| dotnet test "${{ env.TESTS_DIRECTORY }}/TaskAgent.Application.UnitTests" \ | |
| --configuration ${{ env.CONFIGURATION }} \ | |
| --no-build \ | |
| --logger "trx;LogFileName=application-tests.trx" \ | |
| --collect:"XPlat Code Coverage" \ | |
| --results-directory ./TestResults/Application | |
| - name: Run Integration Tests | |
| run: | | |
| dotnet test "${{ env.TESTS_DIRECTORY }}/TaskAgent.Infrastructure.IntegrationTests" \ | |
| --configuration ${{ env.CONFIGURATION }} \ | |
| --no-build \ | |
| --logger "trx;LogFileName=integration-tests.trx" \ | |
| --collect:"XPlat Code Coverage" \ | |
| --results-directory ./TestResults/Infrastructure | |
| - name: Generate Combined Coverage Report | |
| if: ${{ !cancelled() }} | |
| run: | | |
| dotnet tool install -g dotnet-reportgenerator-globaltool | |
| reportgenerator \ | |
| -reports:"./TestResults/**/coverage.cobertura.xml" \ | |
| -targetdir:"./TestResults/CoverageReport" \ | |
| -reporttypes:"Html;JsonSummary;Cobertura" | |
| - name: Generate Test Summary | |
| if: ${{ !cancelled() }} | |
| run: | | |
| echo "## 🧪 Backend Test Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Count tests from TRX files | |
| echo "### 📊 Test Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Project | Tests | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|---------|-------|--------|" >> $GITHUB_STEP_SUMMARY | |
| # Domain Tests | |
| if [ -f "./TestResults/Domain/domain-tests.trx" ]; then | |
| DOMAIN_TOTAL=$(grep -oP 'total="\K[0-9]+' ./TestResults/Domain/domain-tests.trx | head -1 || echo "0") | |
| DOMAIN_PASSED=$(grep -oP 'passed="\K[0-9]+' ./TestResults/Domain/domain-tests.trx | head -1 || echo "0") | |
| echo "| Domain.UnitTests | $DOMAIN_PASSED/$DOMAIN_TOTAL | ✅ |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Domain.UnitTests | - | ⚠️ |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Application Tests | |
| if [ -f "./TestResults/Application/application-tests.trx" ]; then | |
| APP_TOTAL=$(grep -oP 'total="\K[0-9]+' ./TestResults/Application/application-tests.trx | head -1 || echo "0") | |
| APP_PASSED=$(grep -oP 'passed="\K[0-9]+' ./TestResults/Application/application-tests.trx | head -1 || echo "0") | |
| echo "| Application.UnitTests | $APP_PASSED/$APP_TOTAL | ✅ |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Application.UnitTests | - | ⚠️ |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # Infrastructure Tests | |
| if [ -f "./TestResults/Infrastructure/integration-tests.trx" ]; then | |
| INFRA_TOTAL=$(grep -oP 'total="\K[0-9]+' ./TestResults/Infrastructure/integration-tests.trx | head -1 || echo "0") | |
| INFRA_PASSED=$(grep -oP 'passed="\K[0-9]+' ./TestResults/Infrastructure/integration-tests.trx | head -1 || echo "0") | |
| echo "| Infrastructure.IntegrationTests | $INFRA_PASSED/$INFRA_TOTAL | ✅ |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "| Infrastructure.IntegrationTests | - | ⚠️ |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Coverage Summary from ReportGenerator JSON | |
| echo "### 📈 Coverage Summary (Combined)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -f "./TestResults/CoverageReport/Summary.json" ]; then | |
| echo "| Metric | Coverage |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|----------|" >> $GITHUB_STEP_SUMMARY | |
| LINE_COV=$(cat ./TestResults/CoverageReport/Summary.json | jq -r '.summary.linecoverage // 0') | |
| BRANCH_COV=$(cat ./TestResults/CoverageReport/Summary.json | jq -r '.summary.branchcoverage // 0') | |
| METHOD_COV=$(cat ./TestResults/CoverageReport/Summary.json | jq -r '.summary.methodcoverage // 0') | |
| echo "| Lines | ${LINE_COV}% |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Branches | ${BRANCH_COV}% |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Methods | ${METHOD_COV}% |" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "⚠️ Coverage summary not available" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "📥 **Download Reports:**" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`backend-test-results\` - TRX test results" >> $GITHUB_STEP_SUMMARY | |
| echo "- \`backend-coverage\` - HTML coverage report" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| - name: Upload Test Results | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: backend-test-results | |
| path: ./TestResults/**/**.trx | |
| retention-days: 30 | |
| - name: Upload Coverage Reports | |
| uses: actions/upload-artifact@v4 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: backend-coverage | |
| path: ./TestResults/CoverageReport/ | |
| retention-days: 30 | |
| build: | |
| runs-on: ubuntu-latest | |
| needs: test | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| name: Build | |
| steps: | |
| - uses: actions/checkout@v5 | |
| - name: Setup .NET SDK | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: ${{ env.DOTNET_CORE_VERSION }} | |
| - name: Restore | |
| run: dotnet restore "${{ env.WORKING_DIRECTORY }}" | |
| - name: Build | |
| run: dotnet build "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-restore | |
| - name: Publish | |
| run: dotnet publish "${{ env.WORKING_DIRECTORY }}" --configuration ${{ env.CONFIGURATION }} --no-build --output "${{ env.AZURE_WEBAPP_PACKAGE_PATH }}" | |
| - name: Publish Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: webapp | |
| path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} | |
| deploy: | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| name: Deploy | |
| steps: | |
| - name: Download artifact from build job | |
| uses: actions/download-artifact@v5 | |
| with: | |
| name: webapp | |
| path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} | |
| - name: Azure Login | |
| uses: azure/login@v2 | |
| with: | |
| creds: ${{ secrets.app_taskagent_prod_SPN }} | |
| - name: Deploy to Azure WebApp | |
| uses: azure/webapps-deploy@v2 | |
| with: | |
| app-name: ${{ env.AZURE_WEBAPP_NAME }} | |
| package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} |