Skip to content

Latest commit

 

History

History
202 lines (150 loc) · 16.3 KB

release.md

File metadata and controls

202 lines (150 loc) · 16.3 KB

Release

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.Z tags 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, geniex-android-aar-<tag>.aar, geniex-bench-{linux,windows,android}-arm64-<tag>.{tar.gz,zip}, geniex-pysdist{,-llama_cpp,-qairt}-<tag>.tar.gz, and per-file .sha256 sidecars.
  • The CPU-only variant (#1217) adds a -cpu infix on the Linux and Android assets: geniex-{sdk,cli}-linux-arm64-cpu-<tag>.* and geniex-android-aar-cpu-<tag>.aar. Docker mirrors it as the -cpu tag; Maven Central publishes only the default AAR. No geniex-bench archive: on Linux the SDK zip already ships bin/geniex-bench, and no device in the benchmark fleet needs a CPU-only build.
  • Re-run a tag via Actions → Release → Run workflow: set Use workflow from to the tag (Tags tab) and pass the same tag as release_tag. resolve-tag rejects a dispatch whose commit differs from the tag's, and refuses a stable tag whose release already has assets. Prereleases can be re-run freely; a shipped stable tag can't, so cut a higher one.
  • 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.

Versioning

Tags follow SemVer 2.0 with a v prefix: vX.Y.Z for stable, vX.Y.Z-<channel>.<n> for pre-releases.

Digits

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.

Channels

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.n on main before stable.
  • A stable tag must be on a commit whose HTP bundle is Microsoft-signed, not self-signed. Check the last rc: its release body's "Hexagon HTP" section says which, and a self-signed release carries ggml-htp-v1.cer and a -selfsigned SDK zip (gh release view <rc> --json assets). If it's self-signed, finish Hexagon HTP signing first.
  • Never re-use or move a published tag. Retract via a new patch.
  • alpha.n tags on feature branches are disposable — don't rebase the tagged commit afterward.

Decision procedure

This is the algorithm /release follows. Apply in order.

  1. Find the latest stable tag v0.A.B after fetching (stale local tags give wrong proposals):

    git fetch --tags --prune origin
    git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | 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, target v0.1.0 and skip to step 3.

  2. Pick the target X.Y.Z from git log v0.A.B..HEAD --no-merges --format="%h %s" --stat (subject + touched files in one pass; run git show <sha> only if that's still ambiguous):

    • any commit with a breaking change → v0.(A+1).0 (while X = 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.

  3. 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, resetting n). Channels only move forward on a given X.Y.Z;
    • all -rc.n green + HTP Microsoft-signed → cut bare vX.Y.Z.
  4. 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 at v0.(new)-alpha.1 or -rc.1.

  5. Counter n resets per X.Y.Z and per channel: 0.4.0-alpha.{1,2} → 0.4.0-beta.{1} → 0.4.0-rc.{1,2} → 0.4.0.

Worked examples

  • Latest stable v0.3.2. git log v0.3.2..HEAD has one fix: and one docs:. On main, first tag → v0.3.3-rc.1, then v0.3.3.
  • Latest stable v0.3.2. Log contains a feat: adding a new runtime. On a feature branch → v0.4.0-alpha.1; after merge to main → v0.4.0-rc.1 → v0.4.0.
  • On v0.4.0-rc.2, a CLI flag rename (breaking) lands. Target rises from 0.4.0 to 0.5.0. Leave -rc.2 alone; next tag is v0.5.0-alpha.1 or v0.5.0-rc.1 depending on the branch.

S3 mirror & manifest

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.

Layout (flat)

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-sdk-linux-arm64-cpu-<tag>.zip(.sha256) every tag default CPU-only Linux SDK, fetched by GENIEX_SDK_VARIANT=cpu
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)
geniex-cli-linux-arm64-cpu-<tag>.tar.gz(.sha256) every tag default CPU-only 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
geniex-cli-linux-arm64-cpu.tar.gz(.sha256) stable only no-cache Mutable pointer consumed by install.sh --cpu-only
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.

Manifest contract for client apps

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. Compare tag with the locally installed version. Cached no-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[].url always points at the versioned object (never at the mutable geniex-cli.exe / geniex-cli-linux-arm64.tar.gz / install.sh), so the referenced bytes are immutable and the listed sha256 is authoritative. kind is one of sdk / 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 CPU-only objects are on S3 but deliberately absent from the manifest: their metadata would be identical to the default build's, so a kind/platform/arch lookup could hand a normal Snapdragon device the slow artifact. Nothing discovers them that way anyway — install.sh --cpu-only and GENIEX_SDK_VARIANT=cpu both build the URL from the naming convention.

Hexagon HTP signing

The Windows ARM64 SDK ships libggml-htp.cat plus libggml-htp-v{73,75,79,81,85}.so — Windows refuses to load them unsigned. Release CI runs an overlay-htp job before build-cli that sparse-checks-out sdk/signed-htp/libggml-htp-<sha>.zip from the chore/signed-htp-lfs-store branch of qcom-ai-hub/geniex (LFS-tracked), where <sha> is the first 6 characters of the third-party/llama.cpp commit. 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-cli packages them into the installer; release normally.
  • Miss — keep the self-signed build. The SDK name gets a -selfsigned suffix, and the release also carries ggml-htp-v1.cer (users import it) and libggml-htp-to-sign-<sha>.zip (operators submit it for signing).

The signed bundle must contain exactly these six files at the zip root: libggml-htp.cat and libggml-htp-v{73,75,79,81,85}.so — no .inf.

The cross-repo checkout reuses secrets.GH_PAT (already scoped for cross-repo access to qcom-ai-hub/geniex — see publish-s3 below). If CI reports signed=false but the bundle is on chore/signed-htp-lfs-store, first check that GH_PAT has not expired.

Promoting self-signed → Microsoft-signed

  1. Download libggml-htp-to-sign-<sha>.zip from the draft release.
  2. Get it Microsoft (attestation) signed through the internal signing service. The steps are in the team's internal runbook, not in this public repo.
  3. Repack the signed libggml-htp.cat and .so files (no .inf) at the zip root, and commit it to qcom-ai-hub/geniex at sdk/signed-htp/libggml-htp-<sha>.zip on the chore/signed-htp-lfs-store branch (LFS).
  4. Re-run the Release workflow for the same prerelease tag (see the re-run rule above).

Windows installer signing gate

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.

Release checklist

  1. -rc.n on main, HTP Microsoft-signed (promote first if not).
  2. Smoke-test the rc on CPU / GPU / NPU with one small GGUF and one AI Hub qairt model.
  3. Cut bare vX.Y.Z on the same commit.
  4. Manual follow-ups the workflow doesn't do: click Publish for the Maven Central deployment, get the Windows installer Authenticode-signed, replace it on S3, then set windows-signed.txt to true.