the call site stops guessing a credential, and the publisher moves where it runs

Two separate things kept this projection from ever moving.

The generator lives in hanzoai/openapi and this repo went and got it for itself,
with no credential, from a private repo — so the first dispatch that ever
reached the client lane died at `could not read Username`, exit 128. hanzoai/ci
holds the token that reads this forge, so it now fetches the driver once and
hands it over; a call site that does not clone cannot be wrong about where to.

And the trigger: `repository_dispatch` is a verb this forge does not have, and a
nightly `schedule:` decides when by a clock rather than by a release. The forge
answers a workflow_dispatch POST with 204 and a runner takes it in seconds, so
that is the one trigger left.
This commit is contained in:
2026-08-08 11:51:54 -07:00
parent e6370c46d0
commit ad0e0b843a
2 changed files with 28 additions and 41 deletions
+15 -9
View File
@@ -20,15 +20,21 @@ on:
branches: [main]
tags: ['v*']
pull_request:
# THE PROJECTION ASKS FOR THE RELEASE. hanzoai/ci's client lane, given no
# payload, reads hanzoai/cloud's newest tag off git.hanzo.ai and regenerates
# this tree onto it. That replaces a fan-out cloud pushed at nine repos and
# which never once reached a first step — github.com has no runner for the
# label these callers ask for, and the forge, which does, cannot receive a
# repository_dispatch at all. Asking needs no credential spanning six owners,
# and a release stays green whether or not this repo is well.
schedule:
- cron: '19 7 * * *'
# THE RELEASE POKES THE PROJECTION, and workflow_dispatch is how, because it
# is the dispatch verb this forge HAS. Two shapes were tried and neither could
# work: cloud posted `repository_dispatch: spec-update` at api.github.com,
# where these callers ask for a label github.com has no runner for, so twelve
# accepted events ran nothing; then this file grew a nightly `schedule:`,
# which does move the projection but decides WHEN by a clock rather than by a
# release, so a client is a day stale on average and nothing anywhere records
# which release it is a projection of.
#
# Measured instead of assumed: POST /v1/repos/<o>/<r>/actions/workflows/
# cicd.yml/dispatches on git.hanzo.ai answers 204, a git-runner picks the run
# up in seconds, and it reaches its first step. So the release dispatches this
# directly, on the side that has the runners, and the lane's no-payload path
# takes cloud's NEWEST TAG — which exists only after that commit built, smoked
# and rolled out. One trigger, fired by the thing that changed.
workflow_dispatch:
jobs:
cicd:
+13 -32
View File
@@ -1,46 +1,27 @@
#!/usr/bin/env bash
# Regenerate pkg/hanzoai/cloud/ from the Hanzo OpenAPI document.
# Regenerate pkg/hanzoai/cloud/ from the Hanzo Cloud API document.
#
# This is a CALL SITE, not a generator invocation. The invocation is logic and
# lives once, in hanzoai/openapi `generate.py`; every per-language knob —
# generator, library, package name — is data in `sdks.yaml` beside it. Nothing
# about how this client is produced is written down in this repo, so there is
# nothing here that can drift on its own. It is the same eight lines java-sdk
# and kotlin-sdk already run, and deliberately not a second driver: the old
# scripts/generate.sh here WAS one, it did `rm -rf pkg/hanzoai`, and it was
# deleted for that reason. This one cannot — `generate.py` owns exactly the one
# `take` path sdks.yaml names, `hanzoai/cloud -> pkg/hanzoai/cloud`.
# A CALL SITE, not a generator invocation. The invocation is logic and lives
# once, in `generate.py`; every per-language knob — generator, library, package
# name — is data in `sdks.yaml` beside it. Nothing about how this client is
# produced is written down here, so there is nothing here that can drift.
#
# ./scripts/generate.sh # regenerate in place
# ./scripts/generate.sh --check # non-zero if the committed client drifted
#
# THE GENERATOR IS A TOOL; THE DOCUMENT IS AN ARGUMENT. They had one name here
# and it broke the lane. `SPEC_REF` is the ref of the DOCUMENT — hanzoai/ci's
# client lane exports a hanzoai/cloud sha or v-tag — and it was also handed to
# `git clone --branch` on hanzoai/openapi, which is a different repository and
# has never had a ref by that name. So every CI regeneration died at the clone,
# and by hand it worked only because SPEC_REF defaulted to `main` and both repos
# happen to have one. The generator is cloned at its own default branch now, and
# the document reaches the driver as a value.
# BOTH INPUTS ARRIVE AS VALUES. $SPEC is the document, already fetched at a
# pinned ref and digest-checked; $OPENAPI is the checkout holding the driver.
# hanzoai/ci's client lane sets both, because it holds the one credential that
# reads this forge. This script used to clone the driver itself, anonymously,
# from a private repo — so every CI regeneration died at the clone with
# `could not read Username`. Set OPENAPI by hand to run it by hand.
#
# Point OPENAPI at a checkout you already have to skip the clone entirely.
#
# Requires: python3 (+pyyaml), java 11+, git.
# Requires: python3 (+pyyaml), java 11+.
set -euo pipefail
cd "$(dirname "$0")/.."
OPENAPI="${OPENAPI:-}"
: "${OPENAPI:?the generator lives in hanzoai/openapi; hanzoai/ci's client lane sets OPENAPI, or point it at a checkout}"
if [ -z "$OPENAPI" ]; then
OPENAPI="$(mktemp -d "${TMPDIR:-/tmp}/hanzo-openapi.XXXXXX")"
trap 'rm -rf "$OPENAPI"' EXIT
git clone --depth 1 -q https://git.hanzo.ai/hanzoai/openapi "$OPENAPI"
fi
# hanzoai/ci's client lane fetches the release document and exports SPEC, so it
# arrives by value, already digest-checked. Without it the driver reads this
# tree's own .spec-lock and fetches the same bytes from git.hanzo.ai, so a hand
# run and a CI run project the same document rather than whatever main is today.
if [ -n "${SPEC:-}" ]; then set -- --spec "$SPEC" "$@"; fi
exec python3 "$OPENAPI/generate.py" python --repo "$PWD" "$@"