Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .spec-lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ref=86dcaea8092b3597a6325b7abd6df0b820978160
sha256=8886c3c7b7f8e5feee00b09a36241a3a283fc43a24ccdb1264dc44606115f901
ref=v8.5.844
sha256=5ca4ed58f87827cb879e409da8f803ef57b52b29123d2403868ed6af66aa8e2f
repo=hanzo-inc/cloud
path=openapi.yaml
path=/v1/openapi.json
url=https://api.hanzo.ai/v1/openapi.json
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hanzo-cli"
version = "8.5.752"
version = "8.5.753"
edition = "2021"
authors = ["Hanzo AI"]
description = "Unified CLI for Hanzo AI development tools"
Expand Down
66 changes: 10 additions & 56 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,70 +1,24 @@
# hanzo — the CLI. Five commands matter here, and they are TWO questions asked
# about ONE chain:
#
# hanzoai/cloud@<tag> openapi.yaml the document, pinned in .spec-lock
# │ genspec
# spec/cloud.json the projection
# │ genproduct
# generated.rs the command tree
# · spec/live.json what the server said about all of it
#
# `spec` / `spec-check` IS THE PROJECTION STILL ITS INPUT? Re-derive, refuse a
# delta. Needs the document (network, a token).
# `live` / `live-check` IS THE INPUT STILL TRUE OF PRODUCTION? Ask the running
# server, with a control for every probe, and write down
# the raw answers. Needs the network and nothing else.
# `verify` RULE ON WHAT IS COMMITTED. Both directions, every link of
# the chain, no network at all — which is why `cargo test`
# runs it too, and why it cannot be the gate that gets
# switched off for being slow or flaky.
#
# That split is the whole design. A gate that needs the network in CI gets
# disabled, and a disabled gate is worse than none; a gate that never asks the
# server can only compare derived artifacts with each other, which they satisfy
# whether or not either is still true. So ASKING and RULING are two commands over
# one checked-in body of evidence.
#
# `spec` and `spec-check` both go through scripts/generate.sh, which is also what
# hanzoai/ci's `client:` lane calls — so a maintainer, the push gate and the
# release all run the same derivation. A gate that runs a different generation
# than the writer is a gate that tests something nobody ships.

# Hanzo CLI checks: generate from the same public API document used by CI.
# The lock records the API release and the digest actually used. spec-check
# refuses changed input; make spec refreshes the projection from that input.
SHELL := /bin/bash

# THE FORGE IS CANONICAL. git.hanzo.ai holds hanzoai/cloud; the GitHub copy is a
# mirror, and a mirror can be behind — universe's was measured 40 chat versions
# and 51 app versions stale on the same day this changed. Verifying the command
# tree against a mirror means a gate that passes on a document production is not
# serving, which is worse than a gate that fails.
#
# The forge serves its API at /v1/, NOT /api/v1/ — asking for /api/v1 returns a
# 404 that reads exactly like a rejected credential and costs an hour.
SPEC_HOST ?= https://git.hanzo.ai/v1
SPEC_REPO ?= hanzo-inc/cloud
SPEC_PATH ?= openapi.yaml
LOCK := .spec-lock
SPEC_URL ?= https://api.hanzo.ai/v1/openapi.json
LOCK := .spec-lock

.PHONY: help spec spec-check live live-check verify test build lint

help: ## This.
@grep -hE '^[a-z-]+:.*##' $(MAKEFILE_LIST) | sed 's/:.*## /\t/' | expand -t20

