Skip to main content

hyperi-ci

One CLI for all your CI. Python, Rust, TypeScript, Go — same tool locally and in GitHub Actions. No bash scripts, no composite actions, no submodules.

What's New in v2.0

Version-first single-run pipeline. A Release: true git trailer on your head commit is the single signal that a push is a release. The CI run predicts the next version up front, stamps it into Cargo.toml / VERSION / pyproject.toml / package.json before the build, then tags

  • uploads to all configured registries — all in one workflow. No second "catch-up" build, no version-stamp drift between binary and tag.

Tag-on-publish. A git tag exists iff the artefact is in the registry. Aligns with kubernetes / rust / python OSS conventions. No more orphan tags from "tag every fix:, publish later" mode.

100% FOSS pipeline. Every artefact publishes to public registries: crates.io, PyPI, npm, GHCR, GitHub Releases, and Cloudflare R2 (downloads.hyperi.io). The legacy publish.target knob is accepted in .hyperi-ci.yaml for backward compatibility but ignored at runtime — JFrog publishing was removed in v2.1.4. The only switch left to flip for full open-source visibility is making the source repos themselves public.

See docs/migration/onboarding.md for the v1 → v2 migration. Pre-v2.1.4 docs that mention JFrog targets, the destinations_internal block, or target: internal are historical only — those code paths have been removed.

Why Use This

You get:

  • One command before every push: hyperi-ci check
  • Same quality / test / build runs locally as in CI — no "works on my machine"
  • Automatic versioning via semantic-release (just use conventional commits)
  • One-shot release: hyperi-ci push --release (single CI run, single tag, single registry upload)
  • Commit message validation that actually helps ("Computer says no.")

Your repo gets:

  • A 5-line GitHub Actions workflow (calls our reusable workflow)
  • A Makefile with make check, make quality, make test, make build
  • Semantic-release config that just works
  • A commit hook that catches bad messages before they hit CI

Preventative, Not Detective

hyperi-ci is the preventative layer: gitleaks blocks a secret before the push, commit validation blocks a bad message before it lands, quality gates block a broken build before it merges. GitHub's native security features (secret scanning alerts, CodeQL code scanning, Dependabot) are the detective layer - they find what already landed, or what arrived from upstream in repos hyperi-ci never runs on (external forks, mirrors). Run both: hyperi-ci stops you making the mess, the GitHub side catches the mess you inherited.

Install

uv tool install hyperi-ci

Set Up a Project

cd my-project
hyperi-ci init                          # Auto-detects language, generates everything
git config core.hooksPath .githooks     # Activate commit validation hook

This creates .hyperi-ci.yaml, Makefile, .github/workflows/ci.yml, and .githooks/commit-msg. Commit and push. No .releaserc is scaffolded - version bumps follow semantic-release's own default rules (a repo commits a .releaserc.json only for a genuine exception).

Daily Workflow

# 1. Write code
# 2. Check before pushing (mandatory)
hyperi-ci check                         # Quality + test
hyperi-ci check --quick                 # Quality only (fast)
hyperi-ci check --full                  # Quality + test + build
hyperi-ci check --strict                # Also fail on warn-tier findings (zero warnings)

# 3. Commit (hook validates your message format)
git commit -m "fix: resolve timeout in auth handler"

# 4. Push (ships nothing -- no tag, no release)
hyperi-ci push

# That's it. Quality and test run if the pushed range is release-worthy.
# Nothing compiles, no image is built, no tag, no registry is touched.

Releasing

You opt in to a release explicitly. Two ways:

Primary: hyperi-ci push --release

git commit -m "fix: handle empty tenant id"
hyperi-ci push --release        # --publish still works

This amends your head commit with the Release: true git trailer, then pushes. The single CI run:

  1. Reads the trailer in setup → declares this a release run
  2. Runs npx semantic-release --dry-run to predict the next version (e.g. v1.5.4)
  3. Stamps that version into Cargo.toml + VERSION before build
  4. Builds (binary now embeds CARGO_PKG_VERSION = 1.5.4)
  5. Builds + pushes container image to GHCR (multi-arch)
  6. Runs npx semantic-release for real → creates tag, CHANGELOG commit
  7. Uploads binaries to GitHub Release + R2; publishes to crates.io / PyPI / npm

One workflow run, one tag, one release.

Forced bump: ship a release with no release-worthy commits

Sometimes the work you want to ship is a docs-only PR, a refactor, or just a "force a rebuild" — none of which warrant a semver bump under conventional-commits rules. To avoid having to invent a fake fix: commit:

hyperi-ci push --bump-patch        # +0.0.1 even with docs:/chore: commits
hyperi-ci push --bump-minor        # +0.1.0

Either flag implies --release. Under the hood, the tool adds an empty fix(release): force patch bump (or feat(release): force minor bump) commit on top of HEAD with the Release: true trailer. semantic-release sees that and cuts the version. Honest git history: the marker commit explicitly states "this is a forced bump."

Major bumps are deliberately excluded from this flag — they require a human-written BREAKING CHANGE: footer per HyperI commit-type discipline.

Secondary: re-release an existing tag

If a previous release run failed mid-way (e.g. registry timeout) and you want to retry without re-tagging:

hyperi-ci release v1.5.4         # `publish` still works

This dispatches a workflow_dispatch event for the tag and runs build → container → publish from the existing tagged source.

hyperi-ci release --list         # see unreleased tags

What pushes WITHOUT --release

A plain hyperi-ci push ships nothing and builds nothing -- run-build is release-only, so no cargo compile and no container job runs at all. Quality and test run when the pushed range is release-worthy (it carries a feat:, fix: or perf:), and skip entirely when it is not.

So the default state of main is "landed, and tested if it was release-worthy" -- not "built and ready to ship". You release explicitly by running hyperi-ci push --release on the next conventional commit.

Commit Messages

Conventional commits are enforced by a git hook and CI. The format:

<type>: <description>
<type>(scope): <description>

Get it wrong and you'll hear about it:

Computer says no.

  Unknown commit type: "yolo"

  Did you mean one of these?
    style  — code formatting, linting, cosmetic changes
    spike  — experimental, throwaway investigation

Types that bump the version: feat: (minor), fix: (patch), perf:, hotfix:, security: / sec: (all patch).

Types that don't: docs, test, refactor, chore, ci, build, deps, style, revert, wip, cleanup, data, debt, design, infra, meta, ops, review, spike, ui.

Full list: hyperi-ci check-commit --list

Release Channels

Control where artifacts go with one line in .hyperi-ci.yaml:

release:
  channel: release    # alpha | beta | release

The namespace was publish: and still works -- a publish: block folds into release: at load time and names each key it moved. Nothing has to change to keep building.

Release destinations

Every artefact publishes to the OSS registry stack:

Artefact type Destination
Containers GHCR (ghcr.io/<org>)
Rust crates crates.io
Python packages PyPI
npm packages npmjs.com
Binaries (per-tag) GitHub Releases
Binaries (web-downloadable) Cloudflare R2 (downloads.hyperi.io)
Helm charts OCI under GHCR

The publish.target config field is still accepted in .hyperi-ci.yaml for backward compatibility — values like internal or both are read, preserved on the CIConfig object, and silently routed to the OSS destination map. JFrog publishing was removed in v2.1.4. It is a different thing from the publish-target workflow input, which is still live. The only remaining toggle for full FOSS visibility is making the source repos themselves public on GitHub.

Channel behaviour

Pre-release channels (alpha, beta) flag GH Releases as prerelease and prefix R2 paths. Stable releases require channel: release.

Channel GH Release R2 path
alpha Prerelease /{project}/alpha/v1.3.0/
beta Prerelease /{project}/beta/v1.3.0/
release GA /{project}/v1.3.0/ + /{project}/latest/

Graduating to GA

alpha -> beta -> release

Each step is a one-line change to release.channel in .hyperi-ci.yaml. No code changes, no workflow changes.

Commands

