Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 116 additions & 4 deletions .github/workflows/ci-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ name: Codebelt Coverlet MTP CI
on:
pull_request:
branches: [master]
push:
branches: [master]
workflow_dispatch:

permissions:
Expand All @@ -20,6 +18,31 @@ jobs:
upload-build-artifact-name: build-release
timeout-minutes: 45

release_version_guard:
name: validate-release-version
needs: [build]
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Validate calculated release version
shell: bash
env:
RELEASE_VERSION: ${{ needs.build.outputs.version }}
run: |
echo "Calculated release version: ${RELEASE_VERSION}"

if [[ -z "${RELEASE_VERSION}" ]]; then
echo "::error::Build did not produce a release version."
exit 1
fi

if [[ "${RELEASE_VERSION}" =~ ^0\.0\.0-alpha(\.|$) ]]; then
echo "::error::MinVer did not find a valid v-prefixed SemVer tag. Refusing to pack/publish Codebelt.Coverlet.MTP."
exit 1
fi

test:
name: call-test-${{ matrix.os }}-${{ matrix.project_name }}
needs: [build]
Expand Down Expand Up @@ -47,7 +70,7 @@ jobs:

pack:
name: call-pack
needs: [build, test]
needs: [build, test, release_version_guard]
uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3
with:
projects: src/coverlet.MTP/coverlet.MTP.csproj
Expand Down Expand Up @@ -103,6 +126,95 @@ jobs:
name: NuGet-Release
path: artifacts/package/release

- name: Validate packaged release artifact
shell: bash
env:
RELEASE_VERSION: ${{ needs.build.outputs.version }}
run: |
echo "Calculated release version: ${RELEASE_VERSION}"

if [[ -z "${RELEASE_VERSION}" ]]; then
echo "::error::Build did not produce a release version."
exit 1
fi

if [[ "${RELEASE_VERSION}" =~ ^0\.0\.0-alpha(\.|$) ]]; then
echo "::error::MinVer did not find a valid v-prefixed SemVer tag. Refusing to pack/publish Codebelt.Coverlet.MTP."
exit 1
fi

mapfile -t packages < <(find artifacts/package/release -type f -name '*.nupkg' | sort)

