Jens-G opened a new pull request, #3896: URL: https://github.com/apache/thrift/pull/3896
> **Stacked on [#3893](https://github.com/apache/thrift/pull/3893) (THRIFT-6310) → [#3894](https://github.com/apache/thrift/pull/3894) (THRIFT-6312).** It uses `check-portable-exe.ps1` from #3893. Review those first; this branch contains both. A .NET developer on Windows who wants the Thrift compiler has to find the download page, pick the right file and put it on `PATH` by hand. Every other build-time tool in that ecosystem comes from `dotnet tool install`, and can be pinned per repository in a `dotnet-tools.json` manifest so everyone building a project uses the same compiler version. ```shell dotnet tool install --global Apache.Thrift.Compiler thrift --version ``` ## The package id Checked against nuget.org: | id | | |---|---| | `ApacheThrift` | the existing netstd runtime library — stays what it is | | `Apache.Thrift` | **not available.** Held by a third party and delisted. NuGet ids are permanent, so a delisted id cannot be reused. | | `Apache.Thrift.Compiler` | free, and follows the `Apache.Avro` / `Apache.Arrow` naming sibling ASF projects use | `Apache.*` is not a reserved prefix on nuget.org (`Apache.Avro`, `Apache.Arrow` and `ApacheThrift` are all unverified), so a push under this id will be accepted. Only `Apache.NMS*` and `Apache.Ignite*` are reserved, and neither covers it. ## Why there is a managed launcher A .NET tool package needs a managed entry point, and the payload is a native executable. So the package carries a small launcher that finds `thrift.exe` next to itself and hands over — arguments, standard streams and exit code all pass straight through. .NET 10 *can* name a native executable as the entry point directly, with no managed code (`ToolCommandRunner=executable`). I tested it: a package built that way **cannot be installed by the .NET 8 or .NET 9 SDK at all** — `unsupported runner 'executable'` — and the two MSBuild properties involved are undocumented. The launcher costs one 72 KB assembly and works on every supported SDK, so the package targets `net8.0`. ## Platforms The package carries the Windows compiler only, and NuGet installs a tool package on any platform without gating. So the launcher checks and says so, pointing at the download page, rather than failing to start a Windows executable. If the compiler is built for other platforms later, .NET 10's RID-specific tool packages turn this into an install-time refusal without changing the install command. ## Testing `dotnet-tool/test-dotnet-tool.ps1` looks inside the `.nupkg` and then **installs it into a throwaway directory and runs it**. Two things can go wrong with a tool package wrapping a native executable, and neither shows up before somebody installs it: the payload can be missing, and the layout can be such that the tool installs but does not run. 18 checks: package layout, `DotnetToolSettings.xml` command name and entry point, nuspec id/version/`DotnetTool` package type/licence expression, exactly one copy of the payload, non-empty `LICENSE` and `NOTICE`, install, and run. It runs anywhere PowerShell and the .NET SDK do — I developed it against the real .NET 8 SDK on Linux, where it exercises the platform-guard path; on Windows it checks the compiler's actual output instead. It already earned its keep: it caught that `LICENSE`/`NOTICE` were **not** being packed by the `<None Pack="true">` items I first wrote, because a tool package is built from the *publish* output and does not use the ordinary pack file set. They now travel as content and land next to the executable, where the user actually gets them. ## Publishing `release: published`, not a pre-release — the gate `pypi.yml`, `release_rust.yml` and `release_ruby.yml` use. The workflow refuses to publish when the release tag and the version in `CMakeLists.txt` disagree, as `release_rust.yml` does for the crate. Because **a NuGet version can never be replaced**, the compiler is asserted self-contained *before* the package is built, not only afterwards in `cmake.yml`. Authentication is NuGet **trusted publishing** via `NuGet/login` (on the ASF allowlist), matching PyPI in `pypi.yml` and crates.io in `release_rust.yml` — no long-lived key is stored in the repo. ### What is needed before the first release - a trusted publishing policy on nuget.org for `apache/thrift`, the `.github/workflows/dotnet-tool.yml` workflow and the `release` environment; - a repository variable `NUGET_USER` naming the nuget.org account that owns it. Both are noted in `doc/ReleaseManagement.md`. If trusted publishing turns out not to be workable for a not-yet-existing package, swapping in an API-key secret is a three-line change — tell me and I will. ## Notes - `.gitignore` gains the tool project's `bin`/`obj`. - No version in the `.csproj`; it arrives on the command line, so `build/veralign.sh` gains nothing to keep in step. - Building without `-Compiler` fails loudly rather than producing a package with no compiler in it. - `zizmor` is clean on the new workflow. JIRA: [THRIFT-6313](https://issues.apache.org/jira/browse/THRIFT-6313) 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
