Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf2bb3b87d |
+30
-11
@@ -87,12 +87,6 @@ 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 —
|
||||
@@ -248,11 +242,6 @@ 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 — "+
|
||||
@@ -447,3 +436,33 @@ func BinaryOf(a Artifact) string {
|
||||
}
|
||||
return a.Name
|
||||
}
|
||||
|
||||
// undeliveredBundles says which of a module's bundles nothing would ever put on a machine (novox/hq
|
||||
// 04-ISSUES/216). A bundle reaches a machine three ways: the node's runtime serves it (it says
|
||||
// `loads`, or its module declares `tools`), a resource names it (a process, a step, an archive), or
|
||||
// it is the runtime itself. One reached by none of them was built, recorded and pushed as success,
|
||||
// and was simply absent — seven modules' tools went missing that way on 2026-10-03. Refused here,
|
||||
// naming the field that would deliver it.
|
||||
func undeliveredBundles(m Manifest) []string {
|
||||
if m.Build == nil || m.Module == RuntimeModule {
|
||||
return nil
|
||||
}
|
||||
named := map[string]bool{}
|
||||
for _, r := range m.Resources {
|
||||
if a, ok := r["artifact"].(string); ok && a != "" {
|
||||
named[a] = true
|
||||
}
|
||||
}
|
||||
var problems []string
|
||||
for _, a := range m.Build.Artifacts {
|
||||
if a.Kind != ArtifactBundle || named[a.Name] || len(a.Loads) > 0 || len(m.Tools) > 0 {
|
||||
continue
|
||||
}
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: the bundle %q would be built and never reach a machine: nothing loads it, runs it or "+
|
||||
"unpacks it. A tools bundle says `loads` (the entrypoints the node's runtime serves) or its "+
|
||||
"module lists its `tools`; a daemon or a step is a resource naming it (novox/hq 04-ISSUES/216)",
|
||||
m.Module, a.Name))
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
@@ -1373,6 +1373,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
}
|
||||
}
|
||||
problems = append(problems, m.Build.problems(m.Module)...)
|
||||
problems = append(problems, undeliveredBundles(m)...)
|
||||
// **What provides the artifact store cannot be delivered through it** (novox/hq 04-ISSUES/029).
|
||||
//
|
||||
// Building publishes to the store, and the builder will not start without one. So a module
|
||||
|
||||
@@ -390,45 +390,27 @@ func TestARuntimeCompiledToABinaryRunsItself(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
// novox/hq 04-ISSUES/216: a bundle nothing loads, runs or unpacks is refused at registration; saying
|
||||
// `loads`, listing `tools`, or a resource naming it admits it.
|
||||
func TestABundleNothingDeliversIsRefused(t *testing.T) {
|
||||
base := func() Manifest {
|
||||
return Manifest{Module: "baserow", Version: "1", Build: &Build{Artifacts: []Artifact{
|
||||
{Name: "tools", Kind: ArtifactBundle, Language: "typescript", Entrypoints: []string{"tools/index.js"}}}}}
|
||||
}
|
||||
lamp, err := lamp.Resolve([]Built{{Name: "tools", Kind: ArtifactBundle,
|
||||
Reference: ArtifactStoreScheme + "lamp/tools/blobs/" + bundleDigest, Digest: bundleDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if p := undeliveredBundles(base()); len(p) != 1 || !strings.Contains(p[0], "never reach a machine") {
|
||||
t.Fatalf("a bundle nothing delivers was admitted: %v", p)
|
||||
}
|
||||
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")
|
||||
loads := base()
|
||||
loads.Build.Artifacts[0].Loads = []string{"tools/index.js"}
|
||||
tools := base()
|
||||
tools.Tools = []string{"baserow_list_rows"}
|
||||
run := base()
|
||||
run.Resources = []map[string]any{{"id": "daemon", "type": "process", "artifact": "tools", "run": []any{"node", "tools/index.js"}}}
|
||||
runtime := base()
|
||||
runtime.Module = RuntimeModule
|
||||
for name, m := range map[string]Manifest{"loads": loads, "tools": tools, "a process": run, "the runtime": runtime} {
|
||||
if p := undeliveredBundles(m); len(p) != 0 {
|
||||
t.Errorf("a bundle delivered by %s was refused: %v", name, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user