Skip to content

Development

Working on virtufin-workers itself. Covers the build pipeline, the test layout, the CI surface, and the contribution conventions.

Build pipeline

The repo has two build entry points: per-worker (the WebSocketManagerController's scripts/build_*.py and scripts/build_both.py) and shared (@common/scripts/build_*.py).

The shared scripts: - build_managed.py — produces a managed-only nupkg. - build_aot.py — produces an AOT-only nupkg (requires Docker). - publish.py — uploads the nupkg to the Gitea NuGet feed.

The worker-local scripts wrap the shared ones and set the per-worker paths (the managed and AOT csproj locations, the config.json path, the canonical build/ output dir).

The build script is runnable locally:

# Managed-only
python3 WebSocketManagerController/scripts/build_managed.py

# AOT-only (requires Docker)
python3 WebSocketManagerController/scripts/build_aot.py

# Both (single nupkg with both layouts)
bash WebSocketManagerController/scripts/build_both.py

The smoke_test_aot.py script verifies the AOT path end-to-end:

python3 WebSocketManagerController/scripts/smoke_test_aot.py

Tests

WebSocketManagerController/tests/Virtufin.Worker.WebsocketManagerController.Tests/ is a C# xunit test project for the managed path only. It uses a TestData/ProcessAsync/InvalidInputs.json data file (Theory tests) plus a couple of Fact tests for correlationId and replyTopic behavior. The tests instantiate the worker directly and call ProcessAsync.

The AOT path has no unit tests in this repo (the only test surface is the smoke test, which runs the actual AOT compile). The virtufin-workmanager repo has unit tests for the NativeDllEngine.LoadFromNupkg flow, but those test the engine, not the worker.

CI

Each worker has its own .github/workflows/<workername>-nuget.yaml, a thin caller of the shared virtufin/virtufin-common/.github/workflows/worker-nuget-common.yaml reusable workflow: on push to master (path-filtered to that worker's directory), it runs the worker's tests, builds (managed-only, or managed+NativeAOT via Docker when the caller sets ships_native: true), and publishes to the Gitea NuGet feed. This is the actual publish path now — see AGENTS.md's "Build & publish a worker" section.

/.github/workflows/docs.yaml (calls virtufin-common/.github/workflows/docs-common.yaml@master with docs_repo: virtufin/workers-docs) and /.github/workflows/docs-lint.yaml (calls docs-lint-common.yaml@master) are the doc-site CI surfaces.

The smoke_test_aot.py script remains a local-only tool (not run in CI) for verifying the AOT path end-to-end beyond what the CI build step itself already confirms.

Conventions

  • scripts/build_managed.py and scripts/build_aot.py are thin pass-through wrappers — the real logic is in @common/scripts/build_*.py. The wrapper just sets paths.
  • Both csproj files (Managed/ and Native/) share the same <AssemblyName> so the nupkg's single <virtufinLibrary> value resolves to either the .dll or the .so depending on which engine loads it.
  • The AOT csproj's <Compile Include="..\...Managed\Logic.cs" Link="Logic.cs" /> links the shared handler logic from the managed project dir. Path is relative to the AOT csproj's location, so no absolute paths.
  • NuGet auth is via VIRTUFIN_REGISTRY_USER / VIRTUFIN_REGISTRY_TOKEN env vars; the worker scripts don't read ~/.docker/config.json for NuGet creds (only for Docker). The credentials are provided by the CI runner or the dev shell.

See also

  • Build Scriptbuild_managed.py + build_aot.py + build_both.py reference
  • Troubleshooting — common build/load failures and how to diagnose them