mirror of
https://github.com/hanzoai/ci.git
synced 2026-10-20 15:28:05 +00:00
call it Hanzo Git, which is its name
"Forge" was a borrowed word for the thing that hosts the repos, schedules the builds and issues the tokens. That thing is Hanzo Git. The word named nothing a reader could go look at, and it had already drifted: the same paragraph could say "the forge" and "git.hanzo.ai" and leave you to work out they were one thing. forge.go becomes git.go for the same reason — the file reads what Hanzo Git knows that the run list does not. Where a second mention would just repeat the name, it says "the server" instead. Left alone: "unforgeable" and "cannot be forged", which are the other word entirely and mean what they say. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -39,7 +39,7 @@ runs:
|
||||
- name: Fetch imgver
|
||||
shell: bash
|
||||
# The action ref is the script ref: an action pinned to @v1 runs v1's
|
||||
# imgver. The forge first, because the forge is where this repo is written
|
||||
# imgver. Hanzo Git first, because that is where this repo is written
|
||||
# and GitHub is its mirror.
|
||||
#
|
||||
# `hanzo/ci` IS NOT THIS REPO. It is the tenant-org fork, and its v1 has no
|
||||
|
||||
@@ -19,7 +19,7 @@ description: Publish a built static export to the Hanzo PaaS Sites plane.
|
||||
# ONE credential, read from ONE place. The deploy key comes from KMS, which is
|
||||
# where a secret lives: it rotates by resealing, and every site that publishes
|
||||
# reads the same value the moment it changes. It was `secrets.HANZO_DEPLOY_TOKEN`
|
||||
# on the forge — one copy per org that nobody can read back, so nobody could tell
|
||||
# on Hanzo Git — one copy per org that nobody can read back, so nobody could tell
|
||||
# it had stopped resolving, and cloud answered 403 on every call for the repos
|
||||
# still carrying it while the one repo that had moved to KMS published fine.
|
||||
#
|
||||
@@ -62,7 +62,7 @@ runs:
|
||||
- name: Fetch site
|
||||
shell: bash
|
||||
# The action ref is the script ref: an action pinned to @v1 runs v1's
|
||||
# site. Both forges, because this repo is served from each.
|
||||
# site. Both hosts, because this repo is served from each.
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ref="${GITHUB_ACTION_REF:-v1}"
|
||||
|
||||
@@ -45,7 +45,7 @@ since has built.
|
||||
Two readings the page depends on, both from a run's JOBS rather than its one
|
||||
conclusion. A run that fails at `gate` built nothing; a run that fails at
|
||||
`receipt` has already built, pinned and proved the release live. Both report
|
||||
`failure`. And a commit the forge never constructed a run for is `absent`, not
|
||||
`failure`. And a commit Hanzo Git never constructed a run for is `absent`, not
|
||||
failed — there is no log to open, so it is drawn as a different shape.
|
||||
|
||||
It reads and never writes: no deploy, no retry, no promotion. Drive a sync at
|
||||
@@ -55,7 +55,7 @@ cd.hanzo.ai.
|
||||
|
||||
`ci` reads the cluster through its own ServiceAccount — get and list on
|
||||
workloads, nothing else, no stored credential — and reads `hanzo/universe`
|
||||
through the forge token it already holds. In `charts/app/values/hanzo/ci.yaml`:
|
||||
through the Hanzo Git token it already holds. In `charts/app/values/hanzo/ci.yaml`:
|
||||
|
||||
```yaml
|
||||
rbac:
|
||||
@@ -101,7 +101,7 @@ That's it. The build/test/publish logic lives here, once.
|
||||
|
||||
### `.hanzo/workflows/`, and nothing left in `.github/workflows/`
|
||||
|
||||
The forge collects workflows from the **first** entry of `WORKFLOW_DIRS` that
|
||||
Hanzo Git collects workflows from the **first** entry of `WORKFLOW_DIRS` that
|
||||
exists — not the union. So the moment one file lands in `.hanzo/workflows/`,
|
||||
every remaining file under `.github/workflows/` stops running. It stops
|
||||
*silently*: the checks that would go red are the ones no longer running, so the
|
||||
@@ -117,7 +117,7 @@ comm -23 <(ls .github/workflows) <(ls .hanzo/workflows) # anything listed is d
|
||||
Moving a file is not reviving it. Two things break on the way across:
|
||||
|
||||
- **`runs-on: ubuntu-latest` matches no runner in this fleet**, deliberately —
|
||||
this forge hosts ~1400 mirrored repos whose upstream workflows all ask for it,
|
||||
Hanzo Git hosts ~1400 mirrored repos whose upstream workflows all ask for it,
|
||||
and advertising it hands the fleet to their CI. An unmatched label does not
|
||||
fail, it **queues until the timeout**. Use `hanzo-build-linux-amd64`, or
|
||||
another label declared in the `git-runner-config` ConfigMap (ns `hanzo`).
|
||||
|
||||
@@ -264,7 +264,7 @@ case "$kcode" in
|
||||
echo "X-Org-Id header; CI cannot set that header, cloud derives it from this"
|
||||
echo "bearer. Mint a replacement with POST $api/v1/keys {\"type\":\"secret\"} as a"
|
||||
echo "member of the owning org, then set it as the ORG-level Actions secret"
|
||||
echo "HANZO_DEPLOY_TOKEN on git.hanzo.ai — the forge is what reads"
|
||||
echo "HANZO_DEPLOY_TOKEN on git.hanzo.ai — that is what reads"
|
||||
echo ".hanzo/workflows, not GitHub." ;;
|
||||
esac
|
||||
|
||||
@@ -303,7 +303,7 @@ post() {
|
||||
# success. So a new site needs no out-of-band `POST /v1/projects`; committing the
|
||||
# workflow is enough. The repo is linked so the deploy's lifecycle events name a
|
||||
# repo and reach the notify subscriptions keyed on it — the enqueue itself no
|
||||
# longer requires a link; SITE_REPO overrides the origin for a forge
|
||||
# longer requires a link; SITE_REPO overrides the origin for a
|
||||
# checkout whose remote is not the canonical repo.
|
||||
repo="${SITE_REPO:-$(git remote get-url origin 2>/dev/null || true)}"
|
||||
pcode=$(post "$work"/sd-proj.json "$api/v1/projects" \
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ import (
|
||||
//
|
||||
// It talks to the apiserver over plain HTTP because that is what the apiserver
|
||||
// is: a JSON REST API this service can read with the same client it already
|
||||
// points at the forge. A generated Kubernetes client would be the first
|
||||
// points at Hanzo Git. A generated Kubernetes client would be the first
|
||||
// dependency this repo has ever had, carried to spend two verbs on three
|
||||
// resources.
|
||||
//
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// Every arrow is a job of the pipeline this repo publishes, so a service is
|
||||
// current exactly when all four agree, and each way they can disagree names the
|
||||
// arrow that did not happen. Reading all four from the systems that own them —
|
||||
// the forge, the universe repo, the cluster — is what makes this a measurement
|
||||
// Hanzo Git, the universe repo, the cluster — is what makes this a measurement
|
||||
// rather than a restatement of the last log line the pipeline wrote about itself.
|
||||
//
|
||||
// Three matching values are NOT health, which is the whole reason head is read:
|
||||
@@ -48,11 +48,11 @@ type Version struct {
|
||||
|
||||
func (v Version) known() bool { return v.Tag != "" || v.Digest != "" }
|
||||
|
||||
// Build is what the forge did with one commit.
|
||||
// Build is what Hanzo Git did with one commit.
|
||||
type Build struct {
|
||||
// State is success | failure | running | absent. `absent` is not a kind of
|
||||
// failure and is kept apart from one: a failing run is a build that ran and
|
||||
// said no, while an absent run is the forge never having constructed a run
|
||||
// said no, while an absent run is Hanzo Git never having constructed a run
|
||||
// for the commit at all — a workflow it cannot parse or a reference it
|
||||
// cannot resolve. There is no log to open for the second, so a page that
|
||||
// draws them the same sends you looking for one that does not exist.
|
||||
@@ -96,7 +96,7 @@ type Service struct {
|
||||
Name string `json:"name"` // cloud
|
||||
Namespace string `json:"namespace"` // hanzo
|
||||
Image string `json:"image"` // ghcr.io/hanzoai/cloud
|
||||
Org string `json:"org"` // forge owner; empty when the repo is unresolved
|
||||
Org string `json:"org"` // Hanzo Git owner; empty when the repo is unresolved
|
||||
Repo string `json:"repo"` // hanzo-inc/cloud
|
||||
|
||||
Head Head `json:"head"`
|
||||
@@ -237,7 +237,7 @@ type live struct {
|
||||
Want int
|
||||
}
|
||||
|
||||
// link is one repo as the forge accounts for it.
|
||||
// link is one repo as Hanzo Git accounts for it.
|
||||
type link struct {
|
||||
Repo string
|
||||
Org string
|
||||
@@ -256,7 +256,7 @@ type link struct {
|
||||
// against another's running version, which manufactures drift that is not there.
|
||||
//
|
||||
// So the namespace and name carry the row, the cluster is matched on the same
|
||||
// pair, and the IMAGE is what joins to the forge — several services legitimately
|
||||
// pair, and the IMAGE is what joins to Hanzo Git — several services legitimately
|
||||
// share one repo, and then they share its head and its last build too.
|
||||
func assemble(pins []pin, lives []live, links map[string]link) []Service {
|
||||
at := func(namespace, name string) string { return namespace + "/" + name }
|
||||
@@ -421,8 +421,8 @@ func orgsOfServices(services []Service) []string {
|
||||
// watch refreshes the fleet snapshot on its own interval.
|
||||
//
|
||||
// It is slower than the run poll because the values it reads move on deploy
|
||||
// cadence rather than push cadence, and each cycle costs the forge a read per
|
||||
// service. Nothing about a dashboard justifies loading the forge that schedules
|
||||
// cadence rather than push cadence, and each cycle costs Hanzo Git a read per
|
||||
// service. Nothing about a dashboard justifies loading the server that schedules
|
||||
// every build in the fleet harder than the fleet itself does.
|
||||
func watch(ctx context.Context, logger *slog.Logger, src *gitSource, cl *cluster, cache *fleetCache, cfg config) {
|
||||
refresh := func() {
|
||||
|
||||
+2
-2
@@ -78,14 +78,14 @@ func TestShippedRunStillCountsAsBuilt(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestAbsentIsNotFailure keeps the two apart. A failing run can be opened and
|
||||
// read; a commit the forge never built a run for cannot, and sending someone to
|
||||
// read; a commit Hanzo Git never built a run for cannot, and sending someone to
|
||||
// look for a log that does not exist is its own delay.
|
||||
func TestAbsentIsNotFailure(t *testing.T) {
|
||||
old := time.Now().Add(-time.Hour)
|
||||
if got := verdict(Run{}, nil, old); got.State != "absent" {
|
||||
t.Errorf("no run for an hour-old commit = %q; want absent", got.State)
|
||||
}
|
||||
// A commit that has only just landed has no run yet because the forge is
|
||||
// A commit that has only just landed has no run yet because Hanzo Git is
|
||||
// still constructing one. Calling that absent would alarm on every push.
|
||||
if got := verdict(Run{}, nil, time.Now()); got.State != "running" {
|
||||
t.Errorf("no run for a commit pushed seconds ago = %q; want running", got.State)
|
||||
|
||||
+8
-8
@@ -10,20 +10,20 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// forge.go reads the two things the forge knows that the run list does not: what
|
||||
// git.go reads the two things Hanzo Git knows that the run list does not: what
|
||||
// hanzoai/universe declares, and which commit last produced a proved image.
|
||||
//
|
||||
// Both come from the same host over the same token this service already holds.
|
||||
// The universe repo lives on the forge, so the declared state of the fleet is a
|
||||
// The universe repo lives on Hanzo Git, so the declared state of the fleet is a
|
||||
// file read — no second credential, no checkout, no clone on disk.
|
||||
|
||||
// A commit that has just landed has no run for a moment. Below this age a
|
||||
// missing run is the forge still constructing one, and calling that absent would
|
||||
// missing run is Hanzo Git still constructing one, and calling that absent would
|
||||
// alarm on every push.
|
||||
const settling = 5 * time.Minute
|
||||
|
||||
// How far back the walk looks for the last commit that built. Every step is a
|
||||
// read against the forge, and past this depth the answer is already "this has not
|
||||
// read against Hanzo Git, and past this depth the answer is already "this has not
|
||||
// built in a long time", which the count says without another call.
|
||||
const reach = 12
|
||||
|
||||
@@ -197,7 +197,7 @@ func (g *gitSource) links(ctx context.Context, logger *slog.Logger, pins []pin)
|
||||
//
|
||||
// Where that repo does not exist the publisher sits in another org — ghcr.io/
|
||||
// hanzoai/cloud is built by hanzo-inc/cloud — and there the name alone is
|
||||
// ambiguous, because half a dozen orgs on this forge hold a repo called cloud.
|
||||
// ambiguous, because half a dozen orgs on Hanzo Git hold a repo called cloud.
|
||||
// Then the declared tag decides: the repo carrying the tag the fleet is running
|
||||
// is the repo that cut it. An image no candidate can be shown to publish stays
|
||||
// unlinked, and the page says so rather than showing a guess.
|
||||
@@ -267,7 +267,7 @@ func (g *gitSource) holds(ctx context.Context, repo, tag string) bool {
|
||||
return g.getJSON(ctx, "/v1/repos/"+repo+"/tags/"+url.PathEscape(tag), &out) == nil && out.Name != ""
|
||||
}
|
||||
|
||||
// link reads the forge end of the line: what we wrote, and what was last proved.
|
||||
// link reads the Hanzo Git end of the line: what we wrote, and what was last proved.
|
||||
func (g *gitSource) link(ctx context.Context, repo, branch string) (link, error) {
|
||||
org, _ := splitFullName(repo)
|
||||
c := link{Repo: repo, Org: org}
|
||||
@@ -421,7 +421,7 @@ func (g *gitSource) built(ctx context.Context, repo string, r Run, at time.Time)
|
||||
// which job failed, and whether an artifact exists, is what the page turns on.
|
||||
func verdict(r Run, jobs []job, at time.Time) Build {
|
||||
if r.ID == 0 {
|
||||
// No run for this commit. A push that has only just landed is the forge
|
||||
// No run for this commit. A push that has only just landed is Hanzo Git
|
||||
// still constructing one; older than that, the run is genuinely absent —
|
||||
// a workflow that could not be parsed or a reference that would not
|
||||
// resolve, which leaves no failed run to open.
|
||||
@@ -503,7 +503,7 @@ func (g *gitSource) jobs(ctx context.Context, repo string, run int64) ([]job, er
|
||||
}
|
||||
|
||||
// each runs fn over items with a small fixed fan-out. The cap is deliberately
|
||||
// low: these are reads against the forge that schedules every build in the
|
||||
// low: these are reads against the server that schedules every build in the
|
||||
// fleet, and a dashboard is never worth degrading it.
|
||||
func each[T any](ctx context.Context, items []T, fn func(context.Context, T) error) error {
|
||||
const workers = 4
|
||||
@@ -180,7 +180,7 @@ type config struct {
|
||||
// universe is the repo holding the declared state of the fleet, and
|
||||
// fleetRefresh is how often the four values are re-read. The fleet interval
|
||||
// is longer than the run interval because these values move on deploy
|
||||
// cadence, not push cadence, and each cycle costs the forge a read per
|
||||
// cadence, not push cadence, and each cycle costs Hanzo Git a read per
|
||||
// service.
|
||||
universe string
|
||||
fleetRefresh time.Duration
|
||||
@@ -454,7 +454,7 @@ func poll(ctx context.Context, logger *slog.Logger, src *gitSource, cache *runCa
|
||||
}
|
||||
|
||||
// Fan out, bounded. The cap is small on purpose: this is a read against
|
||||
// the forge that schedules every build in the fleet, and a dashboard is
|
||||
// the server that schedules every build in the fleet, and a dashboard is
|
||||
// never worth degrading it.
|
||||
const workers = 6
|
||||
var (
|
||||
|
||||
Reference in New Issue
Block a user