Configurable static quality and coverage gates for C#/.NET repositories.
Project description
.NET Quality Enforcer
Installable, configuration-driven quality gates for C# and .NET repositories.
This project is pre-1.0 and welcomes feedback from teams trying incremental quality enforcement in real repositories. The latest release is shown by the badge above.
What it does
.NET Quality Enforcer provides reusable checks for:
- architectural dependency boundaries
- code size and complexity
- source and namespace layout
- public API XML documentation
- test architecture and naming conventions
- Cobertura repository, diff, and branch coverage
The package owns the analysis engine and its tests. Consuming repositories own their policies, baselines, source layout, layer names, thresholds, and CI-specific paths.
The checks run against an explicit repository working directory. A policy file is optional for commands that provide defaults, but it is recommended for repeatable CI configuration.
Quality metrics and rules
The enforcer combines numeric maintainability metrics with structural quality rules. Thresholds below are built-in defaults and can be overridden in .quality/quality_policy.json.
| Area | What is measured or enforced | Built-in default |
|---|---|---|
| Code size | Physical lines in each method, type, and source file, excluding XML documentation comment lines from source-file totals; partial types are also aggregated across files. | Warn at 40/250/300 lines and fail at 60/350/450 lines for methods/types/files respectively. |
| Diff complexity | Changed production methods are checked for cyclomatic complexity, cognitive complexity, and CRAP score. | Cyclomatic <= 10, cognitive <= 10, CRAP <= 30.00. No file-count limit by default. |
| CRAP score | Combines cyclomatic complexity with method coverage: complexity² × (1 - coverage)³ + complexity. Higher complexity and lower coverage produce a higher risk score. |
Maximum 30.00. Coverage comes from the supplied Cobertura report. |
| Diff coverage | Executable changed-line coverage and, when configured, changed-branch coverage. | Line coverage >= 80%; branch coverage is optional. No file-count limit by default. |
| Repository coverage | Cobertura line coverage, plus optional branch coverage, for configured packages and classes. | Line coverage defaults to 100% for configured expected packages; branch coverage is optional. |
| Structural rules | Architectural dependency boundaries, namespace-to-path alignment, source type/file layout, public API XML summaries, test project placement, and source-to-test naming/target conventions. | Repository policy defines the expected layers, roots, mappings, and exclusions. |
The diff-complexity gate uses cyclomatic complexity to count independent decision paths, cognitive complexity to account for nesting and control-flow readability, and CRAP to combine complexity with test coverage. These metrics are evaluated against changed code so existing legacy complexity can be managed with baselines and incremental enforcement.
For example, the complexity and coverage limits can be configured as follows:
{
"diff_quality": {
"cyclomatic_complexity_max": 10,
"cognitive_complexity_max": 10,
"crap_score_max": 30,
"line_coverage_threshold": 0.8,
"branch_coverage_threshold": 0.8,
"max_files_for_gate": 40
}
}
Diff complexity and diff coverage analyze the full changed production set by default. Set max_files_for_gate to a positive integer only when a repository explicitly wants a maintenance cap; the setting applies to both diff gates.
Requirements
- Python 3.10 or newer
- A C#/.NET repository to analyze
- .NET 8 SDK only when using the optional Roslyn parser
- ReportGenerator only when using
coverage-report
The package has no runtime Python dependencies outside the standard library.
GitHub Action
This repository can be used directly as a cross-platform composite action. Pin consumers to a release tag or, preferably, an immutable commit SHA:
steps:
- uses: actions/checkout@v7
- id: quality
uses: KaramTNC/dotnet-quality-enforcer@v0
with:
command: code-size
arguments: --scope full
parser: auto
The v0 compatibility tag tracks the latest 0.x release. For production workflows, replace it with the release you have reviewed or an immutable commit SHA.
The action installs the package, runs the selected gate, and exposes result, status, returncode, violations, and warnings outputs. Set install-roslyn: true to install the .NET 8 SDK and build the bundled Roslyn helper before running a Roslyn-enabled gate. The coverage-report command still requires ReportGenerator to be available on the runner.
The action's result output uses the same schema_version: 1 JSON envelope as the command-line interface:
- name: Fail on quality violations
if: steps.quality.outputs.status == 'failed'
run: echo '${{ steps.quality.outputs.violations }}'
The release-download badge above counts downloads of GitHub Release assets. It does not count workflow executions that reference this repository with uses:. GitHub Actions usage is tracked separately through GitHub's Actions usage metrics; no telemetry is sent by this action.
Installation
The current public distributions are attached to GitHub Releases. Download the wheel that matches the release you want, or install from a source checkout:
git clone https://github.com/KaramTNC/dotnet-quality-enforcer.git
cd dotnet-quality-enforcer
python -m pip install .
The versioned release workflow also supports publishing the package to PyPI through trusted publishing. Once the repository's pypi environment is connected to a PyPI trusted publisher, install the CLI with:
python -m pip install dotnet-quality-gates
The one-time PyPI trusted-publisher configuration should use owner KaramTNC, repository dotnet-quality-enforcer, workflow package.yml, and environment pypi. The workflow uses short-lived OIDC credentials; no PyPI token is stored in the repository.
For local development, install the development tools as well:
python -m pip install -e ".[dev]"
For a copyable GitHub Actions workflow and starter policy, see examples/starter.
Usage
Run the top-level help to see every command and its options:
dotnet-quality --help
Commands can analyze the current directory or another repository with --repo-root:
dotnet-quality --repo-root path/to/repository code-size \
--scope full \
--policy-path .quality/quality_policy.json
dotnet-quality public-api-documentation \
--policy-path .quality/quality_policy.json \
--baseline-path .quality/baselines/public_api_documentation_baseline.txt
Available commands:
| Command | Purpose |
|---|---|
architectural-boundaries |
Validate project and namespace dependency boundaries. |
code-size |
Validate C# method, type, and file size. |
diff-complexity |
Validate changed-method complexity and CRAP scores. |
diff-coverage |
Validate changed-line and changed-branch coverage. |
namespace-layout |
Validate source namespaces against their paths. |
public-api-documentation |
Validate XML documentation for public C# APIs. |
repo-coverage |
Validate Cobertura repository and package coverage. |
source-type-layout |
Validate C# source type/file layout. |
test-architecture |
Validate source and test project placement. |
test-conventions |
Validate source-to-test naming and convention rules. |
coverage-report |
Generate a ReportGenerator coverage report. |
For automation, request a structured result envelope:
dotnet-quality --output json code-size --scope full
The JSON envelope has schema_version: 1, a status, returncode, violations, warnings, repository metadata, and the original stdout/stderr for compatibility.
Most commands use .quality/quality_policy.json by default when it exists. The top-level command validates known policy keys before starting a gate and reports the exact invalid key. Baseline files contain known violations that are intentionally accepted by the consuming repository; keep those files in the consuming repository rather than in this package.
The top-level options also support --timeout SECONDS for external tools and --parser auto|python|roslyn. The default auto mode uses Roslyn only when configured; python forces the dependency-free parser, and roslyn fails if the helper is unavailable or cannot analyze a file.
Optional Roslyn parsing
The built-in parser has no .NET runtime dependency. For modern C# syntax, build the optional Roslyn helper and set its command before running a gate:
dotnet build tools/roslyn-analyzer/DotnetQualityRoslyn.csproj -c Release
export DOTNET_QUALITY_ROSLYN_COMMAND="dotnet tools/roslyn-analyzer/bin/Release/net8.0/DotnetQualityRoslyn.dll"
When configured, source-type and unit-test convention analysis uses Roslyn. If the helper is unavailable or returns an error, the built-in parser is used instead.
Versioned releases also include a framework-dependent Roslyn helper archive. It still requires the .NET 8 runtime, but avoids rebuilding the helper locally. Release assets include SHA-256 checksums, an SBOM, build metadata, and GitHub artifact provenance.
Download tracking
The badge at the top of this page tracks downloads of the wheel and source-distribution assets attached to this repository's GitHub Releases. It does not include Git clones, source-archive downloads, or installations from other channels. See the release download statistics for the individual assets and releases.
Development and CI
Run the local checks with:
python -m unittest discover -s tests -p "test_*.py"
ruff check src tests action_runner.py
mypy src action_runner.py
pip-audit .
Pull requests targeting staging or main run the test suite on Python 3.10 through 3.13, plus static analysis and a Roslyn helper smoke test. Successful pushes to main build distributions and create a GitHub Release named main-<commit-sha>. Version tags matching vX.Y.Z create versioned releases and publish the Python package when PyPI trusted publishing is configured. The release workflow requires GitHub Actions permission to write repository contents.
The package version is derived from Git tags with setuptools-scm: a tag such as v0.2.0 produces version 0.2.0. Source checkouts without package metadata use 0.0.0+unknown.
Contributing
Please read CONTRIBUTING.md before opening a pull request. Issues and feature requests can be submitted through the GitHub issue tracker, and usage questions can be asked in Discussions.
Security issues should follow the process in SECURITY.md.
License
This project is available under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dotnet_quality_gates-0.2.3.tar.gz.
File metadata
- Download URL: dotnet_quality_gates-0.2.3.tar.gz
- Upload date:
- Size: 75.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e9f0075376a3a80605f6e4a62a2e0c0497ede7e0471b0218118b23be59e3ab45
|
|
| MD5 |
f95250a6529df243c1ac49b9ce4b78e9
|
|
| BLAKE2b-256 |
6616648482b3bfeea285f7ece982c15bbb4d5f89825857547feb406efae7396d
|
Provenance
The following attestation bundles were made for dotnet_quality_gates-0.2.3.tar.gz:
Publisher:
package.yml on KaramTNC/dotnet-quality-enforcer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dotnet_quality_gates-0.2.3.tar.gz -
Subject digest:
e9f0075376a3a80605f6e4a62a2e0c0497ede7e0471b0218118b23be59e3ab45 - Sigstore transparency entry: 2186140228
- Sigstore integration time:
-
Permalink:
KaramTNC/dotnet-quality-enforcer@72c46cbcea893c530a98b84032f044a0064ba8eb -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/KaramTNC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
package.yml@72c46cbcea893c530a98b84032f044a0064ba8eb -
Trigger Event:
push
-
Statement type:
File details
Details for the file dotnet_quality_gates-0.2.3-py3-none-any.whl.
File metadata
- Download URL: dotnet_quality_gates-0.2.3-py3-none-any.whl
- Upload date:
- Size: 65.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ba4900ed668b57dd2beaf129c6c8b04fe955d30ad6cf28a0a2901e2bc326b58
|
|
| MD5 |
9b189016eaabbf67bf4cc0b3fdba9f3f
|
|
| BLAKE2b-256 |
0a9c083ef169a68f1199cb71600f9bd63b5760ecae2e77ee173fbdbf1a1f59f5
|
Provenance
The following attestation bundles were made for dotnet_quality_gates-0.2.3-py3-none-any.whl:
Publisher:
package.yml on KaramTNC/dotnet-quality-enforcer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dotnet_quality_gates-0.2.3-py3-none-any.whl -
Subject digest:
1ba4900ed668b57dd2beaf129c6c8b04fe955d30ad6cf28a0a2901e2bc326b58 - Sigstore transparency entry: 2186140399
- Sigstore integration time:
-
Permalink:
KaramTNC/dotnet-quality-enforcer@72c46cbcea893c530a98b84032f044a0064ba8eb -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/KaramTNC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
package.yml@72c46cbcea893c530a98b84032f044a0064ba8eb -
Trigger Event:
push
-
Statement type: