Tag a commit with a v-prefixed SemVer 2.0 string to trigger release.yml:
git tag v1.2.3 && git push origin v1.2.3- Tags containing
-are drafts (and push the sdist to TestPyPI). - Bare
vX.Y.Ztags publish immediately (and push the sdist to production PyPI). - Assets:
geniex-sdk-{linux,windows}-arm64-<tag>.zip,geniex-cli-linux-arm64-<tag>.tar.gz,geniex-cli-setup-windows-arm64-<tag>.exe,*.whl,*.aar, and per-file.sha256sidecars. - Re-running the same tag via Actions → Release → Run workflow is safe as long as you set "Use workflow from" to the tag (Tags tab in the dropdown). Dispatching from
mainis rejected byresolve-tagso the workflow never builds a tag against the wrong commit. - S3 mirror at
s3://qaihub-public-assets/qai-hub-geniex/— see § S3 mirror & manifest below.
For the developer/user side of HTP signing (cert import, test-signing), see run.md § Self-signed fallback.
Tags follow SemVer 2.0 with a v prefix: vX.Y.Z for stable, vX.Y.Z-<channel>.<n> for pre-releases.
| Bump | Meaning | Example triggers |
|---|---|---|
MAJOR (X) |
Breaking change to any public surface. Consumers must adapt. | CLI flag removed/renamed, SDK header signature changed, Python API removed, config key renamed. |
MINOR (Y) |
Backwards-compatible feature addition. | New runtime, new model support, new CLI subcommand, new SDK function. |
PATCH (Z) |
Backwards-compatible fix or cleanup. | Bug fix, dependency bump, doc/CI-only change, internal refactor. |
Pre-1.0 rule. The project is still pre-1.0 (X = 0). Do not bump MAJOR while private/unreleased — keep X = 0. Breaking changes bump MINOR (0.Y → 0.(Y+1), resetting Z to 0) and must be flagged in the release notes. The project graduates to X = 1 only on first public release.
Pre-release channels communicate how ready a build is. Ordering: alpha < beta < rc < stable.
| Channel | Purpose | Allowed branch | Still allowed to change |
|---|---|---|---|
alpha.n |
Share in-progress builds; feature shape may still move. | feature branch or main | Anything, including breaking. |
beta.n |
Feature-complete for the target X.Y.Z; seeking feedback. |
main |
Bug fixes and polish only. |
rc.n |
Release candidate — "will ship unless we find a bug". | main |
Bug fixes only. |
| stable | Published release. | main |
Nothing — cut a new bump. |
Rules:
- Always pass through at least one
-rc.nonmainbefore stable. - A stable tag must be on a commit whose HTP bundle is Microsoft-signed, not self-signed. See Hexagon HTP signing; if unsure, run
gh run list --workflow release.yml --limit 5and confirm the SDK artifact name does not end in-selfsigned. If it does, finish the signing promotion first. - Never re-use or move a published tag. Retract via a new patch.
alpha.ntags on feature branches are disposable — don't rebase the tagged commit afterward.
This is the algorithm /release follows. Apply in order.
-
Find the latest stable tag
v0.A.Blocally — avoids aghround-trip:git tag --sort=-v:refname --list "v[0-9]*.[0-9]*.[0-9]*" | grep -v -- '-' | head -1
Don't use
git describe— it only looks at HEAD's ancestor chain and will miss stable tags cut on side branches. If the command prints nothing, targetv0.1.0and skip to step 3. -
Pick the target
X.Y.Zfromgit log v0.A.B..HEAD --format="%h %s" --stat(subject + touched files in one pass; rungit show <sha>only if that's still ambiguous):- any commit with a breaking change →
v0.(A+1).0(whileX = 0, breaking bumps MINOR); - else any feature commit →
v0.(A+1).0; - else →
v0.A.(B+1).
Read both subjects and diffs when the subject is ambiguous — Conventional-Commits prefixes (
feat:,fix:,feat!:) are a hint, not a contract; the repo does not enforce them. - any commit with a breaking change →
-
Pick the channel by cycle position for that target:
- first tag toward a new target, on a feature branch →
alpha.1; - first tag toward a new target, on
main→rc.1(skip beta unless explicitly requested; most cycles go straight to rc); - already in cycle → advance within the channel (
-rc.1→-rc.2) or move forward one channel (-beta.3→-rc.1, resettingn). Channels only move forward on a givenX.Y.Z; - all
-rc.ngreen + HTP Microsoft-signed → cut barevX.Y.Z.
- first tag toward a new target, on a feature branch →
-
If a breaking change lands mid-cycle and raises the target: abandon the current
X.Y.Z(leave existing tags in place — don't retract) and restart atv0.(new)-alpha.1or-rc.1. -
Counter
nresets perX.Y.Zand per channel:0.4.0-alpha.{1,2}→0.4.0-beta.{1}→0.4.0-rc.{1,2}→0.4.0.
- Latest stable
v0.3.2.git log v0.3.2..HEADhas onefix:and onedocs:. Onmain, first tag →v0.3.3-rc.1, thenv0.3.3. - Latest stable
v0.3.2. Log contains afeat:adding a new runtime. On a feature branch →v0.4.0-alpha.1; after merge tomain→v0.4.0-rc.1→v0.4.0. - On
v0.4.0-rc.2, a CLI flag rename (breaking) lands. Target rises from0.4.0to0.5.0. Leave-rc.2alone; next tag isv0.5.0-alpha.1orv0.5.0-rc.1depending on the branch.
A subset of every release is mirrored to s3://qaihub-public-assets/qai-hub-geniex/ (anonymous-GET via https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/...) so that apps and installers can fetch artifacts without GitHub-API rate limits.
All objects live directly under the prefix — no <tag>/ subdirectories. The <tag> in each filename disambiguates versions.
| Object | Per tag? | Cache | Purpose |
|---|---|---|---|
geniex-sdk-windows-arm64-<tag>.zip(.sha256) |
every tag | default | Windows SDK |
geniex-sdk-linux-arm64-<tag>.zip(.sha256) |
every tag | default | Linux SDK |
geniex-cli-setup-windows-arm64-<tag>.exe(.sha256) |
every tag | default | Windows CLI installer (versioned) |
geniex-cli-linux-arm64-<tag>.tar.gz(.sha256) |
every tag | default | Linux CLI archive (versioned) |
install-<tag>.sh(.sha256) |
every tag | default | Linux install script (versioned, pinned via --version) |
geniex-cli.exe |
stable only | no-cache |
Mutable pointer to latest stable Windows installer |
geniex-cli-linux-arm64.tar.gz(.sha256) |
stable only | no-cache |
Mutable pointer consumed by install.sh |
install.sh |
stable only | no-cache |
Mutable install script — curl ... | sh entrypoint |
manifest-<tag>.json |
every tag | immutable |
Per-tag asset listing |
index.json |
every tag | no-cache |
Full version catalogue |
latest.json |
stable only | no-cache |
Pointer to the latest stable manifest |
windows-signed.txt |
stable only | no-cache |
Code-signing gate for the latest Windows installer — see § Windows installer signing gate |
Other assets (AAR, sdist, HTP cert/to-sign zips) ship via GitHub Releases / Maven Central / PyPI (stable) / TestPyPI (prerelease) only — not via S3.
S3 publishing runs in the geniex repo (the IAM role's OIDC trust only allows qcom-ai-hub/geniex); this repo's publish-s3 job just dispatches it. The schema lives in release_s3_manifest.py on geniex's chore/publish-s3 branch; all files carry schema_version: 1.
-
Update check (lightest) — GET
latest.json. Comparetagwith the locally installed version. Cachedno-cache, so the response is always current. Only present once a stable tag has shipped. -
Version picker (history / rollback) — GET
index.json:{ "schema_version": 1, "updated_at": "2026-05-19T08:23:11Z", "latest_stable": "v0.1.5", "latest_prerelease": "v0.1.6-rc.2", "versions": [ { "tag": "v0.1.6-rc.2", "is_prerelease": true, "released_at": "...", "manifest": "manifest-v0.1.6-rc.2.json" }, { "tag": "v0.1.5", "is_prerelease": false, "released_at": "...", "manifest": "manifest-v0.1.5.json" } ] } -
Asset download — GET the per-tag manifest referenced by
versions[].manifest:{ "schema_version": 1, "tag": "v0.1.5", "is_prerelease": false, "released_at": "...", "llama_sha": "abc123", "htp_signed": true, "assets": [ { "name": "geniex-sdk-windows-arm64-v0.1.5.zip", "url": "https://qaihub-public-assets.s3.us-west-2.amazonaws.com/qai-hub-geniex/geniex-sdk-windows-arm64-v0.1.5.zip", "size": 123456789, "sha256": "...", "kind": "sdk", "platform": "windows", "arch": "arm64" } ] }assets[].urlalways points at the versioned object (never at the mutablegeniex-cli.exe/geniex-cli-linux-arm64.tar.gz/install.sh), so the referenced bytes are immutable and the listedsha256is authoritative.kindis one ofsdk/cli-installer/cli-archive/install-script/sha256.
The per-tag manifest is byte-stable across workflow re-runs of the same tag — released_at is preserved from the first publish, so clients can cache it forever.
The Windows ARM64 SDK ships libggml-htp.cat plus libggml-htp-v{68,69,73,75,79,81}.so — Windows refuses to load them unsigned. Release CI runs an overlay-htp job before build-cli that curls s3://qaihub-public-assets/llama-cpp/libggml-htp-<sha>.zip, where <sha> is the third-party/llama.cpp short SHA. Both the installer and the SDK zip end up with the same HTP files:
- Hit — overlay the Microsoft-signed files into the SDK artifact;
build-clipackages them into the installer; release normally. - Miss — keep the self-signed build. The SDK name gets a
-selfsignedsuffix, and the release also carriesggml-htp-v1.cer(users import it) andlibggml-htp-to-sign-<sha>.zip(operators submit it for signing).
The S3 bundle must contain exactly these eight files at the zip root: libggml-htp.cat, libggml-htp.inf, and libggml-htp-v{68,69,73,75,79,81}.so.
- Download
libggml-htp-to-sign-<sha>.zipfrom the draft release. - Submit for Microsoft signing.
a. Put the
.cat.infand all.sofiles intoATT\libggml-htp\in samba; b. Submit Jenkins pipeline, fill path with\path\to\ATT, other field use default or first param. c. Get singed files fromATT\Glymur\01000\ExtractedDrivers. d. keep files in a zip with the same files (without.inf) at the root. - Upload the result to
s3://qaihub-public-assets/llama-cpp/libggml-htp-<sha>.zip, with public acl. - Re-run the Release workflow for the same tag.
The Windows installer is published before it is Authenticode-signed, so geniex update gates on windows-signed.txt to avoid pushing an unsigned build. On Windows, once a newer version is found, the updater GETs this file (see cli/cmd/geniex/update.go, isWindowsSigned): contents true → proceed with the download; anything else or missing → treat as up-to-date and skip.
Release-side: upload windows-signed.txt with true after the latest stable installer is signed; set it back to a non-true value before publishing the next, not-yet-signed installer.