A build records the bases it was handed, and the mesh reads its edges from builds

Bases reach a recipe as build arguments, so the digest was never in the file the builder read
edges from: no build on the mesh recorded what it stood on, and 'build --on', the bases-first
order and the merge follow-up all walked a graph with no edges (novox/hq 04-ISSUES/131). The
builder now reports every base it resolved; the controller records them by artifact path and
reads the newest build's edges from the store, since a recorded manifest carries no build.on.
This commit is contained in:
2026-09-28 03:51:14 +02:00
parent aab6ded41b
commit 35252af665
6 changed files with 198 additions and 70 deletions
+39 -28
View File
@@ -250,7 +250,11 @@ func (f following) SourceMoved(ctx context.Context, m link.SourceMoved) error {
m.Owner, m.Repo, m.Base, m.Commit)
return nil
}
ordered := orderByBases(moved)
against, err := inv.BuiltAgainst(ctx)
if err != nil {
return notNow(err)
}
ordered := orderByBases(moved, against)
names := make([]string, 0, len(ordered))
for _, e := range ordered {
names = append(names, e.Manifest.Module)
@@ -265,7 +269,7 @@ func (f following) SourceMoved(ctx context.Context, m link.SourceMoved) error {
failed = append(failed, e.Manifest.Module)
// A base that failed is a reason to stop: what stands on it would be built against
// the old one, and report success (novox/hq 04-ISSUES/131).
if standsOn(ordered, e.Manifest.Module) {
if standsOn(ordered, e.Manifest.Module, against) {
fmt.Printf(" stopping: %s is a base of what was still to build\n", e.Manifest.Module)
break
}
@@ -292,10 +296,13 @@ func sourceIs(s inventory.Source, m link.SourceMoved) bool {
return s.Ref == "" || s.Ref == m.Base
}
// orderByBases is the entries with every base before what stands on it: a module whose build names
// another's artifact under build.on comes after that module. Entries outside the set are not
// waited for — they are not being rebuilt. Stable for what has no order between it.
func orderByBases(entries []inventory.Entry) []inventory.Entry {
// orderByBases is the entries with every base before what stands on it: a module whose build stood
// on another's artifact comes after that module. Entries outside the set are not waited for — they
// are not being rebuilt. Stable for what has no order between it.
//
// `against` is what each module's newest build stood on (inventory.BuiltAgainst): the edges are
// derived from builds, not declared, because a recorded manifest no longer carries `build.on`.
func orderByBases(entries []inventory.Entry, against map[string][]string) []inventory.Entry {
inSet := map[string]bool{}
for _, e := range entries {
inSet[e.Manifest.Module] = true
@@ -309,13 +316,9 @@ func orderByBases(entries []inventory.Entry) []inventory.Entry {
return
}
seen[name] = true
if e.Manifest.Build != nil {
for _, on := range e.Manifest.Build.On {
for _, base := range entries {
if base.Manifest.Module != name && inSet[base.Manifest.Module] && standsOnModule(on, base.Manifest.Module) {
place(base, seen)
}
}
for _, base := range entries {
if base.Manifest.Module != name && inSet[base.Manifest.Module] && standsOnModule(e, base.Manifest.Module, against) {
place(base, seen)
}
}
placed[name] = true
@@ -328,26 +331,34 @@ func orderByBases(entries []inventory.Entry) []inventory.Entry {
}
// standsOn is whether anything in the set is built on the named module's artifacts.
func standsOn(entries []inventory.Entry, module string) bool {
func standsOn(entries []inventory.Entry, module string, against map[string][]string) bool {
for _, e := range entries {
if e.Manifest.Build == nil {
continue
}
for _, on := range e.Manifest.Build.On {
if standsOnModule(on, module) {
return true
}
if standsOnModule(e, module, against) {
return true
}
}
return false
}
// standsOnModule is whether a base names the module: as written in a manifest (`module`), or as
// recorded after a build, when the mesh has replaced it with the artifact it resolved to
// (`artifact-store://<module>/<artifact>@…`). A recorded manifest is what the catalogue holds.
func standsOnModule(on catalogue.BuildsOn, module string) bool {
if on.Module == module {
return true
// standsOnModule is whether an entry's build stood on the named module: by what its newest build
// recorded it was handed (`artifact-store://<module>/<artifact>@…`, the module's own artifact), or
// — for a module registered from a manifest and not yet built — by the base its manifest names.
func standsOnModule(e inventory.Entry, module string, against map[string][]string) bool {
if e.Manifest.Module == module {
return false
}
return strings.HasPrefix(on.Image, "artifact-store://"+module+"/")
if e.Manifest.Build != nil {
for _, on := range e.Manifest.Build.On {
if on.Module == module {
return true
}
}
}
prefix := catalogue.ArtifactStoreScheme + module + "/"
for _, ref := range against[e.Manifest.Module] {
if strings.HasPrefix(ref, prefix) {
return true
}
}
return false
}