Add C# (.NET) binding over the C ABI hub (#226)
The first language stecker on the C ABI hub: a .NET binding exposing all 514 indicators as idiomatic `IDisposable` classes, generated from `wickra.h`. ## What's here - **`bindings/csharp/`** — the `Wickra` .NET 8 package. `[LibraryImport]` source-generated P/Invoke (`NativeMethods.g.cs`) plus idiomatic wrappers (`Indicators.g.cs`), both generated from the committed `bindings/c/include/wickra.h`. The binding owns no indicator maths — it only marshals types across the C ABI. - **Marshalling, verified end-to-end against the native library.** Opaque handles cross as `nint` kept alive per call via a `SafeHandle`; `bool` as `[MarshalAs(U1)]` (Rust `bool` is one byte); a self-correcting `DllImportResolver` validates the loaded library actually exports the Wickra ABI. Tests cover one representative per FFI archetype (scalar, candle, pairwise, multi-output, bars, profile, values-profile, order-book / array-input) plus exact Sma reference values. - **NuGet packaging** — `dotnet pack` produces `Wickra.<version>.nupkg`; the release pipeline stages prebuilt native libraries under `runtimes/<rid>/native/` for six target triples (win/linux/osx × x64/arm64). - **`examples/csharp/`** — nine examples mirroring `examples/c/`: streaming, backtest, multi_timeframe, parallel_assets, three strategies, and fetch_btcusdt + live_binance. - **CI** — a `csharp` job on the three OSes builds the C ABI, tests the binding, and runs the offline examples. **Release** — a gated `csharp-publish` job packs and pushes to NuGet (gated on `NUGET_API_KEY`, independent of the GitHub-release job so a C# hiccup never blocks the C/C++ asset release). - **Docs consistency wave** — README, CONTRIBUTING, CHANGELOG, examples/README, the issue / PR templates, `sync-about.yml`, and `.gitattributes`. The native Python / Node / WASM bindings and the C ABI are untouched; this is additive. Publishing to NuGet stays gated behind the release tag and the secret.
This commit is contained in:
@@ -624,6 +624,88 @@ jobs:
|
||||
path: wickra-c-${{ matrix.target }}.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
# Pack and publish the .NET binding to NuGet. Independent of the GitHub-release
|
||||
# job so a C# hiccup never blocks the C/C++ asset release. Authentication uses
|
||||
# NuGet Trusted Publishing (OIDC) — no long-lived API key. The 'wickra-release'
|
||||
# trusted-publishing policy on nuget.org (owner KingchenC, repo wickra-lib/wickra,
|
||||
# workflow release.yml) exchanges the GitHub OIDC token for a short-lived key.
|
||||
csharp-publish:
|
||||
name: Publish to NuGet
|
||||
needs: c-abi-build
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # request the GitHub OIDC token for trusted publishing
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download the C ABI native libraries
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: c-abi-*
|
||||
path: c-abi-artifacts
|
||||
|
||||
- name: Stage native libraries into runtimes/<rid>/native
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
declare -A RID=(
|
||||
[x86_64-unknown-linux-gnu]=linux-x64
|
||||
[aarch64-unknown-linux-gnu]=linux-arm64
|
||||
[x86_64-apple-darwin]=osx-x64
|
||||
[aarch64-apple-darwin]=osx-arm64
|
||||
[x86_64-pc-windows-msvc]=win-x64
|
||||
[aarch64-pc-windows-msvc]=win-arm64
|
||||
)
|
||||
base=bindings/csharp/Wickra/runtimes
|
||||
for target in "${!RID[@]}"; do
|
||||
archive=$(find c-abi-artifacts -name "wickra-c-$target.tar.gz" | head -1)
|
||||
if [ -z "$archive" ]; then
|
||||
echo "::error::missing native artifact for $target"; exit 1
|
||||
fi
|
||||
tmp=$(mktemp -d); tar -xzf "$archive" -C "$tmp"
|
||||
dest="$base/${RID[$target]}/native"; mkdir -p "$dest"
|
||||
for f in libwickra.so libwickra.dylib wickra.dll; do
|
||||
src=$(find "$tmp" -name "$f" | head -1)
|
||||
[ -n "$src" ] && cp "$src" "$dest/"
|
||||
done
|
||||
echo "staged ${RID[$target]}:"; ls -l "$dest"
|
||||
done
|
||||
|
||||
- name: Pack
|
||||
shell: bash
|
||||
run: |
|
||||
version="${GITHUB_REF_NAME#v}"
|
||||
dotnet pack bindings/csharp/Wickra/Wickra.csproj -c Release -p:Version="$version" -o nupkg
|
||||
|
||||
# Exchange the GitHub OIDC token for a short-lived (~1h) NuGet API key.
|
||||
# 'user' is the nuget.org profile name (the package owner), not an email.
|
||||
- name: NuGet login (OIDC -> temporary API key)
|
||||
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1.2.0
|
||||
id: nuget_login
|
||||
with:
|
||||
user: KingchenC
|
||||
|
||||
# Pass the temporary key through the environment (not string-interpolated
|
||||
# into the script) so it cannot be parsed as shell — avoids template injection.
|
||||
- name: Push to NuGet
|
||||
shell: bash
|
||||
env:
|
||||
NUGET_API_KEY: ${{ steps.nuget_login.outputs.NUGET_API_KEY }}
|
||||
run: |
|
||||
dotnet nuget push "nupkg/*.nupkg" --api-key "$NUGET_API_KEY" \
|
||||
--source https://api.nuget.org/v3/index.json --skip-duplicate
|
||||
|
||||
- name: Upload the NuGet package as a build artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: nuget-package
|
||||
path: nupkg/*.nupkg
|
||||
if-no-files-found: error
|
||||
|
||||
github-release:
|
||||
name: Attach assets to the draft GitHub Release
|
||||
needs: [cargo-publish, python-publish, node-publish, wasm-publish, c-abi-build]
|
||||
|
||||
Reference in New Issue
Block a user