# The document, BY VALUE at the ref this tree names. Fetching it — rather than
# reading api.hanzo.ai — is what makes the check reproducible: the same ref
# always yields the same bytes, and the digest below proves it. A live host
# answers "what is deployed right now", which is a different question and not
# one a committed artifact can be measured against.
define fetch_spec
ref=$$(sed -n 's/^ref=//p' $(LOCK)); \
want=$$(sed -n 's/^sha256=//p' $(LOCK)); \
[ -n "$$ref" ] || { echo "no $(LOCK) — this tree does not name a document"; exit 1; }; \
[ -n "$${SPEC_TOKEN:-}" ] || { echo "SPEC_TOKEN (contents:read on $(SPEC_REPO)) is unset"; exit 1; }; \
tmp=$$(mktemp); \
curl -fsSL -H "Authorization: token $$SPEC_TOKEN" \
"$(SPEC_HOST)/repos/$(SPEC_REPO)/raw/$(SPEC_PATH)?ref=$$ref" -o "$$tmp"; \
[ -n "$$ref" ] && [ -n "$$want" ] || { echo "$(LOCK) must name the API release and digest"; exit 1; }; \
tmp=$$(mktemp); trap 'rm -f "$$tmp"' EXIT; \
curl -fsSL "$(SPEC_URL)" -o "$$tmp"; \
got=$$(sha256sum "$$tmp" | cut -d' ' -f1); \
[ "$$got" = "$$want" ] || { echo "$(SPEC_REPO)@$$ref:$(SPEC_PATH) hashes to $$got, but $(LOCK) says $$want — the ref moved under this capture"; exit 1; }; \
export SPEC="$$tmp" SPEC_REF="$$ref" HANZO_SPEC_REPO="$(SPEC_REPO)"
[ "$$got" = "$$want" ] || { echo "API document changed: refresh the lock and projection together (expected $$want, got $$got)"; exit 1; }; \
export SPEC="$$tmp" SPEC_REF="$$ref" HANZO_SPEC_REPO="hanzo-inc/cloud"
endef

# Moving the projection forward RE-ASKS THE SERVER in the same breath. Evidence
Expand Down
44 changes: 4 additions & 40 deletions scripts/generate.sh
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
#!/usr/bin/env sh
# The CLI's projection of the Hanzo Cloud API document — the same call site every
# other client repo has, so hanzoai/ci's `client:` lane drives all eight the same
# way.
#
# ONE INPUT, by value:
# $SPEC the document itself, already fetched at a pinned ref and already
# digest-checked by the lane. Never a live host: at generation time
# the deploy this describes has already happened, and asking
# api.hanzo.ai instead would name whatever it is serving now rather
# than the release that handed us this file.
# $SPEC_REF which ref that was. genspec stamps it, with the document's own
# sha256, into the spec's provenance — so `spec/cloud.json` can
# always answer "which release am I?".
#
# One optional argument: `--check` re-asks instead of rewriting (exit 1 on any
# delta). Same binary, same derivation — which is what keeps the gate from
# testing a different generation than the one that writes the file. It is an
# argument and not an env var because that is the spelling every other client
# repo's call site already uses, and one spelling of one idea is the point.
#
# THE SECOND INPUT IS GONE, and with it the clone and the credential. This script
# used to fetch hanzoai/openapi and pin it in `.spec-lock` beside the document,
# because the hand-authored master `hanzo.yaml` was the last source of
# request-body and query SHAPE for cloud's untyped handlers. It had stopped being
# only that: 71 operations entered `spec/cloud.json` on the master's word alone —
# two addressing nothing at all, and sixty-six "confirmed" only by a
# `{wildcardN}` relay door, which answers identically for a real path and an
# invented one. A second authority over what EXISTS is how a CLI grows commands
# no server serves. The document decides existence, prose and shape alike now, so
# there is nothing left to clone and no SPEC_TOKEN to hold.
# Generate from the API document supplied by value in SPEC. SPEC_REF identifies
# the API release that supplied it. CI and make spec-check use the public
# /v1/openapi.json document; .spec-lock records its digest.
# --check refuses any projection change without rewriting files.
set -eu

cd "$(dirname "$0")/.."
Expand All @@ -46,13 +20,3 @@ fi

cargo run --quiet --features maintainer --bin genspec --locked
cargo run --quiet --features maintainer --bin genproduct --locked

# `.spec-lock` has ONE writer — the nightly re-pin in `.hanzo/workflows/drift.yml`,
# which reads hanzoai/cloud's newest tag, fetches the document at it, and records
# the ref beside the digest of the bytes it actually read. It used to be named as
# hanzoai/ci's client: lane, on the strength of a `repository_dispatch` a cloud
# release was supposed to send; nothing was ever delivered, and on a push that lane
# is check-only at the LOCKED ref, so for 104 runs the lock had no writer at all
# and moved once, by hand. This script writes no half of it: it used to append a
# second (`master=`, the hanzoai/openapi commit it generated against), and there is
# no second half.
2 changes: 1 addition & 1 deletion spec/cloud.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion spec/live.json

Large diffs are not rendered by default.

Loading
Loading