mirror of
https://github.com/hanzoai/docs.git
synced 2026-08-05 04:12:57 +00:00
A guard in deploy.yml protects one of six lanes. The image live right now is
tagged ae51c3fc — a SHA that exists only on the forge, whose forge run FAILED —
so something else built it, the fail-closed KMS step never ran, and the bundle
shipped with no key. Proven by streaming the 2.7GB layer: zero occurrences.
This is the shape the export gate already solved by living in the Dockerfile
rather than in a workflow. Same treatment: an empty or non-pk- key now refuses
the artifact instead of producing a hollow one, for every builder, without any
of them knowing this repo is special.
Both lanes are wired to supply it, or the gate would simply break docs:
- forge lane already fetches from KMS; its --build-arg is renamed to
EVENT_INGEST_KEY to match the ARG.
- ci lane now declares build_secrets: [EVENT_INGEST_KEY]; the reusable fetches
that name from KMS and passes it as a build-arg of the same name, and since
hanzoai/ci 5750426 it fails closed when a declared secret cannot be read.
The prefix stays a property of this build: EVENT_INGEST_KEY is the one name in
KMS, NEXT_PUBLIC_ is added here because that is what makes Next inline it.
Gate verified on all three branches: pk- exits 0, empty exits 1, wrong prefix
exits 1.
82 lines
4.7 KiB
YAML
82 lines
4.7 KiB
YAML
# Canonical CI config for hanzo-docs/docs. THREE programs read this file:
|
|
# hanzo/platform (the webhook lane that builds what is serving), hanzoai/ci (the
|
|
# GitHub lane, .github/workflows/cicd.yml), and hanzoai/cloud's forge push
|
|
# orchestrator. All three read `images:`; only ci reads `test:`. They do not agree
|
|
# on the tag — see below, because that disagreement is the whole contract.
|
|
#
|
|
# The repo builds ONE image: the Fumadocs monorepo exported static and served by
|
|
# hanzoai/static. The recipe lives in the Dockerfile and nowhere else — APP
|
|
# selects the workspace, so every static site here is built the same way. This
|
|
# file only declares WHAT is built; it does not restate the recipe.
|
|
#
|
|
# THE TAG CONTRACT. hanzoai/universe pins this image in
|
|
# charts/app/values/hanzo/docs.yaml, and the six builders that can produce it do
|
|
# not agree on what to call it:
|
|
#
|
|
# platform webhook, THIS file <sha40>-amd64-docs ← what universe pins
|
|
# POST /v1/runner whatever the caller names
|
|
# .hanzo/workflows/deploy.yml <sha40>
|
|
# hanzoai/ci (buildx) sha-<sha7>-amd64 + latest
|
|
# hanzoai/ci (mode: delegate) sha-<sha7>-amd64
|
|
# forge push orchestrator sha-<sha7>-amd64-docs (dormant)
|
|
#
|
|
# Three of those are three programs reading THIS file and disagreeing twice over:
|
|
# whether an absent `tag-suffix` falls back to `name` (platform: yes; cloud's
|
|
# apps/git/build_on_push.go: yes; hanzoai/ci: no, it reads `."tag-suffix" // ""`),
|
|
# and whether the sha is abbreviated (platform: no; the other two: yes). Adding
|
|
# `tag-suffix: docs` below would close the first disagreement and not the second
|
|
# — deliberately NOT done, because it changes the tag shape of lanes that have
|
|
# never run. RELEASE.md has the table and the reasoning.
|
|
#
|
|
# Whichever one publishes, the pin has to be told its name — and the line that
|
|
# actually decides what runs is `digest:`, not `tag:`, because the chart renders
|
|
# repo:tag@digest and a digest beside a tag is what gets pulled. Read the tag off
|
|
# the build you actually fired; RELEASE.md writes out the edit for each of the six.
|
|
images:
|
|
- name: docs
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
repo: ghcr.io/hanzoai/docs
|
|
# The KMS name IS the build-arg name. Without this the ci lane passes no key
|
|
# and the Dockerfile gate refuses the build — which is the point: the two
|
|
# lanes now either both supply it or both fail loudly.
|
|
build_secrets: [EVENT_INGEST_KEY]
|
|
|
|
test:
|
|
- name: unit
|
|
# vitest over packages/* and apps/docs. The apps/docs project is what pins
|
|
# the four surfaces: the SDK method names the docs print are openapi-
|
|
# generator's own derivation, and the MCP tool names are the door's, both
|
|
# asserted against real generated clients and a real tools/list answer. A
|
|
# drift in either naming rule fails here rather than teaching a call that
|
|
# does not exist.
|
|
run: pnpm install --frozen-lockfile && pnpm test
|
|
|
|
# NOTE — the export gate is not declared here, and not because it was forgotten.
|
|
#
|
|
# A static-export site fails by exporting NOTHING, which no image build can
|
|
# notice: the layer is valid and the site is empty. So the check has to happen
|
|
# somewhere every builder passes through, and `test:` is not it — hanzoai/ci
|
|
# runs `test:` AFTER `docker buildx --push`, and buildx builds and pushes in one
|
|
# invocation with no step in between to hold. A gate declared here would report a
|
|
# broken export that was already in the registry.
|
|
#
|
|
# The gate is therefore inside the Dockerfile (scripts/check-export.sh, plus
|
|
# apps/docs/export.require naming the sections that must not vanish). A failing
|
|
# export fails the build, so no builder reaches a push — including this one.
|
|
#
|
|
# That placement is also what caught the one thing the GitHub lane got wrong on
|
|
# its own. apps/docs/content/docs/studio is a submodule (hanzo-docs/studio-docs),
|
|
# and hanzoai/ci checked out with actions/checkout's default, which fetches none:
|
|
# the section simply would not exist in the output while page count and nav both
|
|
# stayed green. It is named in export.require, so that lane failed loudly instead
|
|
# of shipping a hole — and the fix is upstream, where hanzoai/ci now takes a
|
|
# `submodules` input (default unchanged) that .github/workflows/cicd.yml sets to
|
|
# `recursive`. The gate stays, because it is what makes the next such hole loud.
|
|
|
|
# No `deploy:` — ON PURPOSE, and this is not the usual "a human should review it".
|
|
# docs.hanzo.ai is delivered by the hanzo/app chart with `cd.automated: true`, so
|
|
# Argo is the sole writer of those objects and self-heals anything else that
|
|
# touches them. A `deploy:` block here would have the platform patch a workload
|
|
# that gets reverted on the next sync. The rollout IS the commit to universe.
|