mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
The Go port took 24 entries from a Python dict that held 40. Sixteen repos were in NEITHER sync leg afterwards -- not pull mirrors, so nothing brought them in, and absent from this table, so nothing pushed them forward -- which is the exact shape the port was written to eliminate, reintroduced by the port itself. The worst of them is hanzoai/ci. Every repo in three orgs imports it as `hanzoai/ci/...@v1`, and a stale pipeline runs GREEN, so a caller cannot tell a current pipeline from one months old. It had already sat 18 commits behind once for exactly this reason. The rest: console and namespace (shipping services whose inbound leg was never written down), the five hanzo-apps surfaces, hanzo-go/sdk and hanzo-rs/sdk (published SDKs that `go get` and `cargo` resolve against the forge copy -- 16 and 10 commits behind when they were found), hanzo-dart, hanzokv, hanzozt, hanzo-docs/team and luxfi/aml. Two guards, because a table that quietly loses rows is not a thing to notice twice. One asserts every declared org is in the allowlist -- without it an entry in a new org is refused and the only symptom is a smaller "N of M resolved". The other asserts the count and names five of the entries that went missing. The allowlist gains hanzo-inc, and the live run is why. Three declared repos -- auto, blog and visor -- have been transferred there, and the run refused all three by name rather than pushing into an org nobody had listed. That is the guard working: it is an allowlist so that an unknown org is a question, not an assumption. hanzo-inc is ours (created 2026-07-24, all three private and pushed within the week), so it is listed now and the refusal goes away. Measured against live GitHub: 40 of 40 resolve, exit 0. EIGHT only resolve because names are RESOLVED and never matched against a listing -- three moved to hanzo-apps, three to hanzo-inc, esign became sign, kms-v1 became kms. A listing would have reported eight absences, and an absent name raises nothing. Co-authored-by: Hanzo Dev <[email protected]>
121 lines
4.1 KiB
Go
121 lines
4.1 KiB
Go
package commands
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/cd/mirror"
|
|
)
|
|
|
|
func write(t *testing.T, body string) string {
|
|
t.Helper()
|
|
p := filepath.Join(t.TempDir(), "repos.json")
|
|
if err := os.WriteFile(p, []byte(body), 0o600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
// Direction decides which side may overwrite the other, so an entry that omits it
|
|
// must be refused rather than assigned a default. Defaulting to native would turn
|
|
// every typo into a push at a repository we do not own; defaulting to mirror would
|
|
// silently stop syncing one we do. Neither is a guess a sync job gets to make.
|
|
func TestDirectionIsNeverAssumed(t *testing.T) {
|
|
_, err := load(write(t, `[{"org":"hanzoai","name":"cloud"}]`))
|
|
if err == nil {
|
|
t.Fatal("an entry with no direction was accepted")
|
|
}
|
|
if !strings.Contains(err.Error(), "direction is required") {
|
|
t.Fatalf("the error should name the missing field, got %q", err)
|
|
}
|
|
}
|
|
|
|
func TestIncompleteEntryIsRefused(t *testing.T) {
|
|
for _, body := range []string{
|
|
`[{"org":"hanzoai","direction":"native"}]`,
|
|
`[{"name":"cloud","direction":"native"}]`,
|
|
} {
|
|
if _, err := load(write(t, body)); err == nil {
|
|
t.Errorf("accepted an entry with no org or no name: %s", body)
|
|
}
|
|
}
|
|
}
|
|
|
|
// An unreadable table must stop the run. Treating it as empty would report a clean
|
|
// reconcile of nothing — the exact shape of silence this program exists to remove.
|
|
func TestAbsentTableIsAnError(t *testing.T) {
|
|
if _, err := load(filepath.Join(t.TempDir(), "nope.json")); err == nil {
|
|
t.Fatal("a missing table was accepted")
|
|
}
|
|
}
|
|
|
|
// The shipped table is the one the scheduled run reads, so it has to survive the
|
|
// same validation as any other. It caught nothing for weeks while living as a Go
|
|
// literal; parsing it here is what makes it config.
|
|
func TestShippedTableIsValid(t *testing.T) {
|
|
entries, err := load("../../../mirror/repos.json")
|
|
if err != nil {
|
|
t.Fatalf("the shipped table does not load: %v", err)
|
|
}
|
|
if len(entries) == 0 {
|
|
t.Fatal("the shipped table is empty")
|
|
}
|
|
for _, e := range entries {
|
|
if e.Direction != mirror.Native && e.Direction != mirror.Mirror {
|
|
t.Errorf("%s: direction %q is neither %q nor %q", e, e.Direction, mirror.Native, mirror.Mirror)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Every declared repo must sit in an org the allowlist owns, or the entry is
|
|
// dead on arrival: `owned` refuses it, the plan drops it, and the report says
|
|
// only that fewer entries resolved than were declared. Adding a repo in a new
|
|
// org is therefore TWO edits, and this is what says so — otherwise the second
|
|
// one is discovered as a silence, which is the failure mode this whole package
|
|
// exists to remove.
|
|
//
|
|
// One direction only. The allowlist is deliberately broader than the table: a
|
|
// repo can be transferred into an org that has no entry yet, and `owned` guards
|
|
// the name GitHub resolves to, not the name declared here.
|
|
func TestEveryDeclaredOrgIsOwned(t *testing.T) {
|
|
entries, err := load("../../../mirror/repos.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, e := range entries {
|
|
if !mirror.Owned[e.Org] {
|
|
t.Errorf("%s: org %q is not in mirror.Owned, so this entry can never resolve", e, e.Org)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The table replaced a Python dict, and the port dropped 16 of its 40 entries --
|
|
// among them hanzoai/ci, the reusable pipeline every repo in three orgs imports
|
|
// as `hanzoai/ci/...@v1`. It sat 18 commits behind with v1 pinned to a pipeline
|
|
// from months earlier, and a stale pipeline runs GREEN, so every caller believed
|
|
// it was current. A count is a blunt guard, but the thing it guards against is
|
|
// blunt too: a table that quietly loses rows.
|
|
func TestTableCoversEveryRepoTheScriptCarried(t *testing.T) {
|
|
entries, err := load("../../../mirror/repos.json")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(entries) < 40 {
|
|
t.Errorf("table has %d entries, want at least the 40 the script carried", len(entries))
|
|
}
|
|
for _, want := range []string{"hanzoai/ci", "hanzoai/console", "hanzo-go/sdk", "hanzo-rs/sdk", "luxfi/aml"} {
|
|
found := false
|
|
for _, e := range entries {
|
|
if e.Org+"/"+e.Name == want {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("%s is missing; it was in the table this replaced", want)
|
|
}
|
|
}
|
|
}
|