Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
876dcde
feat: add recreate stream to ffi
sp-jcberleur Mar 12, 2026
66764e7
feat: add initial .NET SDK
sp-jcberleur Mar 13, 2026
0ed13c5
ci: integrate dotnet to general pipelines
sp-jcberleur Apr 23, 2026
301efc7
chore: add CONTRIBUTING NEXT_CHANGELOG NOTICE
sp-jcberleur Apr 23, 2026
412d809
fix: use malloc on all platforms
sp-jcberleur Apr 23, 2026
5d16636
fix: recreate leak
sp-jcberleur Apr 23, 2026
3ca20b7
fix: add threshold for stack allocation
sp-jcberleur Apr 28, 2026
8c5ba78
perf: encode without concatenation
sp-jcberleur Apr 28, 2026
c90c9f9
fix: use atomic check for sdk dispose
sp-jcberleur Apr 28, 2026
96d265e
feat: add zerobus_stream_is_closed to ffi
sp-jcberleur Jul 8, 2026
5092c11
feat: add ZerobusStream.IsClosed() method
sp-jcberleur Jul 8, 2026
a384f0c
fix(ffi): retryable flag propagation in create_stream, create_stream_…
sp-jcberleur Jul 8, 2026
823b399
fix(dotnet): add reader/writer lock on stream ptr
sp-jcberleur Jul 8, 2026
9adadf8
fix(dotnet): use rust alloc functions
sp-jcberleur Jul 8, 2026
a7bd5d6
fix(dotnet): graceful close from dispose
sp-jcberleur Jul 8, 2026
f8667ce
feat(dotnet): add concurrent ingest test
sp-jcberleur Jul 8, 2026
d79f0e3
feat(dotnet): move to sdk builder
sp-jcberleur Jul 9, 2026
8238712
feat(dotnet): add integration tests
sp-jcberleur Jul 9, 2026
d5e953e
fix(dotnet): allow 0 in config
sp-jcberleur Jul 9, 2026
1232e9d
docs(dotnet): improve Recreate ownership documentation
sp-jcberleur Jul 9, 2026
e61c2f5
fix(dotnet): remove boxing from GetUnackedRecords
sp-jcberleur Jul 9, 2026
a046ac2
fix(dotnet): use native defaults for stream config
sp-jcberleur Jul 9, 2026
044f978
fix(dotnet): headers leak in catch
sp-jcberleur Jul 9, 2026
a8062af
feat(dotnet): add typed stream
sp-jcberleur Jul 9, 2026
e3b9cdd
feat(rust/ffi): Add callback-based async overloads for all previously…
sp-jcberleur Jul 9, 2026
5a90a0a
feat(dotnet): add Async method overloads
sp-jcberleur Jul 9, 2026
cf48487
feat(dotnet): improve GetUnackedRecords on typed streams
sp-jcberleur Jul 9, 2026
8c9ec17
feat(dotnet): add DisposeAsync to streams
sp-jcberleur Jul 9, 2026
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
132 changes: 132 additions & 0 deletions .github/workflows/ci-dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: .NET CI

on:
workflow_call:
inputs:
use_local_sdk:
description: 'Override databricks-zerobus-ingest-sdk with the local path dep (for integration testing unreleased Rust changes)'
type: boolean
default: false

jobs:
fmt:
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
defaults:
run:
working-directory: dotnet
env:
SkipNativeBuild: true
steps:
- uses: actions/checkout@v6

- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Install dotnet-format
run: |
dotnet tool install -g dotnet-format
echo "$HOME/.dotnet/tools" >> $GITHUB_PATH

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt

- name: Format all files
run: make fmt

- name: Fail on differences
run: git diff --exit-code

test:
strategy:
fail-fast: false
matrix:
include:
- os: linux-ubuntu-latest
runner: { group: databricks-protected-runner-group, labels: linux-ubuntu-latest }
- os: windows-server-latest
runner: { group: databricks-protected-runner-group, labels: windows-server-latest }
- os: macos-arm64
runner: [self-hosted, macOS, ARM64, zerobus-sdk]
runs-on: ${{ matrix.runner }}
defaults:
run:
working-directory: dotnet
shell: bash
steps:
- uses: actions/checkout@v6

- name: Unshallow
run: git fetch --prune --unshallow

- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-gnu

- name: Patch Rust workspace to use local SDK
if: inputs.use_local_sdk
run: printf '\n[patch.crates-io]\ndatabricks-zerobus-ingest-sdk = { path = "sdk" }\n' >> ../rust/Cargo.toml

- name: Build Rust FFI
run: make build-rust

- name: Build .NET SDK
run: make build-dotnet

- name: Run tests
run: make test

- name: Build examples
run: make examples