Command What it does
hyperi-ci check Pre-push validation (quality + test)
hyperi-ci check --quick Quality only
hyperi-ci check --full Quality + test + build
hyperi-ci check --strict Also fail on warn-tier findings - see docs/quality-gate.md
hyperi-ci push Push -- ships nothing, quality + test if release-worthy
hyperi-ci push --release Stamp Release: true trailer, push, single-run release
hyperi-ci push --bump-patch Force +0.0.1 release even with no-bump commits
hyperi-ci push --bump-minor Force +0.1.0 release even with no-bump commits
hyperi-ci push --no-ci Push with [skip ci] (skip CI entirely)
hyperi-ci release <tag> Retroactive: dispatch a release on an existing tag
hyperi-ci release --list List unreleased version tags
hyperi-ci run quality|test|build|generate|container|publish Run a single stage locally
hyperi-ci init-contract --app-name <name> Scaffold ci/deployment-contract.json (Tier 3)
hyperi-ci emit-artefacts <output-dir> Generate Dockerfile + chart + ArgoCD app from contract
hyperi-ci stitch <dir> Compose a deployment topology into an umbrella Helm chart
hyperi-ci init-gitops <dir> Scaffold a new gitops monorepo
hyperi-ci init-topology <name> Scaffold a new topology in existing gitops repo
hyperi-ci check-commit --list Show all accepted commit types
hyperi-ci detect Show detected language
hyperi-ci config Show merged config
hyperi-ci trigger [--watch] [--input K=V] Trigger CI workflow; --input once per workflow_dispatch input
hyperi-ci watch [RUN_ID] [--workflow NAME] Watch HEAD's own CI run (default 3600s; --timeout 0 disables)
hyperi-ci rerun [RUN_ID] [--all] Re-run a run's failed jobs — for infra incidents, not for repeating a flake
hyperi-ci logs [RUN_ID] [--workflow NAME] [--failed] Show CI run logs for HEAD's own run
hyperi-ci init Scaffold a new project
hyperi-ci update Update to the channel's release (see autoupdate)
hyperi-ci autoupdate [status|channel live|stable|freeze|unfreeze] Show/set how the CLI updates itself

watch and logs resolve the run built from the commit at HEAD, pinned to the workflow declared in the project's .github/workflows/ci.yml - never "whichever ran last". Name another with --workflow; where the choice is still ambiguous they refuse and list the candidates.

hyperi-ci release is the canonical verb. hyperi-ci publish still works and warns. An earlier notice deprecated release for removal; that was the wrong way round and is withdrawn -- release is the name that stays.

Every old spelling keeps working: the Publish: true trailer, a publish: config block, push --publish, and hyperi-ci publish. Each warns and names its replacement. No project has to change anything to keep building.

How It Works

Your Project                          hyperi-ci
├── .github/workflows/ci.yml          ├── .github/
│   (5 lines — calls reusable)        │   ├── workflows/
├── .hyperi-ci.yaml                   │   │   ├── rust-ci.yml         (per-language)
├── .githooks/commit-msg              │   │   ├── python-ci.yml       (per-language)
└── Makefile                          │   │   ├── go-ci.yml           (per-language)
                                      │   │   ├── ts-ci.yml           (per-language)
                                      │   │   └── _release-tail.yml   (shared: container + publish)
                                      │   └── actions/
                                      │       └── predict-version/    (shared composite)
                                      └── src/hyperi_ci/
                                          ├── cli.py                  (entry point)
                                          ├── dispatch.py             (stage router)
                                          ├── push.py                 (push --release)
                                          ├── publish/                (binaries + retro dispatch)
                                          ├── container/              (docker build/push)
                                          ├── deployment/             (contract / artefact gen)
                                          └── languages/              (per-language stage handlers)

Pipeline (push to main, no Release: true trailer)

flowchart LR
    P[plan] -->|release-worthy| Q[quality]
    P -->|release-worthy| T[test]
    P -->|not release-worthy| S[everything skips]

No build, no container, no tag, no registry upload. A release-worthy merge is TESTED, not shipped -- run-build is release-only, so nothing compiles and no image is produced until you release.

Pipeline (push to main with Release: true trailer, OR workflow_dispatch)

flowchart LR
    Q[quality] --> S["setup<br/>(predict next-version)"]
    T[test] --> S
    S --> B["build<br/>(stamp version)"]
    B --> C["container<br/>(push to registries)"]
    C --> TP["tag-and-publish<br/>(semantic-release + run publish)"]

One workflow, one tag, one release.

Config

.hyperi-ci.yaml in the project root. Cascade (highest wins):

CLI flags -> ENV vars (HYPERCI_*) -> .hyperi-ci.yaml -> defaults.yaml -> hardcoded
language: rust              # Auto-detected if omitted
release:                    # was `publish:` -- still accepted, warns
  enabled: true
  target: oss               # legacy no-op, any value routes to OSS
  channel: release          # alpha | beta | release
build:
  strategies: [native]
  rust:
    targets:
      - x86_64-unknown-linux-gnu
      - aarch64-unknown-linux-gnu
