Files
ci/hanzo.yml
zeekay 909203f2ef Seed the PyPI lane's clean venv with pip
uv venv makes a venv without pip, so the clean install before upload
answered "No module named pip" for every wheel and the lane published
nothing (hanzoai/python-sdk v8.5.162, run 36508815355). The install and
import move into alone(), which bin/alone_test.sh lifts out of the
workflow and runs against wheels it builds.
2026-09-29 10:11:11 -07:00

479 lines
30 KiB
YAML

# Hanzo CI — this repo's own build, driven by this repo's own reusable workflow.
#
# hanzoai/ci is two things that belong together: the reusable pipeline every
# other repo imports (.github/workflows/build.yml), and ci.hanzo.ai, the
# dashboard that shows what that pipeline did. So the dashboard image is built
# by the pipeline it reports on — if the pipeline breaks, the thing that would
# tell you cannot ship, which is the correct and honest coupling.
images:
- name: ci
context: .
dockerfile: Dockerfile
repo: ghcr.io/hanzoai/ci
# Both architectures, one manifest list. The deployment carries no
# nodeSelector, so the kubelet resolves whichever child matches the node it
# lands on; a single-arch image answers `no match for platform in manifest`
# on half the cluster, or runs under qemu where binfmt is registered. The
# default when this line is absent is linux/amd64 alone, which is how the
# v1.0.x series shipped amd64 only.
platforms: [linux/amd64, linux/arm64]
# The runner image: what every CI job runs inside (RUNNER_IMAGE in universe
# charts/app/values/hanzo/cloud.yaml, pinned by digest). Built here like any
# other image, so every change to this repo rebuilds it and nothing is cut by
# hand. Its version is runner/VERSION, patched by bin/imgver on each build.
- name: git-runner
context: runner
dockerfile: runner/Dockerfile
repo: ghcr.io/hanzoai/git-runner
# A runner Job is pinned to the architecture its labels name, so an index
# missing either half strands every job of that architecture at `exec
# format error` before it registers.
platforms: [linux/amd64, linux/arm64]
test:
- name: go-vet
run: |
set -e
export GOWORK=off
go vet ./...
- name: go-unit
# The whole tree, not a named list — an allowlist stops covering whatever is
# added after it is written. Small repo; ./... costs nothing.
#
# scope_test.go is the load-bearing one: it asserts the surface refuses a
# request with no X-Org-Id and that `?org=` can only narrow. Build metadata
# is tenant data — repo names, branches, commit SHAs, actor logins — so a red
# gate here must block the image.
#
# render_test.go is the other one that has to stay green: it pins the
# vendored @hanzo/brand sheet to the hash of the version it claims to be and
# rejects any colour the page names for itself. Both gates are offline —
# checking that we use one design system costs this pipeline no npm, no
# registry and no network.
#
# fleet_test.go pins how the four values are read, and the case it exists for
# is the one where they AGREE: built, declared and running can name the same
# release while main has moved on and nothing since has produced an image.
# It also pins the two readings a board is worthless without — that a run
# failing at `receipt` shipped and one failing at `gate` did not, and that a
# commit with no run is not a commit whose run failed — and the rule that an
# unread source is never drawn as a difference.
#
# declared_test.go covers the values reader against the shapes the fleet's
# files actually take, and is weighted toward refusals: a wrong version on the
# page that exists to say what is declared is worse than a blank, because a
# blank is visibly unknown.
run: |
set -e
export GOWORK=off
go test -count=1 ./...
- name: one-definition
# The pipeline has ONE address: .github/workflows/build.yml. That is the path
# BOTH planes read — github.com resolves a reusable workflow out of
# `.github/workflows` and nowhere else, and the forge reads it as well — so
# one file serves the canonical host and the mirror alike.
#
# A forwarder is what this refuses. Hanzo Git resolves ONE `uses:` hop, not
# two: a caller naming a workflow that itself forwards expands into jobs with
# no labels, no steps and no runner, and reports `failure` with nothing to
# read. Only the cicd lanes can show it — deploy lanes name their runner
# directly and are unaffected — so the refusal has to be mechanical.
#
# So: one file, no hop, and nothing to keep in sync. This asserts the
# definition is here and holds steps, and that no second copy of it has
# appeared under `.hanzo/workflows` to become a second address. The ~7-line
# CALLER lives there — it is what the forge schedules — and a caller is not
# a definition.
run: |
set -e
def=.github/workflows/build.yml
grep -q '^ - name: ' "$def" || {
echo "::error::$def holds no steps — it is the definition, not a forwarder"; exit 1; }
grep -q '^ uses: ' "$def" && {
echo "::error::$def forwards somewhere — Hanzo Git cannot take a second hop, that is what this layout removed"; exit 1; } || true
[ -f .hanzo/workflows/build.yml ] && {
echo "::error::.hanzo/workflows/build.yml is back — it is read by one plane only, and a second address is a second definition to keep in sync"; exit 1; } || true
echo "OK: one definition ($(wc -l < "$def") lines), no forwarder, no second address"
- name: gate-runs-before-delegate
# `mode: delegate` chooses WHERE the image is built. It must never choose
# WHETHER the commit was tested. Those two collapse into one whenever the
# delegate step is ordered ahead of `test:` while the other steps carry
# `if: inputs.mode != 'delegate'` — a delegated run then skips the gate along
# with the build and finishes green having proven nothing. Placement is the
# whole of the property, and placement is what reads as correct in a diff
# that touches one step at a time, so a gate asserts it instead of a reader.
#
# Two assertions, because either one alone can be satisfied while the gate
# still does not run: the delegate POST must come AFTER the test gate, and
# the test gate must not be delegate-guarded.
run: |
set -e
f=.github/workflows/build.yml
t=$(grep -n '^ - name: Test (per hanzo.yml)$' "$f" | cut -d: -f1)
d=$(grep -n '^ - name: Delegate build to the runner (mode=delegate)$' "$f" | cut -d: -f1)
[ -n "$t" ] && [ -n "$d" ] || { echo "::error::cannot find the test gate ($t) or the delegate step ($d) — a rename broke this assertion, fix the assertion"; exit 1; }
[ "$d" -gt "$t" ] || { echo "::error::the delegate step (line $d) runs BEFORE the test gate (line $t) — a delegated build would ship an ungated commit"; exit 1; }
if sed -n "${t}p;$((t+1)),$((t+80))p" "$f" | grep -m1 '^ if: ' | grep -q "mode != 'delegate'"; then
echo "::error::the test gate is guarded by \`inputs.mode != 'delegate'\` — delegate would skip it"; exit 1
fi
echo "OK: test gate at line $t, delegate POST at line $d, gate not delegate-guarded"
- name: inputs-are-strings
# EVERY workflow_call input is `type: string`, and carries a default.
#
# git.hanzo.ai hands a workflow_call input its DECLARED TYPE'S ZERO VALUE
# rather than the value the caller passed. Measured in isolation: a
# `type: boolean` input reads `false` when omitted, `false` when the caller
# passes `true`, and `false` when the polarity is inverted and `true` is
# passed to a `skip_` name. String inputs in the SAME probe resolve correctly
# — `mode` reads `buildx` and `runner` reads its whole JSON array.
#
# So a boolean here is not a type, it is the constant `false`, and any
# `if: … && inputs.<bool>` never runs. A number fails the same way at 0. A
# string's zero value is the empty string, which no comparison in this
# pipeline treats as an instruction — that is why the guards are written as
# `!= 'false'` rather than `== 'true'`, so an empty value runs the gate
# instead of skipping it.
#
# A default is required for the same reason from the other side: an input
# with no default and no value passed is the zero value again, arriving as if
# someone had chosen it.
run: |
set -e
awk '
/^ inputs:$/ { ins = 1; next }
ins && /^[^ ]/ { ins = 0 }
ins && /^ [a-z][a-z0-9_-]*:$/ { name = $1; sub(/:$/, "", name); n++; order[n] = name }
ins && /^ type: / { type[name] = $2 }
ins && /^ default:/ { dflt[name] = 1 }
END {
for (i = 1; i <= n; i++) {
k = order[i]
if (type[k] != "string") { printf "::error::input `%s` is `type: %s` — Hanzo Git substitutes the zero value of a declared type, so anything but a string is a constant the caller cannot set\n", k, (type[k] ? type[k] : "<none>"); bad = 1 }
else if (!dflt[k]) { printf "::error::input `%s` declares no default — an omitted one then arrives as the empty string as though it had been chosen\n", k; bad = 1 }
else printf " %-12s string, with a default\n", k
}
printf "OK: %d input(s) checked\n", n
exit bad
}
' .github/workflows/build.yml
- name: verdict-is-unconditional
# The test phase may be skipped. The VERDICT on it may not.
#
# `Test verdict` carries no `if:` at all, so its condition names no input and
# no input value can silence it. That is the whole property: a skipped step is
# not a failed one, so a refusal living inside a step guarded by `inputs.tests`
# vanishes with the step, and skipping the tests reports the same signal as
# passing them.
#
# Four assertions, because each one alone can hold while the phase still
# reports green over nothing: the verdict exists, it is unguarded, it sits
# after both steps that can write a receipt, and it sits before anything is
# built or published — so no artifact can be produced from a commit whose
# gate was never adjudicated.
#
# The fifth is the polarity. `!= 'false'` runs the gate for every value except
# the one explicit opt-out, INCLUDING the empty string a substitution failure
# produces; `== 'true'` would skip it for all of them.
run: |
set -e
f=.github/workflows/build.yml
line() { grep -n "^ - name: $1\$" "$f" | cut -d: -f1; }
t=$(line 'Test (per hanzo.yml)'); o=$(line 'Tests not run (declared by caller)')
v=$(line 'Test verdict'); d=$(line 'Delegate build to the runner (mode=delegate)')
b=$(line 'Build & push images (per hanzo.yml)')
for pair in "t:Test (per hanzo.yml)" "o:Tests not run (declared by caller)" "v:Test verdict" \
"d:Delegate build to the runner (mode=delegate)" "b:Build & push images (per hanzo.yml)"; do
eval "x=\$${pair%%:*}"
[ -n "$x" ] || { echo "::error::the step named '${pair#*:}' is gone from $f — a rename broke this assertion, fix the assertion"; exit 1; }
done
# the `if:` of a step, or nothing: read from its name down to its `run:`
cond() { awk -v s="$1" 'NR>=s && /^ run: /{exit} NR>=s && /^ if: /{sub(/^ if: /,""); print}' "$f"; }
[ -z "$(cond "$v")" ] || {
echo "::error::Test verdict carries \`if: $(cond "$v")\` — the one step that adjudicates the gate must name no input, or an input can switch off the thing that notices"; exit 1; }
# The gate and its opt-out run in the test job (one leg per architecture);
# the role is set per job in the file, never by a caller.
[ "$(cond "$t")" = "env.HANZO_CI_ROLE == 'test' && (inputs.tests != 'false')" ] || {
echo "::error::the test gate is guarded by \`$(cond "$t")\` — it must be \`inputs.tests != 'false'\` in the test job, so every value except the explicit opt-out RUNS it"; exit 1; }
[ "$(cond "$o")" = "env.HANZO_CI_ROLE == 'test' && (inputs.tests == 'false')" ] || {
echo "::error::the opt-out is guarded by \`$(cond "$o")\` — it must be the exact complement of the gate, so one of the two always writes a receipt"; exit 1; }
# Publishing is its own job, and it may exist only behind the gating test
# jobs: a verdict before the build steps adjudicates nothing if the build
# job could run without the tests. Exactly one of `test` and `test-amd64`
# runs, so the publish job needs both and requires the one that ran to
# have succeeded; the advisory leg is needed by nothing.
grep -q '^ needs: \[test, test-amd64\]$' "$f" \
&& grep -qF "if: always() && (needs.test.result == 'success' || needs.test-amd64.result == 'success')" "$f" \
&& grep -qF "HANZO_CI_TESTED: \${{ needs.test.result == 'success' && 'success' || needs.test-amd64.result }}" "$f" || {
echo "::error::the publish job does not need the gating test jobs, or does not carry their answer to the verdict"; exit 1; }
! grep -q 'needs:.*test-advisory' "$f" || {
echo "::error::a job needs test-advisory — the advisory leg reports and gates nothing"; exit 1; }
[ "$v" -gt "$t" ] && [ "$v" -gt "$o" ] || {
echo "::error::Test verdict (line $v) runs before the steps that write the receipt it reads ($t, $o)"; exit 1; }
[ "$v" -lt "$d" ] && [ "$v" -lt "$b" ] || {
echo "::error::Test verdict (line $v) runs after a build step ($d, $b) — an image would exist before its commit was adjudicated"; exit 1; }
echo "OK: verdict at line $v is unguarded, follows the gate ($t) and its opt-out ($o), and precedes both build lanes ($d, $b)"
- name: kind-is-read-once
# `kind:` says what a repository IS (HIP-0138), and this asserts the pipeline
# asks that question ONCE, unconditionally, and in the only window where the
# answer is worth anything.
#
# FOUR PROPERTIES, and each can fail while the other three hold:
#
# ONE READ — `.kind` appears in exactly one yq expression. Every other
# expression in build.yml reads what a manifest CONTAINS; a second read of
# what it IS is a second answer to one question, and two answers drift.
# UNGUARDED — the step names no input, so no input value can skip it. An
# `inputs.mode != 'delegate'` guard would walk a delegated chart build
# straight past it: the same green over nothing, through another door.
# AFTER yq — the read falls back to `image` when yq cannot run, which is
# right for a runner image that ships one and fatal above the step that
# installs it. Ordered there, EVERY manifest resolves to `image` and the
# refusals below can never fire — a check that cannot run, reading as a
# check that passed.
# BEFORE the lanes — ahead of every step that spends a credential, builds,
# or publishes. The client lane publishes to npm and PyPI before the test
# gate runs, so a refusal placed below it refuses a run that has already
# published.
run: |
set -e
f=.github/workflows/build.yml
line() { grep -n "^ - name: $1\$" "$f" | cut -d: -f1; }
at() { l=$(line "$1"); [ -n "$l" ] || { echo "::error::the step named '$1' is gone from $f — a rename broke this assertion, fix the assertion"; exit 1; }; echo "$l"; }
k=$(at 'Kind (per hanzo.yml)')
# Any spelling of the read, and prose about it is not one: comment lines are
# dropped first, so a sentence naming both `yq` and `.kind` is not counted
# as a second reader and the count means what it says.
n=$(grep -vE '^[[:space:]]*#' "$f" | grep -cE "yq .*\.kind")
[ "$n" = 1 ] || { echo "::error::$f reads .kind $n time(s) — it is read once, at 'Kind (per hanzo.yml)', and everything else takes that answer"; exit 1; }
cond() { awk -v s="$1" 'NR>=s && /^ run: /{exit} NR>=s && /^ if: /{sub(/^ if: /,""); print}' "$f"; }
[ -z "$(cond "$k")" ] || {
echo "::error::Kind (per hanzo.yml) carries \`if: $(cond "$k")\` — the step deciding what a repository IS must name no input, or an input can switch it off"; exit 1; }
p=$(at 'Provision parse toolchain (jq + PyYAML)')
[ "$p" -lt "$k" ] || {
echo "::error::Kind (per hanzo.yml) (line $k) runs before the yq it reads with (line $p) — with no yq the read falls back to \`image\` for every manifest, including the ones this step exists to refuse"; exit 1; }
for s in 'Fetch deploy credentials from KMS' \
'Client — regenerate from the release document (per hanzo.yml)' \
'Test (per hanzo.yml)' \
'Delegate build to the runner (mode=delegate)' \
'Build & push images (per hanzo.yml)' \
'Build & publish binaries (per hanzo.yml)' \
'Publish to PyPI (per hanzo.yml)' \
'Publish site (per hanzo.yml)'; do
l=$(at "$s")
[ "$k" -lt "$l" ] || { echo "::error::Kind (per hanzo.yml) (line $k) runs after '$s' (line $l) — a manifest this pipeline cannot serve would reach it"; exit 1; }
done
echo "OK: .kind read once at line $k, unguarded, after yq ($p) and before every lane"
- name: kind
# bin/kind_test.sh lifts the `kind` function out of build.yml and runs that
# text, so what is tested is what executes.
#
# Half of it is the fleet: the fourteen key-sets measured across all 117
# manifests in the estate, none of which declares `kind:`, every one of which
# must still resolve to `image`. The other half is the refusal, and it is
# weighted that way because the defect it closes is a GREEN run — a chart
# repository declares no `images:`, which is exactly the shape the image lane
# skips with `exit 0`, so a miss here looks identical to health.
run: bash bin/kind_test.sh
- name: tally
# The tally decides whether a gate reported a test at all, and every caller in
# three ecosystems inherits it. Its suite lifts the function OUT of build.yml
# and runs that text, so what is tested is what executes — no second spelling
# to keep in step. Weighted toward what must NOT count: a linter, a build, an
# assertion gate printing `OK:` and a go package that says `[no tests to run]`
# all read as zero, because over-counting would manufacture exactly the
# confidence the verdict exists to refuse. Offline: strings in, a number out.
run: bash bin/tally_test.sh
- name: cut
# The cut tags a regenerated client and writes that number into the file the
# repo keeps its version in. Its suite lifts `cut` out of build.yml and runs
# that text. The case it holds: a release that hands its number down gets it
# in the file too, or the repo's publisher sees nothing new.
run: bash bin/cut_test.sh
- name: credential
# The token the cut pushes with, chosen by host. Its suite lifts `credential`
# out of build.yml and runs that text: github.com gets a GitHub token that
# starts the tag's publish, the forge gets the org's IAM token, and neither
# host is handed the other's. Offline.
run: bash bin/credential_test.sh
- name: get
# How the tools arrive at the ref a caller pinned. Its suite lifts `get` out
# of build.yml and runs that text against a local repository: a branch and a
# tag clone, and any sha, the caller's pin included, arrives by fetch.
# Offline.
run: bash bin/get_test.sh
- name: alone
# The PyPI lane's clean install. Its suite lifts `alone` out of build.yml and
# runs that text on wheels it builds: one that declares what it imports
# installs and imports, one that does not is refused. Needs uv, provisioned
# here as the pipeline provisions it, and the index for the seeded pip.
run: |
command -v uv >/dev/null 2>&1 || { curl -fsSL https://astral.sh/uv/install.sh | sh; export PATH="$HOME/.local/bin:$PATH"; }
bash bin/alone_test.sh
- name: enqueue
# The delegate lane's POST to the build door. Its suite lifts `enqueue` out
# of build.yml and runs that text against a faked wire and clock: a 429 or
# an outage waits on a jittered backoff doubling to a 120s ceiling, a
# Retry-After is a floor, the 30-minute budget is never overrun, and a
# refusal is taken on the first answer. Offline.
run: bash bin/enqueue_test.sh
- name: kms
# bin/kms is the ONE way anything here reads a secret. It replaced three
# copies — this repo's site action, this repo's build.yml, and hanzoai/gui's
# npm-token — which were wrong in three different ways at the same time and
# all looked alike in a log. So the suite tests which SENTENCE comes out: a
# refusal names its code and is taken on the first try, an outage says it is
# one and says how many times it asked, and an absent credential names both
# variables. Offline apart from one 401 against the real door, which is the
# cheapest request there is.
run: bash bin/kms_test.sh
- name: publishable
# bin/publishable refuses a `build_secrets` name that has not declared itself
# public. That matters because a build_secret is baked in as a --build-arg,
# which `docker history` prints to anyone who can pull — so the key is a
# publishing decision wearing the word "secret". The check belongs here, where
# the build-arg is assembled once for every caller, not in one repo's Dockerfile.
run: bash bin/publishable_test.sh
- name: publishable-rule-is-one-rule
# The rule is spelled twice on purpose, and this is what keeps it one rule.
#
# build.yml cannot call bin/publishable, because the tools checkout and the
# workflow resolve from DIFFERENT refs: a caller pins the workflow at a ref
# Hanzo Git resolves for it, while the tools step derives its ref from
# GITHUB_WORKFLOW_REF — which git.hanzo.ai does not set, so it falls back to
# `v1`. A step calling a file that `v1` does not carry therefore cannot run
# until `v1` moves, and a rule the pipeline enforces must be reachable on the
# run that enforces it. So the pipeline carries its own copy.
#
# Which leaves the pattern in two files, which is the same shape as the two
# copies of build.yml and gets the same treatment: demand they are identical
# and let the gate, not a reader, be the thing that notices.
run: |
set -e
a=$(grep -oE '^[[:space:]]*PUBLISHABLE_\*\|[^)]*\)' .github/workflows/build.yml | tr -d '[:space:]')
b=$(grep -oE '^[[:space:]]*PUBLISHABLE_\*\|[^)]*\)' bin/publishable | tr -d '[:space:]')
[ -n "$a" ] && [ -n "$b" ] || { echo "::error::publishable rule not found in build.yml ($a) or bin/publishable ($b) — an edit renamed it, fix this assertion"; exit 1; }
[ "$a" = "$b" ] || { echo "::error::the publishable rule differs between build.yml and bin/publishable — build.yml is what runs, bin/publishable is what is tested, so a difference means the tested rule is not the enforced one:
build.yml: $a
bin/publishable: $b"; exit 1; }
echo "OK: one publishable rule, two spellings — $a"
- name: imgver
# bin/imgver decides the version EVERY image in the fleet publishes — this
# workflow's build lane calls it, and so does the imgver composite action the
# repos that hand-roll their own deploy.yml use. A wrong number here is one
# tag covering two digests, which a node running imagePullPolicy:
# IfNotPresent never picks up and no reader can see. Offline and
# deterministic: the registry floor is injected, so it needs no network.
run: bash bin/imgver_test.sh
- name: imgtags
# bin/imgtags decides what every image in the fleet is CALLED, and this asserts
# both build lanes take their tags from it. That second half is the gate: the
# rule is small enough to look right when it is written a second time inside a
# lane, and a lane that spells its own tags is how `delegate` came to publish
# sha-<short7> and nothing else — pin.sh takes a semver and refuses everything
# else, so those repos built green and could not be deployed. Offline: the
# rule is a pure function and the lane check reads the workflow file.
run: bash bin/imgtags_test.sh
- name: imgplat
# bin/imgplat decides whether an image SERVES what hanzo.yml declared, and this
# asserts both build lanes read the manifest back before the run goes green. A
# push exiting 0 is the writer's account of a write; an index carrying one
# manifest and an index carrying two both resolve and both pull, and only one
# of them runs on an arm64 node. Measured on ghcr.io/hanzoai/cloud:sha-ac39bda,
# an arch-neutral tag over a single linux/amd64 manifest. Offline: a stub crane
# answers with manifests taken from real registry replies.
run: bash bin/imgplat_test.sh
- name: vendormark
# bin/vendormark is the gate the build lane runs against every checkout: it
# refuses a site-generator vendor's branding in a tracked path, in tracked
# content, in a commit message, in an author identity or in a Co-authored-by
# trailer. It gates on the way in because the only other remedy is a history
# rewrite and a force-push of every branch.
#
# The suite pins the refusals AND the allowances, and the allowances are the
# larger half on purpose. The vendor's name is also an ordinary English
# adjective: across the 1,065 repos swept it appears innocently in tokenizer
# vocabularies, word lists, ML corpora, packed public-suffix data and an
# upstream Clerk bundle's list of other people's dev hosts. A gate that reds
# a fifth of the estate is a gate someone switches off, so the content rules
# are only strings that cannot occur in prose, and two candidate rules were
# measured and DROPPED for redding that Clerk bundle. Offline and
# deterministic: temp repos, no network.
run: bash bin/vendormark_test.sh
- name: gover
# bin/gover is the gate the build lane runs against every Dockerfile before
# it builds: it refuses a Go builder image older than the go.mod it
# compiles. The golang images set GOTOOLCHAIN=local, so the mismatch is not a
# warning — it is a hard mid-build death, and a sweep of the orgs found 54
# Dockerfiles already below their own go.mod.
#
# The gate is only worth having if it is exact in BOTH directions: a false
# negative lets the next one through, and a false positive blocks a build
# that would have worked, which is how gates get skipped. So the suite pins
# the refusals AND the allowances — a newer image than the floor is fine, an
# alpine suffix is not a Go patch, a floating tag warns instead of failing,
# and a multi-module repo is judged by its NEAREST go.mod. Offline and
# deterministic: temp dirs, no registry, no network.
run: bash bin/gover_test.sh
- name: sitepublish
# bin/sitepublish is the whole of the `site:` lane: it publishes through
# cloud's own routes, on the credentials KMS actually issues per org.
#
# The suite is weighted toward REFUSALS because the failure that matters here
# is a green publish that verified nothing — indistinguishable from a working
# one until a site quietly stops updating. Three defects of exactly that
# shape are pinned by name: an object-shaped read of a body that is not an
# object, `.releases[]` against the BARE ARRAY the list route returns (which
# resolves nothing and so can never fail for its stated reason), and
# `[ "$a" = "$b" ]` on two empty strings, which is TRUE. Reintroducing them
# turns 3, 9 and 1 of these tests red respectively. Offline and
# deterministic: a curl shim answers the routes from fixtures, no network.
run: bash bin/sitepublish_test.sh
- name: site
# bin/site is the other half of the same plane — the per-file presigned
# route the exports too large for sitepublish's 16 MiB body take — and it is
# what all 15 static repos reach through the composite action. Widest reach
# in bin/, so its suite runs here.
#
# What it pins is the part cloud does NOT do for us. On the archive route the
# server stamps Content-Type and Cache-Control itself; under a presigned POST
# it stamps neither, because the policy carries no condition for either — so
# whatever this script sends is what the object keeps, and the cache-control
# table here is load-bearing rather than advisory. That is why the strings are
# asserted literally: they are a second spelling of apps/sites.CacheControlFor,
# and this suite is the only thing that can notice when the two disagree.
#
# It also pins the edge body limit from BOTH sides, which is the only way a
# size check earns its place: over it, the completion manifest is refused
# before a single request goes out; at exactly it, the publish still happens.
# A guard that can only refuse is a guard that eventually blocks a build that
# would have worked, and those are the ones people learn to route around.
# Offline and deterministic: SITE_PLAN prints the manifest, no network.
run: bash bin/site_test.sh
- name: gocache
# bin/gocache carries every Go repo's module and build cache between
# runners, and Go trusts what it restores without re-hashing it. So the suite
# pins who may write (the default branch and tags, never a pull request),
# that an index is written only after all of its parts, that an archive not
# matching its index is never unpacked, and that a refused or absent
# identity is a cold build rather than a red one. Offline: a stub curl plays
# cloud's presigning /v1/s3 and the store behind it.
run: bash bin/gocache_test.sh
- name: conflictmarkers
# bin/conflictmarkers runs against every repo in the fleet, so a false
# positive is as expensive as a miss: it blocks a build that would have
# worked, on a file the author cannot legally change. hanzoai/code is that
# case — VOID_CODEBASE_GUIDE.md documents Void's Search/Replace format,
# which deliberately spells itself the way git writes a conflict, and the
# escape used everywhere else here (indent it) would change what the page
# renders. So the suite pins BOTH directions: the .d.ts and unfenced-
# markdown conflicts still fail, and a fenced sample, a tilde fence,
# hanzoai/app's same-line fixture and an eight-bracket heredoc still pass.
# Offline and deterministic: git repos in temp dirs, no network.
run: bash bin/conflictmarkers_test.sh
# No `deploy:` key exists any more — for any repo. A build ends at a published
# image; what RUNS is a reviewed pin in hanzo/universe, which cd.hanzo.ai reads.
# A pipeline that both builds and rolls itself out can put an unreviewed image
# on a public host, and cd's selfHeal would undo a direct patch anyway.