Run each part of a repository's own check in the toolchain it declares (hq issue 302)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery held for a person: merged, and the controller opened no walk for it within 10m0s — nothing it holds follows that branch, or the merge was…
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery held for a person: merged, and the controller opened no walk for it within 10m0s — nothing it holds follows that branch, or the merge was…
mesh-lab's merge-check.sh runs in the TypeScript toolchain, which holds no Go compiler, so its replays register was never compiled before a merge and a broken one would have merged green. A script now declares a further part with '# mesh-check-also: <toolchain> <script>'; the build seat runs it in that toolchain's own container, and mesh/repo-check passes only when every part does.
This commit is contained in:
@@ -480,3 +480,76 @@ func TestARedeliveredCheckRemovesWhatItsEarlierDeliveryLeft(t *testing.T) {
|
||||
t.Errorf("the check left %d container(s) behind", len(left))
|
||||
}
|
||||
}
|
||||
|
||||
// **A repository in two languages checks both, each in its own toolchain** (novox/hq issue 302): the lab's
|
||||
// merge-check.sh runs in the TypeScript toolchain, and its Go replays were said "NOT CHECKED HERE" on every
|
||||
// pull request, so a register that did not compile would have merged green. Its script declares the Go
|
||||
// part; the check runs it in the Go toolchain's container after the script, and the layer passes only
|
||||
// when both do.
|
||||
func TestARepositoryInTwoLanguagesIsCheckedInBoth(t *testing.T) {
|
||||
script := "#!/bin/sh\n# mesh-check-toolchain: typescript\n# mesh-check-also: go replays/merge-check.sh\nnpm test\n"
|
||||
parts := ScriptParts([]byte(script))
|
||||
if len(parts) != 2 || parts[0] != (ScriptPart{"typescript", CheckScript}) ||
|
||||
parts[1] != (ScriptPart{"go", "replays/merge-check.sh"}) {
|
||||
t.Fatalf("the parts read as %+v", parts)
|
||||
}
|
||||
if got := ScriptParts([]byte("#!/bin/sh\nset -eu\n")); len(got) != 1 || got[0] != (ScriptPart{"go", CheckScript}) {
|
||||
t.Fatalf("a script declaring nothing reads as %+v", got)
|
||||
}
|
||||
|
||||
// Each part run where its toolchain is: here, the image the part was given is what the command says.
|
||||
held := CheckSpec{Toolchain: "go-image", Toolchains: map[string]string{"typescript": "ts-image", "go": "go-image"}}
|
||||
run := func(t *testing.T, spec CheckSpec, files map[string]string, parts []ScriptPart) (*Layer, []string) {
|
||||
t.Helper()
|
||||
tree := t.TempDir()
|
||||
for name, body := range files {
|
||||
if err := os.MkdirAll(filepath.Dir(filepath.Join(tree, name)), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(tree, name), []byte(body), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
var images []string
|
||||
var out tail
|
||||
layer := ownCheck(t.Context(), spec, parts, tree, &out, func() bool { return false },
|
||||
func(image, script string) *exec.Cmd {
|
||||
images = append(images, image+" "+script)
|
||||
cmd := exec.CommandContext(t.Context(), "sh", script)
|
||||
cmd.Dir = tree
|
||||
return cmd
|
||||
}, func(string, string, ...any) {})
|
||||
return layer, images
|
||||
}
|
||||
twoParts := []ScriptPart{{"typescript", CheckScript}, {"go", "replays/merge-check.sh"}}
|
||||
|
||||
layer, images := run(t, held, map[string]string{CheckScript: "exit 0\n", "replays/merge-check.sh": "exit 0\n"}, twoParts)
|
||||
if layer.Verdict != "pass" || !strings.Contains(layer.Summary, "replays/merge-check.sh (go)") ||
|
||||
strings.Join(images, ", ") != "ts-image merge-check.sh, go-image replays/merge-check.sh" {
|
||||
t.Errorf("both parts passing answered %+v, ran %v", layer, images)
|
||||
}
|
||||
layer, _ = run(t, held, map[string]string{CheckScript: "exit 0\n",
|
||||
"replays/merge-check.sh": "echo 'not gofmt'\"'\"'d:'; echo register.go; exit 1\n"}, twoParts)
|
||||
if layer.Verdict != "fail" || !strings.Contains(layer.Summary, "replays/merge-check.sh failed") ||
|
||||
!strings.Contains(layer.Summary, "register.go") || !strings.Contains(layer.Summary, "merge-check.sh (typescript) passed") {
|
||||
t.Errorf("a failing Go part answered %+v", layer)
|
||||
}
|
||||
layer, images = run(t, held, map[string]string{CheckScript: "exit 2\n", "replays/merge-check.sh": "exit 0\n"}, twoParts)
|
||||
if layer.Verdict != "fail" || len(images) != 1 {
|
||||
t.Errorf("a failing first part answered %+v and ran %v", layer, images)
|
||||
}
|
||||
layer, _ = run(t, held, map[string]string{CheckScript: "exit 0\n"}, twoParts)
|
||||
if layer.Verdict != "fail" || !strings.Contains(layer.Summary, "does not hold") {
|
||||
t.Errorf("a declared part the repository lacks answered %+v", layer)
|
||||
}
|
||||
layer, _ = run(t, held, map[string]string{CheckScript: "exit 0\n"}, []ScriptPart{{"typescript", CheckScript},
|
||||
{"go", "../elsewhere.sh"}})
|
||||
if layer.Verdict != "fail" || !strings.Contains(layer.Summary, "not a path inside") {
|
||||
t.Errorf("a part outside the repository answered %+v", layer)
|
||||
}
|
||||
layer, _ = run(t, CheckSpec{Toolchains: map[string]string{"typescript": "ts-image"}},
|
||||
map[string]string{CheckScript: "exit 0\n", "replays/merge-check.sh": "exit 0\n"}, twoParts)
|
||||
if layer.Verdict != "error" || !strings.Contains(layer.Summary, "go toolchain") {
|
||||
t.Errorf("a part in a toolchain the mesh does not hold answered %+v — never a pass", layer)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user