{"id":46536810,"url":"https://github.com/hyperi-io/hyperi-ci","last_synced_at":"2026-05-29T03:01:33.852Z","repository":{"id":342481995,"uuid":"1173953698","full_name":"hyperi-io/hyperi-ci","owner":"hyperi-io","description":"HyperI CI/CD CLI tool — multi-language build, test, and publish automation","archived":false,"fork":false,"pushed_at":"2026-05-27T22:33:57.000Z","size":1740,"stargazers_count":0,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-28T00:17:41.589Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hyperi-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-05T23:22:11.000Z","updated_at":"2026-05-27T22:33:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hyperi-io/hyperi-ci","commit_stats":null,"previous_names":["hyperi-io/hyperi-ci"],"tags_count":185,"template":false,"template_full_name":null,"purl":"pkg:github/hyperi-io/hyperi-ci","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperi-io%2Fhyperi-ci","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperi-io%2Fhyperi-ci/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperi-io%2Fhyperi-ci/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperi-io%2Fhyperi-ci/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hyperi-io","download_url":"https://codeload.github.com/hyperi-io/hyperi-ci/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hyperi-io%2Fhyperi-ci/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33634611,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-07T00:03:27.805Z","updated_at":"2026-05-29T03:01:33.846Z","avatar_url":"https://github.com/hyperi-io.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hyperi-ci\n\nOne CLI for all your CI. Python, Rust, TypeScript, Go — same tool locally\nand in GitHub Actions. No bash scripts, no composite actions, no submodules.\n\n## What's New in v2.0\n\n**Version-first single-run pipeline.** A `Publish: true` git trailer on\nyour head commit is the single signal that a push is a release. The CI\nrun predicts the next version up front, stamps it into Cargo.toml /\nVERSION / pyproject.toml / package.json **before** the build, then tags\n+ uploads to all configured registries — all in one workflow. No second\n\"catch-up\" build, no version-stamp drift between binary and tag.\n\n**Tag-on-publish.** A git tag exists iff the artefact is in the\nregistry. Aligns with kubernetes / rust / python OSS conventions. No\nmore orphan tags from \"tag every fix:, publish later\" mode.\n\n**100% FOSS pipeline.** Every artefact publishes to public registries:\ncrates.io, PyPI, npm, GHCR, GitHub Releases, and Cloudflare R2\n(`downloads.hyperi.io`). The legacy `publish.target` knob is accepted\nin `.hyperi-ci.yaml` for backward compatibility but **ignored at\nruntime** — JFrog publishing was removed in v2.1.4. The only switch\nleft to flip for full open-source visibility is making the source\nrepos themselves public.\n\nSee [docs/MIGRATION-GUIDE.md](docs/MIGRATION-GUIDE.md) for the v1 → v2\nmigration. Pre-v2.1.4 docs that mention JFrog targets, the\n`destinations_internal` block, or `target: internal` are historical\nonly — those code paths have been removed.\n\n## Why Use This\n\n**You get:**\n\n- One command before every push: `hyperi-ci check`\n- Same quality / test / build runs locally as in CI — no \"works on my machine\"\n- Automatic versioning via semantic-release (just use conventional commits)\n- One-shot publish: `hyperi-ci push --publish` (single CI run, single tag, single registry upload)\n- Commit message validation that actually helps (\"Computer says no.\")\n\n**Your repo gets:**\n\n- A 5-line GitHub Actions workflow (calls our reusable workflow)\n- A Makefile with `make check`, `make quality`, `make test`, `make build`\n- Semantic-release config that just works\n- A commit hook that catches bad messages before they hit CI\n\n## Install\n\n```bash\nuv tool install hyperi-ci\n```\n\n## Set Up a Project\n\n```bash\ncd my-project\nhyperi-ci init                          # Auto-detects language, generates everything\ngit config core.hooksPath .githooks     # Activate commit validation hook\n```\n\nThis creates `.hyperi-ci.yaml`, `Makefile`, `.github/workflows/ci.yml`,\n`.releaserc.yaml`, and `.githooks/commit-msg`. Commit and push.\n\n## Daily Workflow\n\n```bash\n# 1. Write code\n# 2. Check before pushing (mandatory)\nhyperi-ci check                         # Quality + test\nhyperi-ci check --quick                 # Quality only (fast)\nhyperi-ci check --full                  # Quality + test + build\n\n# 3. Commit (hook validates your message format)\ngit commit -m \"fix: resolve timeout in auth handler\"\n\n# 4. Push (validate-only — no tag, no publish)\nhyperi-ci push\n\n# That's it. CI runs quality, test, build, and validates the container\n# Dockerfile. No tag is created. No registry is touched.\n```\n\n## Publishing a Release\n\nYou opt in to a release explicitly. Two ways:\n\n### Primary: `hyperi-ci push --publish`\n\n```bash\ngit commit -m \"fix: handle empty tenant id\"\nhyperi-ci push --publish        # alias: --release\n```\n\nThis amends your head commit with the `Publish: true` git trailer, then\npushes. The single CI run:\n\n1. Reads the trailer in setup → declares this a publish run\n2. Runs `npx semantic-release --dry-run` to predict the next version (e.g. v1.5.4)\n3. Stamps that version into Cargo.toml + VERSION before build\n4. Builds (binary now embeds CARGO_PKG_VERSION = 1.5.4)\n5. Builds + pushes container image to GHCR (multi-arch)\n6. Runs `npx semantic-release` for real → creates tag, CHANGELOG commit\n7. Uploads binaries to GitHub Release + R2; publishes to crates.io / PyPI / npm\n\nOne workflow run, one tag, one publish.\n\n### Forced bump: ship a release with no release-worthy commits\n\nSometimes the work you want to ship is a docs-only PR, a refactor, or\njust a \"force a rebuild\" — none of which warrant a semver bump under\nconventional-commits rules. To avoid having to invent a fake `fix:`\ncommit:\n\n```bash\nhyperi-ci push --bump-patch        # +0.0.1 even with docs:/chore: commits\nhyperi-ci push --bump-minor        # +0.1.0\n```\n\nEither flag implies `--publish`. Under the hood, the tool adds an empty\n`fix(release): force patch bump` (or `feat(release): force minor bump`)\ncommit on top of HEAD with the `Publish: true` trailer. semantic-release\nsees that and cuts the version. Honest git history: the marker commit\nexplicitly states \"this is a forced bump.\"\n\nMajor bumps are deliberately excluded from this flag — they require a\nhuman-written `BREAKING CHANGE:` footer per HyperI commit-type discipline.\n\n### Secondary: re-publish an existing tag\n\nIf a previous publish run failed mid-way (e.g. registry timeout) and you\nwant to retry without re-tagging:\n\n```bash\nhyperi-ci publish v1.5.4         # alias: release v1.5.4\n```\n\nThis dispatches a `workflow_dispatch` event for the tag and runs\nbuild → container → publish from the existing tagged source.\n\n```bash\nhyperi-ci publish --list         # see unpublished tags\n```\n\n### What pushes WITHOUT `--publish`\n\nA plain `hyperi-ci push` runs the full pipeline through the build stage\nin **validate-only** mode. Container builds (to catch Dockerfile\nbreakages) but does not push. No tag, no registry upload.\n\nThis means the default state of `main` is \"all green, ready to ship.\"\nYou can release any time by running `hyperi-ci push --publish` on the\nnext conventional commit.\n\n## Commit Messages\n\nConventional commits are enforced by a git hook and CI. The format:\n\n```\n\u003ctype\u003e: \u003cdescription\u003e\n\u003ctype\u003e(scope): \u003cdescription\u003e\n```\n\nGet it wrong and you'll hear about it:\n\n```\nComputer says no.\n\n  Unknown commit type: \"yolo\"\n\n  Did you mean one of these?\n    style  — code formatting, linting, cosmetic changes\n    spike  — experimental, throwaway investigation\n```\n\n**Types that bump the version:** `feat:` (minor), `fix:` (patch), `perf:`,\n`hotfix:`, `security:` / `sec:` (all patch).\n\n**Types that don't:** `docs`, `test`, `refactor`, `chore`, `ci`, `build`,\n`deps`, `style`, `revert`, `wip`, `cleanup`, `data`, `debt`, `design`,\n`infra`, `meta`, `ops`, `review`, `spike`, `ui`.\n\nFull list: `hyperi-ci check-commit --list`\n\n## Publish Channels\n\nControl where artifacts go with one line in `.hyperi-ci.yaml`:\n\n```yaml\npublish:\n  channel: release    # spike | alpha | beta | release\n```\n\n### Publish targets\n\nEvery artefact publishes to the OSS registry stack:\n\n| Artefact type | Destination |\n|---|---|\n| Containers | GHCR (`ghcr.io/\u003corg\u003e`) |\n| Rust crates | crates.io |\n| Python packages | PyPI |\n| npm packages | npmjs.com |\n| Binaries (per-tag) | GitHub Releases |\n| Binaries (web-downloadable) | Cloudflare R2 (`downloads.hyperi.io`) |\n| Helm charts | OCI under GHCR |\n\nThe `publish.target` field is still accepted in `.hyperi-ci.yaml` for\nbackward compatibility — values like `internal` or `both` are read,\npreserved on the `CIConfig` object, and **silently routed to the OSS\ndestination map**. JFrog publishing was removed in v2.1.4. The only\nremaining toggle for full FOSS visibility is making the source repos\nthemselves public on GitHub.\n\n### Channel behaviour\n\nPre-release channels (`spike`, `alpha`, `beta`) flag GH Releases as\nprerelease and prefix R2 paths. Stable releases require `channel: release`.\n\n| Channel | GH Release | R2 path |\n|---|---|---|\n| `spike` | Prerelease | `/{project}/spike/v1.3.0/` |\n| `alpha` | Prerelease | `/{project}/alpha/v1.3.0/` |\n| `beta`  | Prerelease | `/{project}/beta/v1.3.0/` |\n| `release` | GA | `/{project}/v1.3.0/` + `/{project}/latest/` |\n\n### Graduating to GA\n\n```\nspike → alpha → beta → release\n```\n\nEach step is a one-line change to `publish.channel` in `.hyperi-ci.yaml`.\nNo code changes, no workflow changes.\n\n## Commands\n\n| Command | What it does |\n|---|---|\n| `hyperi-ci check` | Pre-push validation (quality + test) |\n| `hyperi-ci check --quick` | Quality only |\n| `hyperi-ci check --full` | Quality + test + build |\n| `hyperi-ci push` | Push (validate-only — no tag, no publish) |\n| `hyperi-ci push --publish` | Stamp `Publish: true` trailer, push, single-run publish |\n| `hyperi-ci push --bump-patch` | Force +0.0.1 release even with no-bump commits |\n| `hyperi-ci push --bump-minor` | Force +0.1.0 release even with no-bump commits |\n| `hyperi-ci push --no-ci` | Push with `[skip ci]` (skip CI entirely) |\n| `hyperi-ci publish \u003ctag\u003e` | Retroactive: dispatch publish on existing tag |\n| `hyperi-ci publish --list` | List unpublished version tags |\n| `hyperi-ci run quality\\|test\\|build\\|generate\\|container\\|publish` | Run a single stage locally |\n| `hyperi-ci init-contract --app-name \u003cname\u003e` | Scaffold `ci/deployment-contract.json` (Tier 3) |\n| `hyperi-ci emit-artefacts \u003coutput-dir\u003e` | Generate Dockerfile + chart + ArgoCD app from contract |\n| `hyperi-ci stitch \u003cdir\u003e` | Compose a deployment topology into an umbrella Helm chart |\n| `hyperi-ci init-gitops \u003cdir\u003e` | Scaffold a new gitops monorepo |\n| `hyperi-ci init-topology \u003cname\u003e` | Scaffold a new topology in existing gitops repo |\n| `hyperi-ci check-commit --list` | Show all accepted commit types |\n| `hyperi-ci detect` | Show detected language |\n| `hyperi-ci config` | Show merged config |\n| `hyperi-ci trigger [--watch]` | Trigger CI workflow |\n| `hyperi-ci watch [--timeout SEC]` | Watch latest CI run (default 3600s; `--timeout 0` disables) |\n| `hyperi-ci logs [--failed]` | Show CI run logs |\n| `hyperi-ci init` | Scaffold a new project |\n| `hyperi-ci upgrade` | Upgrade to latest version |\n\n`hyperi-ci release` is kept as a deprecated alias of `hyperi-ci publish`\nand will be removed in v3.0.\n\n## How It Works\n\n```\nYour Project                          hyperi-ci\n├── .github/workflows/ci.yml          ├── .github/\n│   (5 lines — calls reusable)        │   ├── workflows/\n├── .hyperi-ci.yaml                   │   │   ├── rust-ci.yml         (per-language)\n├── .releaserc.yaml                   │   │   ├── python-ci.yml       (per-language)\n├── .githooks/commit-msg              │   │   ├── go-ci.yml           (per-language)\n└── Makefile                          │   │   ├── ts-ci.yml           (per-language)\n                                      │   │   └── _release-tail.yml   (shared: container + publish)\n                                      │   └── actions/\n                                      │       └── predict-version/    (shared composite)\n                                      └── src/hyperi_ci/\n                                          ├── cli.py                  (entry point)\n                                          ├── dispatch.py             (stage router)\n                                          ├── push.py                 (push --publish)\n                                          ├── publish/                (binaries + retro dispatch)\n                                          ├── container/              (docker build/push)\n                                          ├── deployment/             (contract / artefact gen)\n                                          └── languages/              (per-language stage handlers)\n```\n\n### Pipeline (push to main, no `Publish: true` trailer)\n\n```mermaid\nflowchart LR\n    Q[quality] --\u003e S[setup]\n    T[test] --\u003e S\n    S --\u003e B[build]\n    B --\u003e C[\"container\u003cbr/\u003e(validate-only)\"]\n```\n\nNo tag, no registry upload. Default state of main = \"validated, ready to ship.\"\n\n### Pipeline (push to main with `Publish: true` trailer, OR workflow_dispatch)\n\n```mermaid\nflowchart LR\n    Q[quality] --\u003e S[\"setup\u003cbr/\u003e(predict next-version)\"]\n    T[test] --\u003e S\n    S --\u003e B[\"build\u003cbr/\u003e(stamp version)\"]\n    B --\u003e C[\"container\u003cbr/\u003e(push to registries)\"]\n    C --\u003e TP[\"tag-and-publish\u003cbr/\u003e(semantic-release + run publish)\"]\n```\n\nOne workflow, one tag, one publish.\n\n## Config\n\n`.hyperi-ci.yaml` in the project root. Cascade (highest wins):\n\n```\nCLI flags -\u003e ENV vars (HYPERCI_*) -\u003e .hyperi-ci.yaml -\u003e defaults.yaml -\u003e hardcoded\n```\n\n```yaml\nlanguage: rust              # Auto-detected if omitted\npublish:\n  enabled: true\n  target: oss               # oss (default) | internal | both\n  channel: release          # spike | alpha | beta | release\nbuild:\n  strategies: [native]\n  rust:\n    targets:\n      - x86_64-unknown-linux-gnu\n      - aarch64-unknown-linux-gnu\nquality:\n  gitleaks: blocking\n```\n\n## Container Builds \u0026 Deployment Artefacts\n\nEvery app emits its container image, Helm chart, and ArgoCD `Application`\nfrom a single language-agnostic JSON contract — `ci/deployment-contract.json`.\nThe Build stage regenerates these via `hyperi-ci run generate`, and the\nQuality stage drift-checks the committed `ci/` against the contract.\n\nThree-tier producer model (auto-detected):\n\n| Tier | Detected by | Producer |\n|---|---|---|\n| **Tier 1** (`rust`) | Cargo.toml + `hyperi-rustlib` dep | `\u003capp\u003e generate-artefacts` (rustlib) |\n| **Tier 2** (`python`) | pyproject.toml + `hyperi-pylib` dep | `\u003capp\u003e generate-artefacts` (pylib) |\n| **Tier 3** (`other`) | `ci/deployment-contract.json` only | `hyperi-ci emit-artefacts` |\n| (none) | nothing | container stage no-ops silently |\n\nAll three tiers emit **byte-identical** output for the same JSON contract —\nverified by the cross-tier parity test suite.\n\nFor Tier 3 onboarding: `hyperi-ci init-contract --app-name my-app`\nscaffolds a starter `ci/deployment-contract.json`, then commit it and\nrun `hyperi-ci emit-artefacts ci/` to regenerate.\n\nSee [`docs/deployment-contract.md`](docs/deployment-contract.md) for the\nuser guide.\n\nImages push to GHCR (`ghcr.io/hyperi-io/\u003capp\u003e`). Tags:\n\n- Push to main with `Publish: true`: `:vX.Y.Z` + `:latest` + `:sha-abc1234`\n- workflow_dispatch on tag: same tag set on the existing tagged source\n\nEnable in `.hyperi-ci.yaml`:\n\n```yaml\npublish:\n  container:\n    enabled: auto    # auto | true | false\n    platforms: [linux/amd64, linux/arm64]\n```\n\n## Languages\n\n| Language | Quality | Test | Build | Publish |\n|---|---|---|---|---|\n| Python | ruff, ty, bandit, pip-audit | pytest | uv build | uv publish (PyPI) |\n| Rust | cargo fmt, clippy, audit, deny, **feature_matrix** | cargo test/nextest | cargo build (cross) | cargo publish (crates.io) |\n| TypeScript | eslint, prettier, tsc, npm audit | vitest/jest | npm/pnpm build | npm publish (npmjs / GH Packages) |\n| Go _(beta)_ | gofmt, go vet, golangci-lint, gosec | go test -race | go build (cross) | go proxy, gh release |\n\n\u003e **Go support is beta** — functional but not battle-tested to the same\n\u003e degree as Rust, Python, and TypeScript. Verify results carefully on\n\u003e production pipelines.\n\nPer-language version stamping (publish runs only):\n\n| Language | Stamps |\n|---|---|\n| Rust | `Cargo.toml` `[package].version` (and `[workspace.package].version` for workspaces) + `VERSION` |\n| Python | `pyproject.toml` `[project].version` + `VERSION` |\n| Go | `VERSION` (consumed via `-ldflags \"-X main.Version=...\"`) |\n| TypeScript | `package.json` (via `npm version --no-git-tag-version`) + `VERSION` |\n\n## Rust Feature Matrix Check\n\nRust projects automatically get a `cargo hack --each-feature --no-dev-deps check --lib`\npass during quality checks. This catches feature-gating bugs where a module behind\nfeature `X` uses a crate only declared by feature `Y` — without this check,\ntransitive deps from other features mask the bug until a downstream consumer\nenables only `X`.\n\n**Default behaviour** (always on, zero config): runs the bare-crate check\n(`cargo check --no-default-features --lib`) plus the each-feature pass.\n\n**Opt out** (requires a reason; CI fails if reason is missing):\n\n```yaml\nquality:\n  rust:\n    feature_matrix:\n      enabled: false\n      reason: \"tracked in dfe-loader#87, remediating 2026-04-18\"\n```\n\n## Cross-Compilation\n\nRust projects with C/C++ dependencies (librdkafka, openssl, zstd) are\nsupported. The build handler auto-detects native `-dev` packages, downloads\ncross-arch equivalents into a private sysroot, and sets all compiler/linker\nenvironment variables. Configure targets in `.hyperi-ci.yaml`:\n\n```yaml\nbuild:\n  rust:\n    targets:\n      - x86_64-unknown-linux-gnu\n      - aarch64-unknown-linux-gnu\n```\n\nPush to main without `Publish: true` builds amd64 only (validation).\nPublish runs build the full matrix.\n\n## Design Principles\n\n1. **Version-first** — predict version up front, stamp before build. No catch-up rebuild.\n2. **Tag-on-publish** — git tags exist iff the artefact is in the registry.\n3. **No silent skips** — fail loud on broken handoffs (missing artefacts, missing handlers, etc.).\n4. **No bash** — all CI logic is Python. `subprocess.run()` with list args.\n5. **Semantic release** — push to main with `Publish: true` triggers a single-run release.\n6. **uv for everything** — venv, sync, lock, tool install, build.\n7. **Cross-platform** — Linux (CI) and macOS (dev).\n8. **Self-hosting** — hyperi-ci uses itself for its own CI.\n\n## Licence\n\nThis software is licensed under the Business Source License 1.1 (BUSL-1.1).\nSee [LICENSE](LICENSE) for terms and [COMMERCIAL.md](COMMERCIAL.md) for commercial\nuse; each version converts to Apache 2.0 three years after its release.\n(c) 2026 HYPERI PTY LIMITED.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperi-io%2Fhyperi-ci","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhyperi-io%2Fhyperi-ci","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhyperi-io%2Fhyperi-ci/lists"}