From 8b016cc62b23b77b283751da73c56f49a76b438e Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 3 Oct 2026 22:27:01 +0200 Subject: [PATCH] A Go tools bundle is served by its binary (hq ADR 0193) A bundle compiled to a binary has no entrypoints, and loads had to name one, so a Go bundle could not be served. Its binary is what the runtime starts: loads names the binary, derived when the module lists tools, and the runtime is told the binary's path, delivered like any tools bundle. --- internal/catalogue/build.go | 11 ++++++++ internal/catalogue/runtime_test.go | 43 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/internal/catalogue/build.go b/internal/catalogue/build.go index 1bc31c6..6e3cb08 100644 --- a/internal/catalogue/build.go +++ b/internal/catalogue/build.go @@ -87,6 +87,12 @@ func (m Manifest) Resolve(built []Built) (Manifest, error) { loads := append([]string(nil), a.Loads...) if a.Loads == nil && len(m.Tools) > 0 { loads = append([]string(nil), a.Entrypoints...) + // A bundle compiled to a binary has no entrypoints: the binary is what it is, and what + // the runtime starts to serve it (novox/hq ADR 0193). So a Go tools bundle is served + // as Go — the runtime execs it — exactly as a TypeScript one is through its launcher. + if bin := BinaryOf(a); bin != "" { + loads = []string{bin} + } } // **Kept, never routed** (ADR 0155): the builder publishes to the store at the address // it reached it by, and a manifest carrying that address names an installation — @@ -242,6 +248,11 @@ func (b *Build) problems(module string) []string { for _, e := range a.Entrypoints { found = found || e == load } + // A bundle compiled to a binary is one executable: the runtime loads that or nothing + // (novox/hq ADR 0193). + if bin := BinaryOf(a); bin != "" { + found = load == bin + } if !found { problems = append(problems, fmt.Sprintf( "%s: %q says the runtime loads %q, which is not among its entrypoints — "+ diff --git a/internal/catalogue/runtime_test.go b/internal/catalogue/runtime_test.go index fa4dc43..216c46e 100644 --- a/internal/catalogue/runtime_test.go +++ b/internal/catalogue/runtime_test.go @@ -389,3 +389,46 @@ func TestARuntimeCompiledToABinaryRunsItself(t *testing.T) { t.Errorf("the Go runtime is not told what to serve or whose it is: %v %v", env, process["user"]) } } + +// novox/hq ADR 0193: a Go tools bundle is served — its binary is what the runtime starts, delivered +// like any tools bundle, named to the runtime where a TypeScript bundle names its launcher. +func TestAGoToolsBundleIsServedByItsBinary(t *testing.T) { + with := Rendering{ArtifactStore: "anchor.internal:5101", + Needed: map[string]map[string]string{RuntimeModule: {"broker": "sealed-credential"}}} + lamp := Manifest{Module: "lamp", Version: "1", Tools: []string{"on"}, + Build: &Build{Artifacts: []Artifact{{Name: "tools", Kind: ArtifactBundle, Language: "go", + System: "arch", From: "cmd/lamp-tools"}}}} + if p := lamp.Build.problems("lamp"); len(p) != 0 { + t.Fatalf("a Go tools bundle was refused: %v", p) + } + lamp, err := lamp.Resolve([]Built{{Name: "tools", Kind: ArtifactBundle, + Reference: ArtifactStoreScheme + "lamp/tools/blobs/" + bundleDigest, Digest: bundleDigest}}) + if err != nil { + t.Fatal(err) + } + if fmt.Sprint(lamp.Bundles[0].Loads) != "[lamp-tools]" { + t.Fatalf("the runtime loads %v from a Go bundle, want its binary", lamp.Bundles[0].Loads) + } + out, err := Resolution{Node: "anchor", Account: "ops", Modules: []Manifest{lamp, theRuntime(t)}}.Declaration(with) + if err != nil { + t.Fatal(err) + } + if fileNamed(out, "lamp."+BundleID("tools")) == nil { + t.Errorf("the Go bundle is not delivered: %v", ids(out)) + } + env := fileNamed(out, RuntimeModule+"."+RuntimeProcessID())["env"].(map[string]string) + if env[RuntimeToolModules] != "lamp="+BundlePath("lamp", "tools")+"/lamp-tools" { + t.Errorf("the runtime is told %q, want the binary", env[RuntimeToolModules]) + } + + // An artifact may say it explicitly; naming anything but the binary is refused. + said := Manifest{Module: "lamp", Version: "1", Build: &Build{Artifacts: []Artifact{{Name: "tools", + Kind: ArtifactBundle, Language: "go", System: "arch", Binary: "lamp", Loads: []string{"lamp"}}}}} + if p := said.Build.problems("lamp"); len(p) != 0 { + t.Errorf("loads naming the binary was refused: %v", p) + } + said.Build.Artifacts[0].Loads = []string{"tools/index.js"} + if p := said.Build.problems("lamp"); len(p) == 0 { + t.Error("a Go bundle loading a file it does not contain was admitted") + } +}