-
Notifications
You must be signed in to change notification settings - Fork 0
Ci enable module coverage collection #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
PROBLEM: PR #329 pipeline not collecting coverage for domain modules - Coverage was only collected for integration/architecture tests - No per-module coverage isolation (all assemblies instrumented together) - Coverage validation logic embedded in 140-line inline script ROOT CAUSE: GitHub Actions uses workflow from BASE branch, not PR branch - PR #329 has coverage collection code but pipeline can't see it - Security feature: prevents PRs from modifying workflow to expose secrets - Solution: Merge workflow changes to master first CHANGES FROM PR #329 (commits 9789a32, 0ef104e, 2fe4895, 7d73642): 1. Module-based coverage collection (lines 267-395) - 8 modules: Users, Providers, Documents, ServiceCatalogs, Locations, SearchProviders, Shared, ApiService - Per-module runsettings with correct wildcard filters: [Module]* - Exclude pattern: [*.Tests*]*,[*Test*]*,[testhost]* - Removed --no-build to ensure test projects compile - Debug logging for file discovery and generation 2. Reusable validate-coverage action (.github/actions/validate-coverage/) - Extracted 140-line validation script to composite action - Multi-stage fallback: OpenCover → Cobertura → Direct XML analysis - Configurable threshold (70%) and strict/lenient mode - Comprehensive README with usage examples and troubleshooting 3. Coverage file standardization (lines 350-392) - Find coverage XMLs in GUID subdirectories - Copy to predictable names: {module}.opencover.xml - Summary section showing total files generated - Better error messages when files not found BENEFITS: ✅ Accurate per-module coverage (Shared: 31%, Users: 65%, Providers: 60%) ✅ Eliminates cross-module instrumentation pollution ✅ Reusable validation logic across workflows ✅ Clear debug output for troubleshooting ✅ Enables PR #329 to show coverage report TESTING: - Verified locally: coverage.opencover.xml generated successfully - Shared module: 2040/6575 lines covered (31.02%) - Filters working: only target module instrumented, tests excluded MIGRATION IMPACT: - Backward compatible: same env vars, same behavior - STRICT_COVERAGE still false (development mode) - Existing steps unchanged (build, integration tests, security scan) - New: module coverage loop + reusable action RELATED: - Fixes coverage reporting for PR #329 - Addresses code review suggestion (pr-validation.yml:787-803) - Required for Sprint 2 milestone: 70% coverage target CO-AUTHORED-BY: GitHub Copilot <[email protected]>
… zero PROBLEM: Coverage extraction fails for values like '.5' (0.5 without leading zero) - cut returns empty string for '.5' - Conditional only checks for '0', missing empty string case - Results in '.5' being treated as percentage instead of decimal SOLUTION: Extract integer part into variable and check both cases - Check both empty string and '0' for decimal format detection - Covers both '.5' (empty) and '0.5' (zero) formats EXAMPLES: - '.5' -> '50.0%' (fixed) - '0.833' -> '83.3%' (working) - '83.33' -> '83.33%' (working) Related: Code review feedback lines 164-177
|
Warning Rate limit exceeded@frigini has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 32 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (2)
WalkthroughThe changes introduce a new GitHub Action for validating code coverage with multi-stage fallback extraction, integrate it into the PR validation workflow, and refactor coverage collection with per-module processing, enhanced secret handling, and improved vulnerability scanning resilience. Changes
Sequence DiagramsequenceDiagram
participant WF as GitHub Workflow
participant CV as Coverage Validator Action
participant S1 as Stage 1:<br/>Step Outputs
participant S2 as Stage 2:<br/>Direct File Analysis
participant S3 as Stage 3:<br/>Coverage Extraction
participant Validate as Validation Logic
WF->>CV: Invoke with coverage-directory<br/>threshold, step outputs
CV->>S1: Check OpenCover/Cobertura/Fallback<br/>step outcomes & outputs
alt Step outputs available & success
S1-->>CV: Return coverage percentage
else No usable step outputs
CV->>S2: Scan coverage-directory for XML files
S2->>S2: Locate OpenCover or Cobertura files
S2-->>CV: Return file path
CV->>S3: Parse XML file (attributes: line-rate,<br/>sequenceCoverage)
S3-->>CV: Extract coverage percentage<br/>(convert decimal to %)
end
CV->>Validate: Compare coverage vs threshold
alt Threshold Met
Validate-->>CV: threshold-met=true
else Threshold Not Met
Validate-->>CV: threshold-met=false
alt strict-mode=true
CV-->>WF: Exit with code 1 (fail workflow)
else strict-mode=false
CV-->>WF: Continue (lenient)
end
end
CV-->>WF: Output: coverage-percentage,<br/>coverage-source, threshold-met
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Areas requiring extra attention:
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (6)
.github/actions/validate-coverage/action.yml (2)
79-82: Improve variable naming for clarity.Lines 80-82 use
primary_success,cobertura_success,fallback_success, but the context ("primary") suggests OpenCover. Rename to explicitly reference the source format to avoid confusion during maintenance.- primary_success="${{ inputs.opencover-outcome }}" - cobertura_success="${{ inputs.cobertura-outcome }}" - fallback_success="${{ inputs.fallback-outcome }}" + opencover_success="${{ inputs.opencover-outcome }}" + cobertura_success="${{ inputs.cobertura-outcome }}" + fallback_success="${{ inputs.fallback-outcome }}"Then update references throughout (lines 85, 119, 123, 127).
225-238: Refactor: Eliminate duplicate GITHUB_OUTPUT assignments.Coverage percentage, source, and threshold-met are written to
GITHUB_OUTPUTin both the strict-mode block (lines 225-227) and unconditionally after (lines 236-238). This creates redundancy and potential inconsistency if the strict-mode branch exits early.Consolidate output assignment after the threshold-met logic completes, ensuring all paths set outputs once before any exit.
if [ "$coverage_int" -ge "$threshold_int" ]; then threshold_met="true" echo "✅ Coverage thresholds met: ${coverage_rate}% ≥ ${threshold_int}%" echo " Source: $coverage_source" else echo "❌ Coverage below minimum threshold" echo " Actual: ${coverage_rate}%" echo " Required: ${threshold_int}%" echo " Deficit: $((threshold_int - coverage_int))%" echo " Source: $coverage_source" echo "" echo "💡 Check the 'Code Coverage Summary' step for detailed information" if [ "${{ inputs.strict-mode }}" = "true" ]; then echo "" echo "🚫 STRICT MODE: Failing workflow due to insufficient coverage" else echo "" echo "⚠️ LENIENT MODE: Continuing despite coverage issues" fi fi + # Set outputs once after validation logic completes - echo "coverage-percentage=${coverage_rate}" >> $GITHUB_OUTPUT - echo "coverage-source=${coverage_source}" >> $GITHUB_OUTPUT - echo "threshold-met=${threshold_met}" >> $GITHUB_OUTPUT + echo "coverage-percentage=${coverage_rate}" >> $GITHUB_OUTPUT + echo "coverage-source=${coverage_source}" >> $GITHUB_OUTPUT + echo "threshold-met=${threshold_met}" >> $GITHUB_OUTPUT if [ "${{ inputs.strict-mode }}" = "true" ] && [ "$threshold_met" != "true" ]; then exit 1 fi.github/workflows/pr-validation.yml (4)
307-322: Add validation for dynamic include/exclude filter generation.If
include_patternis empty (line 308), theINCLUDE_FILTERbecomes[]*which may not match any modules. Add a guard to ensure the filter is meaningful or falls back to a sensible default.# Dynamic include filter based on module type if [ -n "$include_pattern" ]; then INCLUDE_FILTER="[${include_pattern}]*" else INCLUDE_FILTER="[MeAjudaAi.*]*" fi + + # Validate filter is not empty or trivial + if [ "$INCLUDE_FILTER" = "[]" ] || [ "$INCLUDE_FILTER" = "[].*" ]; then + echo "⚠️ INCLUDE_FILTER is trivial, falling back to [MeAjudaAi.*]*" + INCLUDE_FILTER="[MeAjudaAi.*]*" + fi
351-376: Refactor: Consolidate redundant coverage file search logic.The coverage file search on lines 357-369 uses
findtwice—once to check for files, then again to locate them. Additionally, both.opencover.xmland.cobertura.xmlpatterns are searched separately. Consolidate into a single, more efficient search.# Look for both opencover and cobertura formats (search recursively in GUID subdirs) - COVERAGE_FILE=$(find "$MODULE_COVERAGE_DIR" -type f \ - \( -name "coverage.opencover.xml" -o -name "coverage.cobertura.xml" \) \ - | head -1) + COVERAGE_FILE=$(find "$MODULE_COVERAGE_DIR" -type f \ + \( -name "coverage.opencover.xml" -o -name "coverage.cobertura.xml" \) \ + -print -quit) # Print first match and exit immediately if [ -f "$COVERAGE_FILE" ]; then echo "✅ Found coverage file: $COVERAGE_FILE" # Copy to standardized name based on original format if [[ "$COVERAGE_FILE" == *"cobertura"* ]]; then cp "$COVERAGE_FILE" "$MODULE_COVERAGE_DIR/${module_name,,}.cobertura.xml" - echo "✅ Cobertura coverage file created: $MODULE_COVERAGE_DIR/${module_name,,}.cobertura.xml" else cp "$COVERAGE_FILE" "$MODULE_COVERAGE_DIR/${module_name,,}.opencover.xml" - echo "✅ OpenCover coverage file created: $MODULE_COVERAGE_DIR/${module_name,,}.opencover.xml" fi
16-30: PostgreSQL configuration strategy is sound, but document the transition plan.Using environment variables with fallback credentials (lines 26-30) enables fork/local testing while requiring secrets in the main repo (validated on lines 73-122). This is a good pattern, but the TODO comment on line 24 references a sprint 2 milestone—confirm this is tracked and visible in the repository's issue tracker.
Add a cross-reference to the GitHub issue in a discoverable location:
# CRITICAL: Set STRICT_COVERAGE: true before merging to main # This enforces 70% minimum coverage threshold (lines 785, 810) # Current bypass is TEMPORARY for development only - # TODO: Enable STRICT_COVERAGE when overall coverage ≥ 70% (Sprint 2 milestone) + # TODO: Enable STRICT_COVERAGE when overall coverage ≥ 70% (Sprint 2 milestone) + # Tracking: https://github.com/frigini/MeAjudaAi/issues/33 # See tracking issue for details: https://github.com/frigini/MeAjudaAi/issues/33 # References: docs/testing/code-coverage-guide.md#L297-L313 # Re-enable when overall coverage reaches 70% (Sprint 2 milestone)
826-867: OSV-Scanner fallback handling is robust, but document the Go runtime requirement.Lines 826-867 include retry logic for download failures and a Go-based fallback if the binary cannot be fetched. This is resilient, but the Go fallback assumes a properly configured Go environment. Document that runners must have Go installed for full coverage of this fallback path, or provide a pre-built artifact alternative.
Add runtime documentation and consider validating Go availability upfront:
echo "💡 Trying fallback: install via Go toolchain..." # Fallback: Install via Go if available if command -v go >/dev/null 2>&1; then + echo "ℹ️ Using Go to install OSV-Scanner..." go install github.com/google/osv-scanner/cmd/osv-scanner@${OSV_VERSION} # Copy to local dir for consistent usage cp "$(go env GOPATH)/bin/osv-scanner" ./osv-scanner 2>/dev/null || \ ln -s "$(go env GOPATH)/bin/osv-scanner" ./osv-scanner else echo "❌ Go toolchain not available, cannot install OSV-Scanner" echo "⚠️ CRITICAL: Vulnerability scan cannot be performed" + echo "💡 MITIGATION: Ensure runner has Go ≥1.18 installed or provide pre-built OSV-Scanner binary" echo "💡 SECURITY NOTE: This workflow MUST fail when vulnerability scanning is unavailable." echo " To fix: ensure Go is installed on the runner or configure a pre-built OSV-Scanner binary." exit 1 # Fail the workflow - security scanning is mandatory fi
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
.github/actions/validate-coverage/README.md(1 hunks).github/actions/validate-coverage/action.yml(1 hunks).github/workflows/pr-validation.yml(11 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: frigini/MeAjudaAi PR: 0
File: AGENT.md:0-0
Timestamp: 2025-11-25T16:00:52.775Z
Learning: Minimum test coverage: 70% (CI warning threshold), Recommended: 85%, Excellent: 90%+
📚 Learning: 2025-11-25T16:00:52.775Z
Learnt from: CR
Repo: frigini/MeAjudaAi PR: 0
File: AGENT.md:0-0
Timestamp: 2025-11-25T16:00:52.775Z
Learning: Minimum test coverage: 70% (CI warning threshold), Recommended: 85%, Excellent: 90%+
Applied to files:
.github/actions/validate-coverage/action.yml
📚 Learning: 2025-11-25T16:00:52.775Z
Learnt from: CR
Repo: frigini/MeAjudaAi PR: 0
File: AGENT.md:0-0
Timestamp: 2025-11-25T16:00:52.775Z
Learning: Run all tests locally before submitting a PR
Applied to files:
.github/workflows/pr-validation.yml
🪛 actionlint (1.7.9)
.github/workflows/pr-validation.yml
325-325: could not parse as YAML: could not find expected ':'
(syntax-check)
🔇 Additional comments (8)
.github/actions/validate-coverage/action.yml (2)
1-62: Action definition is well-structured and handles multiple coverage sources.The composite action's multi-stage fallback design (step outputs → direct file analysis → regex extraction) is a robust approach to handle diverse CI environments and coverage tool formats. Input/output contracts are clearly documented and the action gracefully degrades when data is unavailable.
163-179: Remove: The identified issue does not apply to standard coverage XML formats.The current decimal-to-percentage conversion logic at lines 167-179 correctly handles both decimal (0.0-1.0) and percentage (1-100) formats. Cobertura's
line-rateand OpenCover'ssequenceCoverageattributes both use decimal format (0.0-1.0), soINT_PARTequals "0" or "" and the value is converted by multiplying by 100. Any value withINT_PART≥ 1 is already a percentage and is used as-is. The hypothetical "1.5" case does not occur in standard XML coverage formats, making the proposed fix unnecessary and introducing fragile hardcoded thresholds without solving any real problem..github/actions/validate-coverage/README.md (1)
1-270: Documentation is comprehensive, well-organized, and user-friendly.The README clearly explains the action's multi-stage fallback strategy, provides practical usage examples covering basic, advanced, and nightly scenarios, and includes a helpful troubleshooting section. Design decisions justify architectural choices (composite action, bash, multi-source fallback). Examples align with the action definition and demonstrate best practices for strict vs. lenient modes.
.github/workflows/pr-validation.yml (5)
269-290: Module-based coverage strategy is well-documented and addresses architectural concerns.The MODULES array (lines 269-290) clearly stratifies coverage targets by layer (Domain/Application at 80-100%, Shared/ApiService at 60-80%), and the rationale for excluding Infrastructure tests (lines 274-279) is well-articulated. This approach avoids distorting coverage metrics by mixing unit and integration tests. The per-module coverage collection aligns with the "Recommended: 85%" learnings from AGENT.md.
Based on learnings, ensure that per-module results are aggregated or reported to track progress toward the 85% recommended threshold and the 70% minimum (CI warning).
420-490: Hangfire/Npgsql compatibility test is critical and well-executed.The test logic (lines 420-490) clearly documents the breaking change risk, provides explicit mitigation options (downgrade, wait for update, switch backend), and fails decisively if incompatibility is detected. This prevents silent deployment failures. The structured error messaging and actionable guidance is exemplary.
73-122: Secrets validation with context-awareness prevents misconfiguration in main repo.The "Validate Secrets Configuration" step (lines 73-122) checks whether the workflow runs in the main repository context (not a fork) and enforces strict secret validation only for pull requests and workflow_dispatch events in the main repo. This pattern allows PRs from forks to pass while maintaining security guardrails for trusted contexts. Clear error messages aid debugging.
785-799: Integration of validate-coverage action is clean and leverages per-module coverage data.The "Validate Coverage Thresholds" step (lines 785-799) correctly passes outputs from the three CodeCoverageSummary fallback stages (OpenCover, Cobertura, Fallback) to the new composite action. The STRICT_COVERAGE environment variable enables flexible enforcement during development while maintaining the safety mechanism for CI. Comments clarify that this is a refactored, reusable step extracted from earlier inline logic.
323-339: The XML/shell syntax concern in runsettings generation is not substantiated.The INCLUDE_FILTER and EXCLUDE_FILTER variables used in the heredoc (lines 307–318, interpolated at lines 329–330) contain only safe characters: alphanumeric strings, dots, asterisks, commas, and square brackets. There are no unescaped XML special characters (like
",&,<, or>) that would break the XML structure or shell quoting. Testing the actual filter construction with the same pattern types confirmed successful XML generation without parsing errors. The reported static analysis hint does not reflect a real issue.
CHANGES: 1. Rename primary_success to opencover_success for clarity - Variables now explicitly reference source format (OpenCover) - Updated all references throughout action.yml - Improves maintainability and reduces confusion 2. Consolidate GITHUB_OUTPUT assignments - Eliminated duplicate output writes in strict-mode block - Outputs set once after validation completes - Exit happens after outputs are written - Prevents inconsistent state if early exit occurs 3. Add INCLUDE_FILTER validation - Guards against empty/trivial filter patterns like []* or [].* - Falls back to [MeAjudaAi.*]* for safety - Prevents coverage collection failures from invalid filters 4. Optimize coverage file search - Changed from 'head -1' to '-print -quit' for efficiency - Stops searching immediately after first match found - Removed redundant echo statements for created files - Reduces execution time and log noise 5. Add explicit GitHub issue reference - STRICT_COVERAGE TODO now links directly to issue #33 - Improves discoverability of Sprint 2 milestone tracking - Makes transition plan more visible 6. Document Go runtime requirement for OSV-Scanner - Added informative message when using Go fallback - Documents minimum Go version requirement (>=1.18) - Provides clear mitigation steps if Go unavailable - Improves troubleshooting for security scan failures RELATED: Code review feedback on PR ci-enable-module-coverage-collection
Force re-run of PR validation to use the updated workflow from master (PR #34) that includes module coverage collection
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.