Skip to content

Add prerelease (beta) channel support to the release feed infrastructure #1027

@Aaronontheweb

Description

@Aaronontheweb

Summary

The release feed infrastructure has no concept of a prerelease channel. Today, any tag pushed — including a semver prerelease like 0.19.0-beta1 — becomes the de-facto "latest" everywhere: the install scripts, the Docker :latest tag, and the GitHub "Latest" release. There is no supported way to publish a beta for opt-in testers without it leaking to every new user.

This was discovered while preparing a 0.19.0-beta1 beta; we abandoned the beta and shipped a normal 0.18.1 patch instead because the pipeline could not safely publish a prerelease.

Why this matters

We want to be able to ship x.y.z-beta.n builds that:

  • are installable by testers who explicitly opt in,
  • do not become the default for fresh curl | sh installs,
  • do not move Docker :latest,
  • show up as a GitHub prerelease, not the repo's "Latest" release.

Concrete gaps

  1. feeds/scripts/generate-release-manifest.sh hardcodes "latest": "${VERSION}". Any tag — prerelease included — becomes manifest.json latest.
  2. scripts/install.sh / scripts/install.ps1 read .latest from the manifest when no explicit version is given. With (1), a fresh curl | sh install would silently pull the prerelease.
  3. .github/workflows/publish_release_binaries.ymlpublish-docker unconditionally tags :latest (and :<major.minor>). A prerelease tag would move :latest and docker pull ghcr.io/netclaw-dev/netclaw would return the beta.
  4. publish_release_binaries.ymlcreate-release hardcodes prerelease: false. A prerelease tag would create a GitHub release marked as the repo's "Latest".
  5. UpdateCheckService.IsNewerVersion (src/Netclaw.Configuration/Feeds/UpdateCheckService.cs) compares versions with System.Version.TryParse, which cannot parse semver prerelease strings (0.19.0-beta1). It currently fails safe (parse failure → "no update"), so prerelease versions can never be offered by the update check — and version comparison is not semver-correct in general.
  6. CI gate Validate tag matches VersionPrefix compares the git tag verbatim against the <VersionPrefix> MSBuild node. A prerelease tag forces <VersionPrefix> to hold the full 0.19.0-beta1 string, even though the semver suffix properly belongs in <VersionSuffix>.

Proposed approach

  • generate-release-manifest.sh: if the version contains a - (prerelease), do not overwrite latest — only append the entry to releases[]. Optionally add a separate latestPrerelease field for an opt-in prerelease channel.
  • publish-docker: omit :latest (and probably :<major.minor>) for prerelease tags; publish only :<full-version>.
  • create-release: set prerelease: dynamically — ${{ contains(github.ref_name, '-') }}.
  • Update check: switch to a semver-aware comparison (NuGet.Versioning.NuGetVersion / SemanticVersion) and explicitly decide prerelease policy — prereleases should only be offered to clients that opt into a prerelease channel, never to stable users.
  • CI version gate: either teach the gate to reconstruct VersionPrefix + VersionSuffix, or accept storing the full prerelease string in <VersionPrefix> (the .NET SDK's GetAssemblyVersion strips the suffix for AssemblyVersion/FileVersion, so the build itself survives).
  • Install scripts: document the NETCLAW_VERSION opt-in for prereleases; optionally support a latestPrerelease channel selector.

Acceptance criteria

A x.y.z-beta.n tag can be published such that:

  • the GitHub release is marked as a prerelease;
  • manifest.json latest is unchanged (still points at the latest stable);
  • Docker :latest is unchanged;
  • a default curl | sh / install.ps1 install is unaffected;
  • the prerelease is installable via an explicit version (NETCLAW_VERSION=x.y.z-beta.n) and/or a documented prerelease channel;
  • the update check never offers a prerelease to a stable client.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dockerDocker image packaging, publishing, and containerized workflowsenhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions code

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions