Bump Microsoft.SourceLink.GitHub from 8.0.0 to 10.0.301 #29
Workflow file for this run
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] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| DOTNET_NOLOGO: true | |
| DOTNET_CLI_TELEMETRY_OPTOUT: true | |
| DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true | |
| CI: true | |
| jobs: | |
| build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup .NET SDK | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| global-json-file: global.json | |
| - name: Verify pinned SDK | |
| shell: bash | |
| run: | | |
| SDK_VERSION=$(dotnet --version) | |
| echo "Installed SDK: $SDK_VERSION" | |
| EXPECTED=$(python -c "import json; print(json.load(open('global.json'))['sdk']['version'])") | |
| test "$SDK_VERSION" = "$EXPECTED" | |
| - name: Restore (locked mode) | |
| run: dotnet restore --locked-mode | |
| - name: Verify formatting | |
| if: matrix.os == 'ubuntu-latest' | |
| run: dotnet format --verify-no-changes --verbosity diagnostic | |
| - name: Build Release | |
| run: dotnet build -c Release --no-restore | |
| - name: Build samples | |
| shell: bash | |
| run: | | |
| set -e | |
| for proj in samples/*/*.csproj; do | |
| echo "=== Building $proj ===" | |
| dotnet build -c Release --no-restore "$proj" | |
| done | |
| - name: Test with coverage | |
| shell: bash | |
| run: | | |
| set +e | |
| FAILURES=0 | |
| RESULTS_DIR=./artifacts/test-results | |
| LOG="$RESULTS_DIR/test-output.log" | |
| mkdir -p "$RESULTS_DIR" | |
| : > "$LOG" | |
| for proj in tests/*/*.csproj; do | |
| echo "=== Testing $proj ===" | |
| dotnet test -c Release --no-build \ | |
| --collect:"XPlat Code Coverage" \ | |
| --results-directory "$RESULTS_DIR" \ | |
| --logger "console;verbosity=normal" \ | |
| "$proj" \ | |
| -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura \ | |
| 2>&1 | tee -a "$LOG" | |
| EXIT=$? | |
| if [ "$EXIT" -ne 0 ]; then | |
| FAILURES=$((FAILURES + 1)) | |
| echo "::error::Tests failed for $proj (exit $EXIT)" | |
| fi | |
| done | |
| if grep -E 'Skipped:\s*[1-9][0-9]*' "$LOG"; then | |
| echo "::error::One or more test runs reported skipped tests." | |
| FAILURES=$((FAILURES + 1)) | |
| fi | |
| if [ "$FAILURES" -ne 0 ]; then | |
| exit 1 | |
| fi | |
| - name: Enforce line coverage threshold | |
| if: matrix.os == 'ubuntu-latest' | |
| shell: bash | |
| run: | | |
| python3 <<'PY' | |
| import pathlib, sys, xml.etree.ElementTree as ET | |
| threshold = 85.0 | |
| files = list(pathlib.Path("artifacts/test-results").rglob("coverage.cobertura.xml")) | |
| if not files: | |
| print("::error::No cobertura coverage files found.", file=sys.stderr) | |
| sys.exit(1) | |
| # Deduplicate by package name so multi-project Coverlet runs do not double-count. | |
| package_stats: dict[str, tuple[int, int]] = {} | |
| for path in files: | |
| root = ET.parse(path).getroot() | |
| for package in root.findall(".//package"): | |
| name = package.attrib.get("name", "") | |
| if not name or ".Tests" in name or ".Sample" in name or ".Benchmarks" in name: | |
| continue | |
| if "FunctionFoundry." not in name: | |
| continue | |
| covered = 0 | |
| valid = 0 | |
| for line in package.findall(".//line"): | |
| valid += 1 | |
| if int(line.attrib.get("hits", "0")) > 0: | |
| covered += 1 | |
| previous = package_stats.get(name) | |
| if previous is None or valid > previous[1]: | |
| package_stats[name] = (covered, valid) | |
| if not package_stats: | |
| # Fallback to document totals when package names are unavailable. | |
| covered = valid = 0 | |
| for path in files: | |
| root = ET.parse(path).getroot() | |
| covered += int(float(root.attrib.get("lines-covered", "0"))) | |
| valid += int(float(root.attrib.get("lines-valid", "0"))) | |
| else: | |
| covered = sum(c for c, _ in package_stats.values()) | |
| valid = sum(v for _, v in package_stats.values()) | |
| for name, (c, v) in sorted(package_stats.items()): | |
| pct_pkg = 100.0 * c / v if v else 0.0 | |
| print(f" {name}: {pct_pkg:.1f}% ({c}/{v})") | |
| if valid == 0: | |
| print("::error::Coverage files contained zero valid lines.", file=sys.stderr) | |
| sys.exit(1) | |
| pct = 100.0 * covered / valid | |
| print(f"Aggregated line coverage: {pct:.2f}% ({covered}/{valid}) across {len(package_stats) or len(files)} packages/files") | |
| if pct + 1e-9 < threshold: | |
| print(f"::error::Line coverage {pct:.2f}% is below required {threshold:.1f}%.", file=sys.stderr) | |
| sys.exit(1) | |
| PY | |
| - name: Pack | |
| if: matrix.os == 'ubuntu-latest' | |
| run: | | |
| mkdir -p artifacts/packages | |
| dotnet pack -c Release --no-build -o artifacts/packages | |
| - name: Upload artifacts | |
| if: matrix.os == 'ubuntu-latest' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: packages-and-tests | |
| path: | | |
| artifacts/packages/** | |
| artifacts/test-results/** | |
| dependency-review: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/dependency-review-action@v4 |