quality:
  gitleaks: blocking

Container Builds & Deployment Artefacts

Every app emits its container image, Helm chart, and ArgoCD Application from a single language-agnostic JSON contract — ci/deployment-contract.json. The Build stage regenerates these via hyperi-ci run generate, and the Quality stage drift-checks the committed ci/ against the contract.

Three-tier producer model (auto-detected):

Tier Detected by Producer
Tier 1 (rust) Cargo.toml + scalo dep <app> generate-artefacts (scalo)
Tier 2 (python) pyproject.toml + scalo dep <app> generate-artefacts (scalo)
Tier 3 (other) ci/deployment-contract.json only hyperi-ci emit-artefacts
(none) nothing container stage no-ops silently

All three tiers emit byte-identical output for the same JSON contract — verified by the cross-tier parity test suite.

For Tier 3 onboarding: hyperi-ci init-contract --app-name my-app scaffolds a starter ci/deployment-contract.json, then commit it and run hyperi-ci emit-artefacts ci/ to regenerate.

See docs/deployment/contract.md for the user guide.

Images push to GHCR (ghcr.io/hyperi-io/<app>). Tags:

  • Push to main with Release: true: :vX.Y.Z + :latest + :sha-abc1234
  • workflow_dispatch on tag: same tag set on the existing tagged source

Enable in .hyperi-ci.yaml:

release:
  container:
    enabled: auto    # auto | true | false
    platforms: [linux/amd64, linux/arm64]

Languages

Language Quality Test Build Publish
Python ruff, ty, bandit, pip-audit pytest uv build uv publish (PyPI)
Rust cargo fmt, clippy, audit, deny, feature_matrix cargo test/nextest cargo build (cross) cargo publish (crates.io)
TypeScript eslint, prettier, tsc, npm audit vitest/jest npm/pnpm build npm publish (npmjs / GH Packages)
Go (beta) gofmt, go vet, golangci-lint, gosec go test -race go build (cross) go proxy, gh release

Go support is beta — functional but not battle-tested to the same degree as Rust, Python, and TypeScript. Verify results carefully on production pipelines.

Per-language version stamping (release runs only):

Language Stamps
Rust Cargo.toml [package].version (and [workspace.package].version for workspaces) + VERSION
Python pyproject.toml [project].version + VERSION
Go VERSION (consumed via -ldflags "-X main.Version=...")
TypeScript package.json (via npm version --no-git-tag-version) + VERSION

Rust Feature Matrix Check

Rust projects automatically get a cargo hack --each-feature --no-dev-deps check --lib pass during quality checks. This catches feature-gating bugs where a module behind feature X uses a crate only declared by feature Y — without this check, transitive deps from other features mask the bug until a downstream consumer enables only X.

Default behaviour (always on, zero config): runs the bare-crate check (cargo check --no-default-features --lib) plus the each-feature pass.

Opt out (requires a reason; CI fails if reason is missing):

quality:
  rust:
    feature_matrix:
      enabled: false
      reason: "tracked in dfe-loader#87, remediating 2026-04-18"

Cross-Compilation

Rust projects with C/C++ dependencies (librdkafka, openssl, zstd) are supported. The build handler auto-detects native -dev packages, downloads cross-arch equivalents into a private sysroot, and sets all compiler/linker environment variables. Configure targets in .hyperi-ci.yaml:

build:
  rust:
    targets:
      - x86_64-unknown-linux-gnu
      - aarch64-unknown-linux-gnu

Push to main without Release: true builds amd64 only (validation). Release runs build the full matrix.

Design Principles

  1. Version-first — predict version up front, stamp before build. No catch-up rebuild.
  2. Tag-on-publish — git tags exist iff the artefact is in the registry.
  3. No silent skips — fail loud on broken handoffs (missing artefacts, missing handlers, etc.).
  4. No bash — all CI logic is Python. subprocess.run() with list args.
  5. Semantic release — push to main with Release: true triggers a single-run release.
  6. uv for everything — venv, sync, lock, tool install, build.
  7. Cross-platform — Linux (CI) and macOS (dev).
  8. Self-hosting — hyperi-ci uses itself for its own CI.

Contributing

CONTRIBUTING.md has the commit format, the DCO sign-off, and what a local hyperi-ci check green does and does not promise.