echo "Package files queued for publication:"
if (( ${#packages[@]} == 0 )); then
echo "::error::No .nupkg files were found under artifacts/package/release."
exit 1
fi

for package in "${packages[@]}"; do
echo " - ${package}"
done

if (( ${#packages[@]} != 1 )); then
echo "::error::Expected exactly one .nupkg to publish, found ${#packages[@]}."
exit 1
fi

python3 - "${packages[0]}" "${RELEASE_VERSION}" <<'PY'
import os
import sys
import zipfile
import xml.etree.ElementTree as ET

package_path, expected_version = sys.argv[1], sys.argv[2]
expected_id = "Codebelt.Coverlet.MTP"
expected_filename = f"{expected_id}.{expected_version}.nupkg"
actual_filename = os.path.basename(package_path)

def fail(message: str) -> "None":
print(f"::error::{message}")
raise SystemExit(1)

if expected_version.startswith("0.0.0-alpha"):
fail("MinVer did not find a valid v-prefixed SemVer tag. Refusing to pack/publish Codebelt.Coverlet.MTP.")

if actual_filename != expected_filename:
fail(f"Expected package filename {expected_filename}, found {actual_filename}.")

with zipfile.ZipFile(package_path) as archive:
nuspec_names = [name for name in archive.namelist() if name.endswith(".nuspec")]
if len(nuspec_names) != 1:
fail(f"Expected exactly one .nuspec inside {actual_filename}, found {len(nuspec_names)}.")

root = ET.fromstring(archive.read(nuspec_names[0]))

namespace = {"n": root.tag.split("}", 1)[0][1:]} if root.tag.startswith("{") else None

def find_text(name: str) -> str | None:
if namespace is None:
return root.findtext(f".//{name}")

return root.findtext(f".//n:{name}", namespaces=namespace)

package_id = find_text("id")
package_version = find_text("version")

if package_id != expected_id:
fail(f"Expected package id {expected_id}, found {package_id!r}.")

if package_version != expected_version:
fail(f"Expected package version {expected_version}, found {package_version!r}.")

if package_version.startswith("0.0.0-alpha"):
fail("MinVer fallback version 0.0.0-alpha.0 is not publishable.")

print(f"Nuspec package id: {package_id}")
print(f"Nuspec package version: {package_version}")
print(f"Validated package file: {actual_filename}")
PY

echo "PACKAGE_TO_PUBLISH=${packages[0]}" >> "$GITHUB_ENV"

- name: NuGet login (OIDC -> temporary API key)
uses: NuGet/login@v1
id: login
Expand All @@ -113,4 +225,4 @@ jobs:
shell: bash
env:
NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY }}
run: dotnet nuget push 'artifacts/package/release/*.nupkg' --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate
run: dotnet nuget push "$PACKAGE_TO_PUBLISH" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate
77 changes: 77 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [10.0.1] - 2026-09-20

This is a minor release focused on the Codebelt.Coverlet.MTP fork with narrowed scope, enhanced coverage analysis, improved testing infrastructure, and build reliability improvements.

### Added

- Codebelt CI/CD pipeline with shared workflow jobs replacing legacy Azure Pipelines,
- Coverage threshold failure messages and exit codes for MTP handler,
- Method coverage calculation and reporting alongside line and branch coverage,
- CoverletCoverageDataProducer for MTP message bus publishing of coverage data,
- Preflight checks for locked and unresolvable assemblies before instrumentation,
- Architecture documentation and diagrams for all integration points,
- Comprehensive unit tests for previously untested internal methods with dependency injection test setup,
- Benchmarks, GitHub Actions, and documentation infrastructure for performance testing,
- Test for [DoesNotReturn] detection in async state machines,
- Dynamic exclusion filters for Coverlet.MTP assemblies to improve filtering reliability,
- netstandard2.0 target framework support for broader compatibility,
- Trace diagnostics via --diag option and actionable warnings for instrumentation, hit, and empty-result failures,
- ResourceStream null guard to improve robustness,
- URL documentation for central testconfig.json configuration,
- MinVer tag prefix configuration for source projects to ensure correct semantic version tag detection during build,
- Pre-publish validation job in CI pipeline to verify calculated release version before packing,
- Comprehensive package artifact validation that verifies package filename, contents, and nuspec metadata match expected values,
- Targeted NuGet package push that publishes only the validated package instead of using wildcard patterns.

### Changed

- Rebranded repository to Codebelt.Coverlet.MTP fork with narrowed scope focusing on MTP integration,
- Removed legacy projects, workflows, documentation, and examples to simplify codebase,
- Updated target frameworks to netstandard2.0, net9.0, and net10.0,
- Aligned dependencies to modern framework versions including .NET 10.0.12,
- Enhanced core instrumentation code and capabilities with improved architecture,
- Integrated MTP extension and fixed test isolation issues,
- Updated test infrastructure for modern testing platform (xunit v3, Microsoft.Testing.Platform),
- Improved .NET Framework assembly resolution on Windows,
- Enhanced report output with summary table and console reporters,
- Improved pattern matching branch detection logic and documentation,
- Improved log formatting for multi-line messages in console output,
- Replaced ConcurrentBag with List for unload handlers registry with explicit locking,
- Eliminated phantom branches from async try-finally with await statements,
- Relaxed auto-property skip logic to improve coverage for records,
- Configuration parsing and CoverageConfiguration enhancements,
- Replaced legacy .sln files with modern .slnx format,
- Updated dependencies to latest stable releases across all packages.

### Fixed

- Fix silent zero coverage on .NET Framework that occurred since 8.0.0,
- Fix EndOfStreamException in coverage collection,
- Fix pattern matching 'or' synthetic branch detection,
- Fix FieldReference handling in delegate cache branch detection,
- Avoid unnecessary testhost restarts during test execution,
- Normalize Cobertura XML paths to forward slashes for consistency,
- Module restored atomically to prevent loaded assembly corruption,
- Unknown assembly fallback behavior and error handling,
- MTP validation tests infrastructure and test isolation issues,
- Remove skipping UnresolvableDependencies from preflight checks.

### Removed

- Legacy .sln solution files (replaced with .slnx format),
- Legacy projects and workflows,
- Documentation files (Changelog.md, GlobalTool.md, KnownIssues.md, MSBuildIntegration.md, VSTestIntegration.md, UnderstandingBranchCoverage.md, Troubleshooting.md, DeterministicBuild.md, etc.),
- Example projects for MSBuild and VSTest integrations,
- CodeQL GitHub Actions workflow (integrated into ci-pipeline.yml),
- Legacy dotnet.yml and other Azure Pipelines workflows,
- Legacy build scripts (scripts/build.ps1, scripts/test.ps1, scripts/report.ps1),
- .vscode/settings.json configuration,
- .devcontainer legacy configuration.

[10.0.1]: https://github.com/codebeltnet/coverlet/compare/v10.0.0...v10.0.1
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project>
<PropertyGroup>
<RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>
<IsSourceProject>$(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src'))</IsSourceProject>
<FullFrameworkTFM>net472</FullFrameworkTFM>
<!-- enforce CamelCase case string-->
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -46,4 +47,8 @@
<Deterministic>true</Deterministic>
</PropertyGroup>

<PropertyGroup Condition="'$(IsSourceProject)' == 'true'">
<MinVerTagPrefix>v</MinVerTagPrefix>
</PropertyGroup>

</Project>
Loading