GitHub user Fatorin created a discussion: Release Workflow Design for OpenDAL .NET Binding
## Summary The .NET binding publish pipeline was introduced in [#7323](https://github.com/apache/opendal/pull/7323). Before cutting the first release, there are two design decisions that need community input. Both are strongly tied to how NuGet (the .NET package platform) works. **Key NuGet platform constraint**: Unlike PyPI (`test.pypi.org`) or Maven (staging repositories), **NuGet has no staging or test feed**. Pre-release packages are published directly to nuget.org — but they are **hidden by default** from all standard tooling (Visual Studio, `dotnet` CLI) unless users explicitly opt in with `--prerelease`. This is the standard practice across the .NET ecosystem, including the .NET SDK itself. --- ## 1. Testing the Release Workflow via `workflow_dispatch` I'd like to run a few `workflow_dispatch` builds to validate that the full CI pipeline (native build matrix → pack → publish) works end-to-end before we tag a real release. Since NuGet packages **cannot be deleted** (only unlisted), this needs to be done carefully. **The current workflow does not properly handle `workflow_dispatch` versioning** — it only derives the package version from git tags, so manual dispatch builds fall back to the bare `.csproj` version without any pre-release suffix. I plan to submit a PR to fix this, adding a `preview.{run_number}` suffix for `workflow_dispatch` builds so each run produces a unique pre-release version. The proposed test plan after the CI fix: 1. **Dry run** — `workflow_dispatch` with `dotnet_publish = false`. Validate that the build matrix and pack step produce the correct artifact. No package is published. 2. **Publish test** — `workflow_dispatch` with `dotnet_publish = true`. Publish a `preview` pre-release package to nuget.org and verify installation from a consumer project on all three platforms. For tag-triggered releases, the workflow follows the **.NET SDK's own lifecycle** (`preview` → `rc` → `stable`): | Trigger | Version | Example | |---------|---------|---------| | `workflow_dispatch` | `<csproj Version>-preview.{run_number}` | `0.1.0-preview.42` | | Tag with pre-release suffix | Use tag as-is | `v0.1.0-rc.1` → `0.1.0-rc.1` | | Tag without suffix (safety net) | Auto-append `-rc` | `v0.1.0` → `0.1.0-rc` | The `-rc` safety net on bare tags prevents accidental stable releases and will be removed once the workflow is validated. **Are there any concerns before I proceed with the CI fix and testing?** --- ## 2. Tag Namespace and Versioning Following the precedent of other OpenDAL bindings (Java `0.48.2`, Node.js `0.49.2`, Go `0.1.x` — all independent from core `0.55.0`), the .NET binding will also use **independent versioning**, starting at `0.1.0`. ### Current behavior and the problem The current .NET workflow triggers on `v[0-9]+.[0-9]+.[0-9]+*` and **derives the package version from the tag**. This is the same pattern used by core and Node.js. Since all bindings version independently, this causes a conflict: when core pushes `v0.55.0` or `v0.56.0`, the .NET workflow is triggered and would attempt to publish `0.55.0` or `0.56.0` — versions that have nothing to do with the .NET binding. This is especially dangerous because NuGet packages **cannot be deleted**, only unlisted. Note: **Java has the same issue in theory** — its tag pattern `v*-rc.*` would also match RC tags from other bindings. In practice this hasn't caused problems because Maven uses a staging repository where mistakenly staged artifacts can be dropped before promotion. NuGet has no such safety net. ### Why reading from `.csproj` doesn't fully solve it either One idea is to ignore the tag version and always read from `.csproj` instead. But this creates a different problem: **version collisions**. If the `.csproj` is at `0.1.0` and we append `-rc` on every tag push: - Core pushes `v0.55.0` → .NET workflow produces `0.1.0-rc` - Core pushes `v0.56.0` → .NET workflow produces `0.1.0-rc` again (same version, `--skip-duplicate` skips it) - Core pushes `v0.55.0-rc.1` → .NET workflow produces `0.1.0-rc` again Every unrelated tag triggers a wasted build, and if any of them happens to be the first run, it publishes a `0.1.0-rc` that wasn't intentionally released. ### The core question The shared `v*` tag namespace is **not viable** for the .NET binding — whether we read the version from the tag or from `.csproj`, unrelated tags will either publish wrong versions or trigger wasted/colliding builds. This means we need a .NET-specific trigger mechanism. The options are: 1. **Binding-specific tag prefix** (e.g., `dotnet-v0.1.0-rc.1`). Maintainers push a dedicated tag when they want to release the .NET binding. Version comes from the tag, no ambiguity. 2. **`workflow_dispatch` only** — remove tag-triggered publishing entirely. Before each release, manually update `.csproj` `<Version>`, merge, then trigger the workflow. But for **urgent releases** (e.g., a critical bug fix), this requires multiple steps: update `.csproj` → merge PR → manually trigger workflow — which adds friction in time-sensitive situations. **How should we handle the .NET binding's release trigger?** --- ## Appendix: How Other OpenDAL Bindings Handle Versioning For reference, here is how the existing Java and Node.js bindings handle versioning and releases. All bindings use **independent versioning** — none of them align with the Rust core version. | | Java | Node.js | .NET (current) | |--|------|---------|----------------| | **Binding version** | `0.48.2` | `0.49.2` | `0.1.0` | | **Rust core version** | `0.55.0` | `0.55.0` | `0.55.0` | | **Tag trigger** | `v*-rc.[0-9]+` (RC only) | `v*` | `v*` | | **`workflow_dispatch`** | Yes, no parameters | Yes, dry-run + publish toggles | Yes, publish toggle | | **Publish platform** | Maven (Nexus staging) | npm | NuGet | Key observations: - **Java** only accepts `-rc.N` tags because Maven has a staging repository mechanism. RC packages are pushed to Nexus staging, reviewed, and then promoted to Maven Central as a stable release. The tag itself is always an RC, but the final published artifact on Maven Central is a stable version. - **Node.js** triggers on any `v*` tag and publishes directly to npm. Similar to NuGet, npm has no staging feed, so the tag version is the published version. - **Both bindings version independently from core.** The Rust core is at `0.55.0`, while Java is at `0.48.2` and Node.js is at `0.49.2`. This is explicitly documented in [`bindings/README.md`](https://github.com/apache/opendal/blob/main/bindings/README.md). --- ## References For context, here is how other projects in the NuGet ecosystem handle pre-releases: | Project | Pre-release Pattern | Strategy | |---------|-------------------|----------| | **.NET SDK** | `-preview.N` → `-rc.N` → stable | Full lifecycle on nuget.org | | **Npgsql** | `-rc.N` on nuget.org, CI on MyGet | Separate feed for CI builds | | **Polly** | `-beta.N` | Direct to nuget.org | | **Serilog** | `-dev-NNNNN` | Every CI build to nuget.org | | **MassTransit** | `-develop.NNN` | Every CI build to nuget.org | All of these publish pre-releases directly to nuget.org. NuGet's default hiding of pre-release packages makes this safe for end users. --- Feedback on both topics is welcome — especially from maintainers with experience in other binding release workflows. GitHub link: https://github.com/apache/opendal/discussions/7394 ---- This is an automatically sent email for [email protected]. To unsubscribe, please send an email to: [email protected]
