コンテンツにスキップ

0019: Distributing the CLI: release binaries, Windows zips and WinGet

Date: 2026-10-12 · Status: accepted

Context

Tenax runs in Kubernetes, but people use the tenax CLI from their own machines, many of them on Windows. Releases published binaries for Linux and macOS only, and the CLI did not build for Windows: internal/proc ran children in Unix process groups.

Decision

  • Windows builds. On Windows a child the CLI starts (cloudflared for tenax sandbox connect) gets its own console process group, and stopping it ends its whole process tree with taskkill /T /F; Windows has no group signals. The controller and data movers stay Linux-only. CI builds and vets the CLI for Windows on every change.
  • Release assets. Every release carries tenax-<os>-<arch> for Linux and macOS, tenax-windows-<arch>.exe and tenax-windows-<arch>.zip (tenax.exe and LICENSE.txt) for amd64 and arm64, the chart, and SHA256SUMS. The zips are reproducible (fixed timestamps, hack/release/mkzip), so their checksums depend only on the binaries.
  • Public downloads, private source. Tenax's repository is private, so its release downloads are too, and package managers download anonymously. Each release copies the CLI downloads (binaries, Windows zips, LICENSE, SHA256SUMS; no source, no chart) to a public repository that holds nothing else (vars.TENAX_PUBLIC_REPO, default republic-global/tenax-cli, written with secrets.TENAX_PUBLIC_RELEASES_TOKEN). Without the token the release stays private and no package is built.
  • WinGet downloads from the public release. The WinGet manifests (TheRepublic.Tenax, portable zip) point at its zips and pin their SHA-256 (hack/release/packages.sh); their metadata links only to the public repository. WinGet is built into Windows 10 and 11, so users need no extra tool.
  • The release job fills the manifests on Linux and attaches them to the private release as tenax-winget-manifests.zip. A maintainer submits them from a Windows machine (GitHub-hosted Windows runners are not available to us), as below. Pre-releases (v1.2.3-rc1) are never submitted.
  • Not now. Chocolatey (it moderates every new version by hand, and packing it needs a Windows runner), a Scoop bucket and a Homebrew tap; code signing (Authenticode) needs a certificate. They can follow without changing the release assets.

Submitting a release to WinGet

From PowerShell on Windows, with the manifests zip from the release unpacked (<dir> is manifests\t\TheRepublic\Tenax\<version>):

winget validate --manifest <dir>
winget settings --enable LocalManifestFiles   # once, as administrator
winget install --manifest <dir>; tenax version  # install it exactly as users will
winget install Microsoft.WingetCreate           # once
wingetcreate submit --token <GitHub token, classic, public_repo scope> <dir>

wingetcreate opens a pull request on microsoft/winget-pkgs; Microsoft's bots validate it and the first version also gets a moderator review. Once it merges, winget install TheRepublic.Tenax installs that version.

Consequences

  • winget install TheRepublic.Tenax works once a version's pull request on winget-pkgs merges; each release needs one manual submission until a Windows runner can do it.
  • Unsigned executables can trigger SmartScreen warnings on first run until a signing certificate is added.
  • Publishing needs the public repository and its token; without them a release still succeeds and stays private. The token used for wingetcreate only opens the pull request on Microsoft's public winget-pkgs; WinGet never needs access to the source.