user-experience:
# Skip when testing local SDK — this job tests the published release download flow
if: ${{ !inputs.use_local_sdk }}
strategy:
fail-fast: false
matrix:
include:
- os: linux-ubuntu-latest
runner: { group: databricks-protected-runner-group, labels: linux-ubuntu-latest }
lib: lib/linux_amd64/libzerobus_ffi.a
- os: windows-server-latest
runner: { group: databricks-protected-runner-group, labels: windows-server-latest }
lib: lib/windows_amd64/libzerobus_ffi.a
- os: macos-arm64
runner: [self-hosted, macOS, ARM64, zerobus-sdk]
lib: lib/darwin_arm64/libzerobus_ffi.a
runs-on: ${{ matrix.runner }}
defaults:
run:
working-directory: dotnet
shell: bash
steps:
- uses: actions/checkout@v6

- uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- uses: dtolnay/rust-toolchain@stable

- name: Test .NET build works (user step 2)
run: dotnet build

- name: Test JSON single example builds
working-directory: dotnet/examples/JsonSingle
run: dotnet build

- name: Test JSON batch example builds
working-directory: dotnet/examples/JsonBatch
run: dotnet build

- name: Test Proto single example builds
working-directory: dotnet/examples/ProtoSingle
run: dotnet build
21 changes: 19 additions & 2 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
needs: [changes, rust, java, typescript, python, go, purego, cpp]
needs: [changes, rust, java, typescript, python, go, purego, cpp, dotnet]
if: always()
steps:
- run: |
Expand All @@ -22,7 +22,8 @@ jobs:
"${{ needs.python.result }}" \
"${{ needs.go.result }}" \
"${{ needs.purego.result }}" \
"${{ needs.cpp.result }}"; do
"${{ needs.cpp.result }}" \
"${{ needs.dotnet.result }}"; do
if [[ "$result" != "success" && "$result" != "skipped" ]]; then
echo "Job failed or was cancelled: $result"
exit 1
Expand All @@ -41,6 +42,7 @@ jobs:
go: ${{ steps.filter.outputs.go }}
purego: ${{ steps.filter.outputs.purego }}
cpp: ${{ steps.filter.outputs.cpp }}
dotnet: ${{ steps.filter.outputs.dotnet }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
Expand Down Expand Up @@ -71,6 +73,9 @@ jobs:
cpp:
- 'cpp/**'
- '.github/workflows/ci-cpp.yml'
dotnet:
- 'dotnet/**'
- '.github/workflows/ci-dotnet.yml'

# Blocking jobs — required for merge
rust:
Expand Down Expand Up @@ -129,6 +134,11 @@ jobs:
contents: read
uses: ./.github/workflows/ci-cpp.yml

dotnet:
needs: changes
if: needs.changes.outputs.dotnet == 'true'
uses: ./.github/workflows/ci-dotnet.yml

# Non-blocking cross-SDK jobs — run all SDK tests against the local Rust
# source when rust/** changes, to catch breakage before a new FFI release.
# These are intentionally excluded from `gate` so they don't block merging.
Expand All @@ -150,6 +160,13 @@ jobs:
contents: read
uses: ./.github/workflows/ci-cpp.yml

cross-sdk-dotnet:
needs: changes
if: needs.changes.outputs.rust == 'true'
uses: ./.github/workflows/ci-dotnet.yml
with:
use_local_sdk: true

cross-sdk-python:
needs: changes
if: needs.changes.outputs.rust == 'true'
Expand Down
194 changes: 194 additions & 0 deletions .github/workflows/release-dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
name: Build & Release .NET SDK
run-name: "Release .NET SDK ${{ github.ref_name }}"

on:
push:
tags:
- 'dotnet/v*'
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: write
id-token: write # Required for attestation
attestations: write # Required for attestation

jobs:
build-ffi:
strategy:
fail-fast: false
matrix:
include:
- runner: { group: databricks-protected-runner-group, labels: linux-ubuntu-latest }
target: x86_64-unknown-linux-gnu
static_lib: libzerobus_ffi.a
dynamic_lib: libzerobus_ffi.so
artifact_dir: linux-x64
cross: false
- runner: { group: databricks-protected-runner-group, labels: linux-ubuntu-latest }
target: aarch64-unknown-linux-gnu
static_lib: libzerobus_ffi.a
dynamic_lib: libzerobus_ffi.so
artifact_dir: linux-arm64
cross: true
- runner: { group: databricks-protected-runner-group, labels: windows-server-latest }
target: x86_64-pc-windows-msvc
static_lib: zerobus_ffi.lib
dynamic_lib: zerobus_ffi.dll
artifact_dir: win-x64
cross: false
- runner: [self-hosted, macOS, ARM64, zerobus-sdk]
target: aarch64-apple-darwin
static_lib: libzerobus_ffi.a
dynamic_lib: libzerobus_ffi.dylib
artifact_dir: osx-arm64
cross: false
- runner: [self-hosted, macOS, ARM64, zerobus-sdk]
target: x86_64-apple-darwin
static_lib: libzerobus_ffi.a
dynamic_lib: libzerobus_ffi.dylib
artifact_dir: osx-x64
cross: false

name: Build FFI - ${{ matrix.artifact_dir }}
runs-on: ${{ matrix.runner }}

defaults:
run:
working-directory: rust

steps:
- uses: actions/checkout@v4

- name: Install protoc
uses: arduino/setup-protoc@v3

- name: Install cross-compilation toolchain (Linux ARM64)
if: matrix.cross
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq gcc-aarch64-linux-gnu
mkdir -p .cargo
echo '[target.aarch64-unknown-linux-gnu]' > .cargo/config.toml
echo 'linker = "aarch64-linux-gnu-gcc"' >> .cargo/config.toml

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}

