Skip to content

Latest commit

 

History

History
264 lines (181 loc) · 9.23 KB

CONTRIBUTING.md

File metadata and controls

264 lines (181 loc) · 9.23 KB

Contributing to ClickHouse.Driver

ClickHouse.Driver is an open-source project, and we welcome any contributions from the community. Please share your ideas, contribute to the codebase, and help us maintain up-to-date documentation.

Getting started

Prerequisites

You will need:

  • .NET SDK 9.0+ (the project also targets .NET 6.0, 8.0, .NET Standard 2.1, and .NET Framework 4.6.2/4.8)

You can verify your .NET installation:

dotnet --version

Create a fork of the repository and clone it

git clone https://github.com/ClickHouse/clickhouse-cs
cd clickhouse-cs

Restore dependencies

Build the solution

dotnet build ClickHouse.Driver.sln -c Release

Agentic development

It is recommended to have the C# LSP server and plugin installed and enabled if using Claude Code. See here for more information.

Style Guide

The project enforces code style automatically via .editorconfig and <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>.

Testing

Whenever you add a new feature or fix a bug, we strongly encourage you to add appropriate tests to ensure everyone in the community can safely benefit from your contribution.

Test structure

The solution includes three test projects:

  • ClickHouse.Driver.Tests - Unit and functional tests
  • ClickHouse.Driver.IntegrationTests - Integration tests (Liq2db)
  • ClickHouse.Driver.Benchmark - Performance benchmarks (BenchmarkDotNet)

Running unit tests

Tests require a ClickHouse server. By default, the test suite uses Testcontainers to automatically start and manage a ClickHouse container for you - all you need is a running Docker daemon.

dotnet test ClickHouse.Driver.Tests/ClickHouse.Driver.Tests.csproj --framework net9.0 -c Release

Using an existing ClickHouse instance

If you'd rather point the tests at an already-running ClickHouse instance, set CLICKHOUSE_CONNECTION to a valid connection string. When this variable is set, Testcontainers is skipped:

# Windows (PowerShell)
$env:CLICKHOUSE_CONNECTION="Host=localhost;Port=8123;Username=default"

# Linux/macOS (Bash)
export CLICKHOUSE_CONNECTION="Host=localhost;Port=8123;Username=default"

A matching Docker command, if you want to run your own instance:

docker run --rm -d \
  -p 8123:8123 \
  -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \
  --name clickhouse-dev clickhouse/clickhouse-server:latest

Running integration tests

Integration tests use the same ClickHouse instance but are in a separate project:

dotnet test ClickHouse.Driver.IntegrationTests/ClickHouse.Driver.IntegrationTests.csproj --framework net9.0 -c Release

Running tests with code coverage

The project uses Coverlet for code coverage. The HTTP and TCP drivers have separate test projects:

dotnet test ClickHouse.Driver.Tests/ClickHouse.Driver.Tests.csproj \
  --framework net9.0 \
  -c Release \
  /p:CollectCoverage=true \
  /p:CoverletOutputFormat=opencover \
  /p:SkipAutoProps=true

dotnet test ClickHouse.Driver.Tcp.Tests/ClickHouse.Driver.Tcp.Tests.csproj \
  --framework net9.0 \
  -c Release \
  /p:CollectCoverage=true \
  /p:CoverletOutputFormat=opencover \
  /p:SkipAutoProps=true

The TCP tests start a ClickHouse container with Testcontainers unless CLICKHOUSE_TCP_CONNECTION sets a server connection string.

Coverage reports are generated in each test project directory:

ClickHouse.Driver.Tests/coverage.net9.0.opencover.xml
ClickHouse.Driver.Tcp.Tests/coverage.net9.0.opencover.xml

Running benchmarks

Benchmarks use BenchmarkDotNet and require a running ClickHouse instance. CLICKHOUSE_CONNECTION carries the HTTP connection string. CLICKHOUSE_TCP_CONNECTION_STRING carries the native one; with it unset, the native endpoint is derived from the HTTP string on port 9000.

dotnet run --project ClickHouse.Driver.Benchmark/ClickHouse.Driver.Benchmark.csproj \
  --configuration Release \
  -- --join --filter "*" --artifacts ./results

Results will be saved in the results/ directory.

Every benchmark class carries a category, and the CI runs select on them:

Category Covers
http-regression HTTP throughput and allocation
tcp-regression Native protocol throughput and allocation
cross The same workload over both transports
http-investigation, tcp-investigation One issue or one past optimization
compression Codecs and framing, shared by both transports
dotnet run --project ClickHouse.Driver.Benchmark/ClickHouse.Driver.Benchmark.csproj \
  --configuration Release \
  -- --join --filter "*" --anyCategories tcp-regression cross --artifacts ./results

A pull request runs the categories its changed files select, in .github/workflows/benchmark-compare.yml. The nightly build runs all of them.

The full iteration counts take hours. For a quick local pass, BENCH_WARMUP, BENCH_ITERATIONS and BENCH_LAUNCHES override them:

BENCH_WARMUP=1 BENCH_ITERATIONS=5 BENCH_LAUNCHES=1 \
  dotnet run --project ClickHouse.Driver.Benchmark/ClickHouse.Driver.Benchmark.csproj \
  --configuration Release -- --join --filter "*TcpReadTiers*" --artifacts ./results

Testing against different ClickHouse versions

To pin tests to a specific ClickHouse version, set CLICKHOUSE_VERSION. Testcontainers will pull the matching clickhouse/clickhouse-server:<version> image:

export CLICKHOUSE_VERSION="25.3"
dotnet test ClickHouse.Driver.Tests/ClickHouse.Driver.Tests.csproj --framework net9.0 -c Release

If you're using your own ClickHouse instance via CLICKHOUSE_CONNECTION, CLICKHOUSE_VERSION is still honored for feature-flag detection — otherwise the server version is queried at startup.

Changelog entries

Any user-visible change needs a changelog entry. Entries are fragments: one file per change under changelog.d/, which maintainers fold into CHANGELOG.md at release time.

dotnet run scripts/changelog.cs -- --new fixes 512-variant-null

Write the entry into the file it creates — one or two sentences on the user-visible change, plus the issue number. Preview how it will render with --render, and run --check to see what CI will say.

Do not edit CHANGELOG.md's Unreleased section, and do not edit RELEASENOTES.md at all. Two pull requests appending to one shared section conflict every time, and GitHub ignores .gitattributes merge drivers when merging pull requests, so a merge driver cannot fix it. A fragment is a file only your branch adds, so there is nothing to reconcile. RELEASENOTES.md is generated from CHANGELOG.md.

See changelog.d/README.md for the categories and naming convention.

CI/CD

Expected CI behavior

  • All tests must pass on all target frameworks and ClickHouse versions
  • Code coverage is reported to Codecov. The HTTP and TCP suites upload separate reports (flags http and tcp), and Codecov shows their combined total. The codecov/patch check has a 75% target for the lines that a pull request changes.
  • Benchmarks run but don't fail the build (used for performance regression detection)
  • CodeQL must not find security issues
  • Public API analyzer will fail if API surface changes aren't documented

Release process

Releases are managed by maintainers through the GitHub Actions release workflow.

Version updates

Before release, update version in:

  • ClickHouse.Driver/ClickHouse.Driver.csproj - <Version> property
  • Ensure PublicAPI.Shipped.txt is updated with new APIs
  • Move entries from PublicAPI.Unshipped.txt to PublicAPI.Shipped.txt

Then fold the pending changelog fragments into the changelog:

dotnet run scripts/changelog.cs -- --release v1.4.0

That retitles the Unreleased section to the new version and fills it from changelog.d/, opens a fresh empty Unreleased, regenerates RELEASENOTES.md, and deletes the consumed fragments. Do this on a release-prep branch and open it as a normal pull request — the release workflow builds from a commit and does not write to the repository.

If you forget, the release workflow stops before building: its first job runs --verify-release <version> and fails unless the changelog was actually assembled for the version being released and no fragments are left pending. That gate exists because both outputs are permanent — a NuGet package can only be delisted, never unpublished, and RELEASENOTES.md is baked into it via PackageReleaseNotes.

Release workflow

The release is triggered manually via GitHub Actions:

  1. Go to Actions → Release workflow
  2. Click "Run workflow"
  3. The workflow will:
    • Build the solution in Release mode
    • Pack the NuGet package
    • Publish to NuGet.org
    • Create a GitHub release

Getting help

  • Issues: Open a GitHub issue for bugs or feature requests
  • Discussions: Use GitHub Discussions for questions
  • Pull Requests: Submit PRs with clear descriptions and tests

Code of Conduct

Please be respectful and constructive in all interactions. We aim to foster an inclusive and welcoming community.

License

By contributing to ClickHouse.Driver, you agree that your contributions will be licensed under the MIT License.