Files
zooqueenandHanzo Dev 5e3dac2a12 Publish docs.hanzo.ai from hanzo.yml's site lane through hanzoai/ci build.yml@v2
The forge's KMS identity is granted HANZO_DEPLOY_TOKEN at /deploy only
(hanzoai/universe kms-grants.yaml). deploy.yml published through hanzoai/ci
.github/actions/site@v1, which reads the flat /HANZO_DEPLOY_TOKEN, and KMS
answers that read 403. build.yml@v2 reads the token at /deploy.

- .hanzo/workflows/cicd.yml calls build.yml@v2 with submodules: recursive and
  the org's KMS_CLIENT_ID/SECRET, one run per ref at a time, never cancelled.
- hanzo.yml declares site: (slug docs-hanzo-ai, dir apps/docs/out, build
  scripts/site.sh, on main) and a test: that runs the docs vitest project
  against the packages it imports, built first.
- scripts/site.sh does what deploy.yml did: bun 1.3.11, pnpm install, the
  ingest key check, the package build, the export under a memory heartbeat,
  check-export.sh and the key grep.
- deploy.yml is removed, so one lane publishes the prefix.
- images: is removed. No route serves the image, and its build_secrets read
  PUBLISHABLE_KEY, which this identity is not granted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-10-04 09:14:47 -07:00

78 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Builds apps/docs/out, the static export that hanzo.yml's `site:` publishes as
# Sites project docs-hanzo-ai, and refuses an export that is not a site.
#
# hanzoai/ci runs it as the site build on its publish job: a fresh linux-amd64
# runner with Node and corepack's pnpm, this repo checked out with its
# submodules, and nothing installed. RELEASE.md has the whole path.
set -euo pipefail
cd "$(dirname "$0")/.."
# build:pre and build:post run under bun. This exact version comes from the npm
# registry, which also serves the platform binary its install links.
bun_version=1.3.11
if [ "$(bun --version 2>/dev/null || true)" != "$bun_version" ]; then
bun_dir="${RUNNER_TEMP:-${TMPDIR:-/tmp}}/bun-$bun_version"
npm install --silent --no-save --no-audit --no-fund --prefix "$bun_dir" "bun@$bun_version"
export PATH="$bun_dir/node_modules/.bin:$PATH"
fi
echo "node $(node --version), pnpm $(pnpm --version), bun $(bun --version)"
pnpm install --frozen-lockfile
# The ingest key is the hanzo org's publishable key from @hanzo/event's keyring,
# which the page resolves from its own host (packages/analytics). This resolves
# it the same way for docs.hanzo.ai. A key that names a deleted project answers
# 403 while the site looks perfect, so the endpoint is asked before the build:
# a lost answer (000/429/5xx) warns, a refusal stops the build.
key="$(cd packages/analytics && node --input-type=module \
-e "import { keyFor } from '@hanzo/event'; process.stdout.write(keyFor('docs.hanzo.ai') ?? '')")"
case "$key" in
pk-*) ;;
*) echo "::error::@hanzo/event resolves no pk- key for docs.hanzo.ai"; exit 1 ;;
esac
answer="$(mktemp)"
code="$(curl -s -o "$answer" -w '%{http_code}' --max-time 20 \
-X POST "https://api.hanzo.ai/v1/event?ingest_key=$key" \
-H 'content-type: application/json' -d '{"batch":[]}')" || code=000
case "$code" in
200) ;;
000|429|5*) echo "::warning::ingest endpoint answered $code; key unverified" ;;
*) echo "::error::the ingest key is refused ($code): $(cat "$answer")"; exit 1 ;;
esac
# The workspace packages first, on their own, so a failure there is not read as
# a failure of the site. `docs^...` is turbo's word for "what docs depends on,
# not docs".
pnpm turbo run build --filter='docs^...'
# The export, run directly rather than through turbo: turbo holds a task's output
# until the task ends, so a build that dies midway would report nothing.
#
# The heartbeat prints memory and disk every 15 seconds, so a run that reaches
# the runner's memory limit says so in its last line instead of stopping: the
# cgroup's figure where the runtime exposes one, otherwise the sandbox's own
# /proc/meminfo, then the five largest processes by resident memory.
#
# The compile peaks near 10 GB and the whole build, render workers included,
# near 14 GB, inside a runner pod capped at 6 cores and 24Gi.
# TURBO_TASKS_AVAILABLE_PARALLELISM=2 runs two Turbopack tasks and two loader
# processes at once, which keeps the loaders near 1.2G together.
( while :; do
printf '[usage] %s mem=%s disk=%s%s\n' "$(date -u +%H:%M:%S)" \
"$(cat /sys/fs/cgroup/memory.current 2>/dev/null || awk '/^MemTotal:/{t=$2} /^MemAvailable:/{a=$2} END{print (t-a)"k"}' /proc/meminfo)" \
"$(df -Pk . | awk 'NR==2{print $4"k free"}')" \
"$(awk '/^Name:/{n=$2} /^VmRSS:/{print $2, n}' /proc/[0-9]*/status 2>/dev/null | sort -rn | head -5 | awk '{printf " %s=%dM", $2, $1/1024}')"
sleep 15
done ) &
heartbeat=$!
trap 'kill "$heartbeat" 2>/dev/null || true' EXIT
NEXT_EXPORT=1 HANZO_DOCS_SYNC=0 NEXT_TELEMETRY_DISABLED=1 \
NODE_OPTIONS=--max-old-space-size=8192 TURBO_TASKS_AVAILABLE_PARALLELISM=2 \
pnpm --filter docs build
kill "$heartbeat" 2>/dev/null || true
sh scripts/check-export.sh apps/docs/out
grep -rqF "$key" apps/docs/out \
|| { echo "::error::the ingest key is not in the export"; exit 1; }