- name: Build FFI library
run: cargo build --release -p zerobus-ffi --target ${{ matrix.target }}

- name: Prepare artifacts
shell: bash
run: |
mkdir -p artifacts/${{ matrix.artifact_dir }}/native
cp target/${{ matrix.target }}/release/${{ matrix.static_lib }} artifacts/${{ matrix.artifact_dir }}/native/
cp target/${{ matrix.target }}/release/${{ matrix.dynamic_lib }} artifacts/${{ matrix.artifact_dir }}/native/

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ffi-${{ matrix.artifact_dir }}
path: rust/artifacts/${{ matrix.artifact_dir }}

publish:
needs: build-ffi
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
defaults:
run:
working-directory: dotnet

steps:
- uses: actions/checkout@v4

- name: Set up .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Download all native libraries
uses: actions/download-artifact@v8
with:
path: native
pattern: ffi-*
merge-multiple: false

- name: Consolidate native libraries
run: |
mkdir -p src/Zerobus/runtimes
cp -r native/ffi-linux-x64/linux-x64 src/Zerobus/runtimes/
cp -r native/ffi-linux-arm64/linux-arm64 src/Zerobus/runtimes/
cp -r native/ffi-osx-x64/osx-x64 src/Zerobus/runtimes/
cp -r native/ffi-osx-arm64/osx-arm64 src/Zerobus/runtimes/
cp -r native/ffi-win-x64/win-x64 src/Zerobus/runtimes/

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/dotnet/v}" >> "$GITHUB_OUTPUT"

- name: Package NuGet
env:
CI: true
run: |
dotnet pack src/Zerobus/Zerobus.csproj \
--configuration Release \
--output ./packages \
-p:SkipNativeBuild=true \
-p:Version=${{ steps.version.outputs.version }} \
-p:PackageVersion=${{ steps.version.outputs.version }} \
-p:AssemblyVersion=${{ steps.version.outputs.version }} \
-p:FileVersion=${{ steps.version.outputs.version }} \
-p:InformationalVersion=${{ steps.version.outputs.version }} \
-p:CI=true

- name: Generate attestation
uses: actions/attest-build-provenance@v4
with:
subject-path: "packages/*.nupkg"

- name: NuGet login (OIDC → temp API key)
uses: NuGet/login@v1
id: login
with:
user: ${{ secrets.NUGET_USERNAME }}

- name: Publish to Nuget Packages
env:
NUGET_AUTH_TOKEN: ${{ steps.login.outputs.NUGET_API_KEY }}
run: |
dotnet nuget push packages/*.nupkg \
--api-key "${{ steps.login.outputs.NUGET_API_KEY }}" \
--skip-duplicate \
--source https://api.nuget.org/v3/index.json

release:
name: Create GitHub Release
runs-on:
group: databricks-protected-runner-group
labels: linux-ubuntu-latest
needs: publish

steps:
- uses: actions/checkout@v4

- name: Extract version from tag
id: version
run: echo "version=${GITHUB_REF#refs/tags/dotnet/v}" >> "$GITHUB_OUTPUT"

- name: Extract release notes from changelog
run: |
version="v${{ steps.version.outputs.version }}"
awk "/^## Release ${version}/{found=1; next} /^## /{if(found) exit} found{print}" dotnet/CHANGELOG.md > /tmp/release-notes.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
name: .NET SDK v${{ steps.version.outputs.version }}
body_path: /tmp/release-notes.md
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Zerobus is a high-throughput streaming service for direct data ingestion into Da
| TypeScript | [`typescript/`](typescript/) | [`@databricks/zerobus-ingest-sdk`](https://www.npmjs.com/package/@databricks/zerobus-ingest-sdk) |
| Java | [`java/`](java/) | [`com.databricks:zerobus-ingest-sdk`](https://central.sonatype.com/artifact/com.databricks/zerobus-ingest-sdk) |
| C++ | [`cpp/`](cpp/) | Source / CMake (`zerobus::zerobus`) |
| C# | [`dotnet/`](dotnet/) | [`Databricks.Zerobus`](https://www.nuget.org/packages/Databricks.Zerobus) |

## Platform Support

Expand Down
Loading