A HoruSphere Group company
Build operations for game studios.
Horu CLI instruments Unity and Unreal builds wherever they already run — a developer shell, Jenkins, GitHub Actions, TeamCity, GitLab. Horu CI is the native build system around the same engine. One Go binary and one horu.yaml produce the first useful report, with no server, no Docker and no account in the way.
Horu CLI is at MVP. Horu CI is in development. We are onboarding a small number of studios as design partners, one build lane at a time.
Getting started
horu init --engine unity --platform windows macos --template starter
horu build --platform windows --configuration release
horu report --format text,json,html --out ./out/horu
Three commands and one config file, added to a build lane you already have. The orchestrator stays where it is.
Three properties we will not trade away.
Server-optional
The CLI is a native Go binary. It does not need Docker, an account, a database or a hosted service to produce a report. If Horu disappeared tomorrow your pipeline would still run — that constraint is deliberate, and it is what makes the first evaluation cheap.
Engine-aware
Build targets, host filtering, cache keys derived from Packages/manifest.json and ProjectSettings/ProjectVersion.txt, and log rule packs aimed at Unity licensing, IL2CPP and Addressables failures. Generic CI treats these as your problem.
Evidence-first
A green badge is not a release decision. Every run writes one report directory: stage timings, cache behaviour and miss reasons, sanitation findings, package manifests, artifact checksums, and the failure anchor if there was one.
Architecture
Three ways to run one build engine.
Horu CLI is not a thin API client wrapped around a service. It is the build engine, and it is the same code in all three positions: a developer running a build locally, an existing orchestrator calling it as a step, and a Horu CI runner executing it natively.
That is why moving between them is a change of launcher rather than a migration. The records come out the same shape whichever one started the run, so history collected during evaluation is not thrown away when a team decides Horu should own orchestration.
The build report
What one Horu build actually records.
Not a status. A chain of linked facts, written in order by six commands, that survives the run and can be read by a person or a machine afterwards.
- source context
- stage timing
- runner and tool inventory
- logs and failure anchors
- cache reports
- sanitation reports
- package manifests
- artifact checksums
- report outputs
1.0 Init
Fail in ten seconds, not forty minutes.
Long engine stages fail late for cheap reasons: a missing rsync, an unset UNITY_LICENSE, a disk with no room for the cook step. horu init checks the environment before any of that starts, and scaffolds horu.yaml from an engine template if the project does not have one yet.
Naming note, because it comes up: the command is init, not doctor. Verifying a machine before a release-critical operation is not a support activity.
horu init --engine unity --platform windows macos \
--template steam-windows
- windows
- macos
- linux
- android
- ios
- webgl
- tools.required
- Executables the stage assumes are on PATH —
git, the engine binary,rsync, SDK utilities, signing and upload CLIs. - environment.required
- Variables that must be set, such as
UNITY_LICENSE. An unset licence is a five-second failure, not a thirty-minute one. - disk.min_free_gb
- A floor checked before the run, so a cook or package step does not die halfway through for want of space.
- profile
- Resolved automatically:
ciwhenCI=trueand that profile exists, otherwisedefault. One config file serves both machines. - ProjectVersion.txt
- Unity starter targets read the Editor version from the project, so macOS and Windows builds do not need a
unitybinary resolvable on PATH.
2.0 Build
Run the target matrix, filtered by host.
Each entry in build.targets carries a hosts field. horu build filters to what the current machine can actually produce, so a Windows runner does not start a macOS target and discover the problem at the end. If nothing compatible survives the filter the command fails rather than reporting a hollow success.
-
horu build --dry-runHeadless validation of the resolved targets. Checks the shape of the run without invoking the engine. -
horu build --configuration releaseSelects a configuration and narrows the matrix throughbuild.targets[].configurations. -
horu build --platform macos --configuration releaseNon-dry-run builds require an explicit--platform, and must target that platform. Silence is not a default here. -
horu build --platform macos --configuration release --cleanDisables cache restore and save for this run, which is how you get a clean baseline to compare against.
Two lines worth grepping for
A successful non-dry-run prints Build artifact output: target=<id> path=<path>, so the next stage does not have to guess where the output landed. Every build also prints workspace_affinity_key=<key>, which an external scheduler can use to route repeat jobs back to a machine that already has a warm workspace.
3.0 Run
Turn engine log noise into a failure anchor.
A failed Unity build is usually thousands of lines of asset-import chatter wrapped around one meaningful error. horu run wraps a stage, streams stdout and stderr live while capturing them, collapses known noise by pattern, and classifies what went wrong against a rule pack. The left column is configuration you write once. The right column is what the run records.
logs:
unity:
engine: unity
collapse_patterns:
- "^Refreshing native plugins"
- "^Asset import worker"
failure_packs:
- unity-licensing
- unity-il2cpp
- unity-addressables
- stage
- The stage name you wrapped, so timings are comparable across runs.
- timing
- Wall time for the wrapped command, kept per stage rather than per job.
- exit state
- Process exit preserved as a stable exit code the orchestrator can branch on.
- classification
- Build, asset, package, test, validation or infrastructure — a taxonomy, not a boolean.
- failure anchor
- The first meaningful failure line, surfaced instead of the last thousand repeated ones.
Used inside Jenkins or GitHub Actions, horu run replaces the defensive scripting that accumulates in build files, while leaving the existing orchestrator in charge.
4.0 Cache
Cache behaviour as a record, not runner folklore.
Keys are derived from files you name, not from a convention buried in a runner image. Entries are split per configured path so one bad save does not poison the whole workspace, and high-churn directories are excluded on purpose. Compression defaults to none, because the archive overhead frequently costs more than the transfer it saves.
- restore / save
- Attempted by default whenever cache profiles are configured.
--cleanopts a run out. - warm no-op
- Restore is skipped when the paths are already present, and save is skipped when that key is already stored. A no-op is faster than a correct copy.
- accelerator_endpoint
- When set, Unity Accelerator flags are passed to the engine while Horu still owns restore and save. The two are not mutually exclusive.
- invalidate
horu cache --invalidatedrops an entry deliberately, and the reason is part of the report rather than tribal knowledge.
5.0 Package
Stop shipping files that were never meant to ship.
Sanitation is a package step with a report attached, not a shell one-liner somebody remembers to add to the release lane. remove strips known junk. forbid fails the run when an internal directory or a development config is present in the output. Both decisions are written down, so the answer to “how did that get in the build” is a lookup rather than an investigation.
Manifests carry a sha256 checksum per artifact, which is what makes a later promotion decision about a specific build rather than about a branch name.
remove
Stripped silently. Junk that has no business in a shipped tree.
- **/.DS_Store
- **/Thumbs.db
- **/*.tmp
- **/*.log
forbid
Fails the run. Presence is a defect, not a cleanup task.
- **/InternalOnly/**
- **/*Development*.json
manifest
Written for every package, and used by later gates.
- mode: fail
- manifest: true
- checksum: sha256
horu package --path ./Builds/Windows \
--profile steam-release --sanitize --manifest --validate
6.0 Report
One report, four ways to read it.
Every command contributes to a single build report directory. The console summary is for the CI log. JSON is for machines and for upload. HTML is for the person who has to explain the failure to someone who was not there. GitHub annotations put the anchor on the pull request, where the decision actually happens.
Upload is additive. The report is useful with no server in the picture, and a team can run Horu CLI for months before deciding whether history is worth hosting.
- text
- json
- html
- github
horu report --format text,json,html --out ./out/horu
# on GitHub Actions, annotate the run
horu report --format github,json,html --out ./out/horu
# send the same records to Horu CI for history
horu report --format json --out ./out/horu --upload
- console
- A summary short enough to read in a CI log without scrolling.
- json
- Structured records, and the payload Horu CI ingests.
- html
- A local file you can attach to a ticket or archive as a build artifact.
- annotations
- GitHub Actions annotations pointing at the failure anchor.
- exit codes
- Stable across versions, because pipelines branch on them.
Status
What is built, and what is only written down.
We publish this split because a pre-1.0 build tool that hides its edges is not worth adopting. Everything on the right is documented and specified. None of it is shipping yet, and no part of the argument above depends on it.
Implemented today
- Horu CLI with
init,build,run,cache,packageandreport - Shared build modules behind both the CLI and the runner, so the two cannot drift apart
- Horu CI service: build records, pipeline records, manual runs, source-event triggers, queue items
- Runner pools, leases and heartbeats, with host, container and test executors
- Artifact upload, manifest validation, log upload, stage records, rerun, retry and cancel
- Release candidates and release gate evaluation
- Raw CLI report upload with list and detail history
Documented, not yet built
- Deep Unity and Unreal rule packs beyond the MVP diagnostic baseline
- Remote cache restore and save, with cache evidence to match
- Horu CI runners orchestrating the shared build modules as their native execution path
- Web views for reports, cache analytics and package output
- ReleaseOps, PM, Wiki and VCS as separate product surfaces
Surface area
Facts you can check, rather than outcomes you cannot.
We have no customer metrics to show you and will not invent any. These are statements about what the product is.
horu.yaml, not by a growing verb list.
Products
Two products now. Four documented and deferred.
The deferred four are real designs, not roadmap decoration — and they stay deferred until build evidence is good enough to be worth building on.
Build operations
Documented, deferred
-
Horu ReleaseOps Planned
Release candidates and promotion gates
-
Horu PM Planned
Evidence-backed production management
-
Horu Wiki Planned
Evidence-linked studio knowledge
-
Horu VCS Planned
Source intelligence for builds
Pick one painful build lane.
That is the whole ask. Add the binary and a config file to a single Unity or Unreal lane, compare failure diagnosis and cache visibility before and after, and decide from there. We onboard a small number of studios at a time so that early reports get read by the people who wrote the tool.