The check built in a temporary directory of the build holder's container, which the host's engine mounted empty: every check build failed and every walk built again.
126 lines
5.8 KiB
Go
126 lines
5.8 KiB
Go
package builder
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// novox/hq issue 713: after ADR 0350 the walk of novox/mesh-controller#331 built mesh-controller, build-agent and
|
|
// route-proxy again, each "built: no check of novox/mesh-controller#331 built it". The check had built none: its
|
|
// build log said "built for the merge: 0 of 3 module(s)", mesh-controller's go bundle failing with "go.mod file not
|
|
// found". The build seat's holder runs in a container sharing only its workspace with the host, and a build's
|
|
// toolchain container is run by the host's container engine, which mounts a path as the host names it. The check
|
|
// built in a temporary directory of the holder's own (issue 703's copy), which the engine mounted as an empty one.
|
|
|
|
// aHostsEngine runs as the host's container engine does for a holder in a container: a mount of a path outside the
|
|
// workspace the holder shares with the host is an empty directory, and a Go toolchain finds no go.mod in it.
|
|
type aHostsEngine struct {
|
|
onTrunk
|
|
shared string
|
|
}
|
|
|
|
func (h aHostsEngine) run(ctx context.Context, dir, name string, args ...string) (string, error) {
|
|
if name == "docker" && len(args) > 0 && args[0] == "run" {
|
|
for i, a := range args {
|
|
if a != "--volume" || i+1 >= len(args) {
|
|
continue
|
|
}
|
|
from, _, _ := strings.Cut(args[i+1], ":")
|
|
if !withinDir(h.shared, from) {
|
|
return "", errors.New("go: go.mod file not found in current directory or any parent directory")
|
|
}
|
|
}
|
|
}
|
|
return h.onTrunk.run(ctx, dir, name, args...)
|
|
}
|
|
|
|
// aGoModule is a module of one Go bundle built from a repository's root.
|
|
func aGoModule(module string) map[string]string {
|
|
files := aSharedRepository("one", "1")
|
|
files[ManifestName] = `{"module":"` + module + `","version":"1","build":{"artifacts":[
|
|
{"name":"server","kind":"bundle","language":"go","system":"arch","from":"cmd/ctl","binary":"ctl"}]},
|
|
"resources":[{"id":"server","type":"process","name":"` + module + `","artifact":"server","run":["./ctl"]}]}`
|
|
return files
|
|
}
|
|
|
|
// A controller change whose walk moves mesh-controller, build-agent and route-proxy (#331's) has its check build all
|
|
// three, in the workspace the host's engine sees, and the walk of its merge takes each from the check by its key.
|
|
func TestReplay713TheWalkOfAControllerChangeTakesAllThreeFromItsCheck(t *testing.T) {
|
|
withVersion(t, "builder-1")
|
|
pull := PullRef{Owner: "novox", Repo: "mesh-controller", Number: 331}
|
|
held := map[string]string{"mesh-tools-go/build": "registry.invalid/mesh-tools-go/build@sha256:" + strings.Repeat("b", 64)}
|
|
repositories := map[string]string{"build-agent": "https://forge.invalid/mesh-catalog-build-agent.git",
|
|
"route-proxy": "https://forge.invalid/mesh-catalog-route-proxy.git"}
|
|
checked := func(shared string) (*recorded, aHostsEngine) {
|
|
r := &recorded{tree: "tree-of-the-merge", contents: aGoModule("mesh-controller"),
|
|
secondary: map[string]map[string]string{}}
|
|
for module, repository := range repositories {
|
|
r.secondary[repository] = aGoModule(module)
|
|
}
|
|
return r, aHostsEngine{onTrunk: onTrunk{recorded: r}, shared: shared}
|
|
}
|
|
spec := CheckSpec{ID: "build-331", Owner: pull.Owner, Repo: pull.Repo, Number: pull.Number,
|
|
Builds: []CheckBuildSpec{{Module: "mesh-controller", Merged: true, Tier: 0},
|
|
{Module: "build-agent", Repository: repositories["build-agent"], Ref: "main", Tier: 1},
|
|
{Module: "route-proxy", Repository: repositories["route-proxy"], Ref: "main", Tier: 2}}}
|
|
judged := CheckVerdict{Head: "98e856874913", Trunk: "c975dbf7b053", Merge: "2d22279b1d07"}
|
|
passed := CheckVerdict{Gate: &Layer{Verdict: "pass"}, Repo: &Layer{Verdict: "pass"}}
|
|
|
|
// As it ran: the builds made in the holder's own temporary directory, which the host's engine does not see.
|
|
shared, outside := t.TempDir(), t.TempDir()
|
|
merged := filepath.Join(outside, "merged")
|
|
r, engine := checked(shared)
|
|
asItRan := buildForTheMerge(t.Context(), engine.run, spec, CheckBuilding{Publish: r, Scratch: newScratch(), Held: held},
|
|
outside, merged, judged.Merge, judged, GitCredential{}, nil, logging(nil))
|
|
if builtCount(asItRan) != 0 {
|
|
t.Fatalf("the engine of this test sees the holder's temporary directory: %+v", asItRan)
|
|
}
|
|
|
|
// The check as it builds now: every module its merge moves, kept under its key.
|
|
scratch := newScratch()
|
|
spec.BuildWith = &CheckBuilding{Publish: r, Scratch: scratch, Held: held}
|
|
built := forTheMerge(t.Context(), engine.run, spec, passed, shared, merged, judged, GitCredential{}, nil,
|
|
logging(nil))
|
|
if len(built) != 3 || builtCount(built) != 3 {
|
|
t.Fatalf("the check built %d of %d module(s) its merge moves: %+v", builtCount(built), len(built), built)
|
|
}
|
|
var names []NamedBuild
|
|
for _, b := range built {
|
|
n := NamedBuild{Pull: pull, Check: spec.ID, Module: b.Module, Key: b.Key, Digests: map[string]string{}}
|
|
for _, m := range b.Made {
|
|
d := m.Digest
|
|
if d == "" {
|
|
d = DigestOf(m.Reference)
|
|
}
|
|
n.Digests[m.Name] = d
|
|
}
|
|
names = append(names, n)
|
|
}
|
|
|
|
// The walk of the merge commit, tier by tier: each module taken from the check, none built.
|
|
walked := map[string]string{"mesh-controller": "https://forge.invalid/mesh-controller.git"}
|
|
for module, repository := range repositories {
|
|
walked[module] = repository
|
|
}
|
|
for _, module := range []string{"mesh-controller", "build-agent", "route-proxy"} {
|
|
w, walk := checked(shared)
|
|
w.secondary[walked["mesh-controller"]] = aGoModule("mesh-controller")
|
|
got, err := BuildReusing(t.Context(), walk.run, w, Reuse{Scratch: scratch, Pulls: []PullRef{pull}, Named: names},
|
|
walked[module], "", "main", t.TempDir(), held, Npmrc{}, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("%s: %v", module, err)
|
|
}
|
|
if got.Reused == nil || got.Why != "" {
|
|
t.Fatalf("the walk built %s again: %s", module, got.Why)
|
|
}
|
|
for _, line := range w.ran {
|
|
if strings.HasPrefix(line, "docker run") && strings.Contains(line, "go build") {
|
|
t.Fatalf("the walk compiled %s: %s", module, line)
|
|
}
|
|
}
|
|
}
|
|
}
|