It also carries a For coding agents section, which is binding rather than advisory: the boy-scout rule for the ci-test-* fixtures, the DO-NOT-FIX exception for planted failures, the fixture-git wrapper, and why a green test suite does not prove a workflow change. Read it before touching a fixture.

Licence

This software is licensed under the Business Source License 1.1 (BUSL-1.1). See LICENSE for terms and COMMERCIAL.md for commercial use; each version converts to Apache 2.0 three years after its release. (c) 2026 HYPERI PTY LIMITED.

Release files for hyperi-ci 2.10.7

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for hyperi-ci 2.10.7
File Size Uploaded
hyperi_ci-2.10.7.tar.gz 1.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for hyperi-ci 2.10.7
File Interpreter ABI Platform
hyperi_ci-2.10.7-py3-none-any.whl Python 3 none any Details

Total release size: 1.6 MB

Release files / hyperi_ci-2.10.7.tar.gz

Download URL hyperi_ci-2.10.7.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
1c46dc54d2cc3717fc5305a0ae6c5eed2f4f0b43be72631c88ff91dc16f190bd
BLAKE2b-256 checksum
How to use checksums
a8a921076e7dace27aed1966c354760c0831a38ed1624ab5a0ae70153f84f1e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / hyperi_ci-2.10.7-py3-none-any.whl

Download URL hyperi_ci-2.10.7-py3-none-any.whl
Size 580.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
186fc08064d81ef97bc746fad3f5477ff36fcc3c5680e778450a7d272d63b7d2
BLAKE2b-256 checksum
How to use checksums
7789021ebf924cc5143ec3a04a838b0ee9fcf1d6589368b6a95306b50cf75ee6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

2.10.9

2 release files

2.10.8

2 release files

This release

2.10.7 This release

2 release files

2.10.6

2 release files

2.10.5

2 release files

2.10.4

2 release files

2.10.3

2 release files

2.10.2

2 release files

2.10.1

2 release files

2.10.0

2 release files

2.9.28

2 release files

2.9.27

2 release files

2.9.26

2 release files

2.9.25

2 release files

2.9.24

2 release files

2.9.23

2 release files

2.9.22

2 release files

2.9.21

2 release files

2.9.20

2 release files

2.9.12

2 release files

2.9.11

2 release files

2.9.10

2 release files

2.9.9

2 release files

2.9.8

2 release files

2.9.7

2 release files

2.9.6

2 release files

2.9.5

2 release files

2.9.4

2 release files

2.9.3

2 release files

2.9.2

2 release files

2.9.1

2 release files

2.9.0

2 release files

2.8.3

2 release files

2.8.2

2 release files

2.8.1

2 release files

2.8.0

2 release files

2.7.3

2 release files

2.7.2

2 release files

2.7.1

2 release files

2.7.0

2 release files

2.6.15

2 release files

2.6.14

2 release files

2.6.13

2 release files

2.6.10

2 release files

2.6.4

2 release files

2.6.3

2 release files

2.6.1

2 release files

2.4.2

2 release files

2.4.1

2 release files

2.3.12

2 release files

2.3.10

2 release files

2.3.8

2 release files

2.3.7

1 release file

2.2.3

2 release files

2.2.2

2 release files

2.2.1

2 release files

2.2.0

2 release files

2.1.6

2 release files

2.1.5

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.13.2

2 release files

1.13.1

2 release files

1.13.0

2 release files

1.12.2

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.2

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.7

2 release files

1.10.6

2 release files

1.10.4

2 release files

1.10.2

2 release files

1.10.1

2 release files

1.10.0

2 release files

1.9.6

2 release files

1.9.5

2 release files

1.9.4

2 release files

1.9.2

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.3

2 release files

1.4.10

2 release files

1.4.4

2 release files

1.4.3

2 release files

1.4.2

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.26

2 release files

1.1.25

2 release files

1.1.24

2 release files

1.1.23

2 release files

1.1.22

2 release files

1.1.21

2 release files

1.1.20

2 release files

1.1.19

2 release files

1.1.18

2 release files

1.1.17

2 release files

1.1.16

2 release files

1.1.15

2 release files

1.1.14

2 release files

1.1.13

2 release files

1.1.12

2 release files

1.1.11

2 release files

1.1.10

2 release files

1.1.9

2 release files

1.1.7

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.49

2 release files

1.0.48

2 release files

1.0.47

2 release files

1.0.46

2 release files

1.0.45

2 release files

1.0.44

2 release files

1.0.43

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page