mesh/merge-gate out of date: judged against trunk 76304d9db817, the trunk is now 4af9f1147de6; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 76304d9db817, the trunk is now 4af9f1147de6; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
Without a judge cache the replays still ran mesh-lab's checkout in the workspace with the container runtime's socket, so a self-judging pull request could write a test into them (hq issue 709, review 12303). They now run from a copy in a container volume of the check's own, made before anything of the change runs, with a fresh home and throwaway caches, removed after the check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
827 lines
41 KiB
Go
827 lines
41 KiB
Go
package builder
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// **A pull request into a release branch is judged by the controller of that branch** (novox/hq ADR 0358, issue
|
|
// 704): the build seat's check builds the judge from mesh-controller read at the release branch — with mesh-host's
|
|
// validator of that branch when mesh-host has one — reads every repository beside it at that branch where it has one
|
|
// and at its trunk otherwise, and names the branch, the judge's commit and each repository read with its commit.
|
|
|
|
const releaseBranch = "feat/57-config-and-provisioning"
|
|
|
|
// gitInRepo runs git in a repository made for a test.
|
|
func gitInRepo(t *testing.T, dir string, args ...string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", args...)
|
|
cmd.Dir = dir
|
|
cmd.Env = append(os.Environ(), "GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@example.org",
|
|
"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@example.org")
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %v: %v\n%s", args, err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
|
|
// commitFiles writes files into a repository and commits them on the branch checked out.
|
|
func commitFiles(t *testing.T, dir string, files map[string]string) string {
|
|
t.Helper()
|
|
for name, body := range files {
|
|
if err := os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
gitInRepo(t, dir, "add", "-A")
|
|
gitInRepo(t, dir, "commit", "--quiet", "-m", "x")
|
|
return gitInRepo(t, dir, "rev-parse", "HEAD")
|
|
}
|
|
|
|
// aRepositoryWithARelease is a repository whose main holds mainFiles and, when releaseFiles is not nil, a release
|
|
// branch over them holding releaseFiles. It answers the repository, main's head and the release branch's head.
|
|
func aRepositoryWithARelease(t *testing.T, mainFiles, releaseFiles map[string]string) (string, string, string) {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
gitInRepo(t, dir, "init", "--quiet", "-b", "main")
|
|
main := commitFiles(t, dir, mainFiles)
|
|
if releaseFiles == nil {
|
|
return dir, main, ""
|
|
}
|
|
gitInRepo(t, dir, "checkout", "--quiet", "-b", releaseBranch)
|
|
head := commitFiles(t, dir, releaseFiles)
|
|
gitInRepo(t, dir, "checkout", "--quiet", "main")
|
|
return dir, main, head
|
|
}
|
|
|
|
// aJudgeOf is a controller that judges as told: its gate passes, saying who it is and the word of the node-engine
|
|
// validator it vendors — the one it was built with.
|
|
func aJudgeOf(who string) map[string]string {
|
|
return map[string]string{
|
|
"go.mod": "module example.org/judge\n\ngo 1.22\n\nrequire github.com/novox/mesh-host v0.0.0\n",
|
|
"vendor/modules.txt": "# github.com/novox/mesh-host v0.0.0\n## explicit; go 1.22\ngithub.com/novox/mesh-host/validate\n",
|
|
"vendor/github.com/novox/mesh-host/validate/v.go": "package validate\n\nconst Word = \"the vendored validator\"\n",
|
|
"vendor/github.com/novox/mesh-host/internal/declaration/d.go": "package declaration\n",
|
|
"cmd/mesh-controller/main.go": `package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/novox/mesh-host/validate"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) == 2 && os.Args[1] == "merge-gate" {
|
|
fmt.Println("usage: merge-gate --facts <file> --store <postgres>")
|
|
os.Exit(2)
|
|
}
|
|
fmt.Printf("{\"verdict\":\"pass\",\"summary\":\"%s with %s\"}\n", "` + who + `", validate.Word)
|
|
}
|
|
`,
|
|
}
|
|
}
|
|
|
|
// aHostSaying is the node-engine's validator, saying a word.
|
|
func aHostSaying(word string) map[string]string {
|
|
return map[string]string{
|
|
"validate/v.go": "package validate\n\nconst Word = \"" + word + "\"\n",
|
|
"internal/declaration/d.go": "package declaration\n",
|
|
"validate/v_test.go": "package validate\n",
|
|
"internal/declaration/x_test.go": "package declaration\n",
|
|
}
|
|
}
|
|
|
|
// aRuntimeThatBuildsTheJudge puts a container runtime on PATH that runs every container's command on this machine,
|
|
// in the directory and with the environment the check gives it — the judge's build too — and a store and a bus that
|
|
// answer. It answers the file each `go` the check runs is written to.
|
|
func aRuntimeThatBuildsTheJudge(t *testing.T) string {
|
|
t.Helper()
|
|
if _, err := exec.LookPath("go"); err != nil {
|
|
t.Skip("no Go toolchain on this machine to build a judge with")
|
|
}
|
|
bus, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { bus.Close() })
|
|
go func() {
|
|
for {
|
|
c, err := bus.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
c.Close()
|
|
}
|
|
}()
|
|
dir := t.TempDir()
|
|
built := filepath.Join(dir, "built")
|
|
runtime := `#!/bin/sh
|
|
cmd="$1"; shift
|
|
case "$cmd" in
|
|
ps|rm|exec) exit 0 ;;
|
|
volume)
|
|
if [ -n "$VOLUMES" ]; then echo "$*" >> "$VOLUMES"; fi
|
|
if [ "$1" = rm ] && [ -n "$REPLAY_MOUNT" ]; then rm -rf "$REPLAY_MOUNT"; fi
|
|
exit 0 ;;
|
|
port) echo "` + bus.Addr().String() + `"; exit 0 ;;
|
|
run)
|
|
if [ -n "$RUNS" ]; then echo "$*" >> "$RUNS"; fi
|
|
workdir=""; detached=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
-d) detached=1; shift ;;
|
|
--rm) shift ;;
|
|
--workdir) workdir="$2"; shift 2 ;;
|
|
--env) export "$2"; shift 2 ;;
|
|
--network|--volume|--user|--label|--name|-p|-e) shift 2 ;;
|
|
*) break ;;
|
|
esac
|
|
done
|
|
if [ -n "$detached" ]; then echo a-container; exit 0; fi
|
|
shift
|
|
if [ "$1" = go ]; then echo "GOCACHE=$GOCACHE GOENV=$GOENV $*" >> "` + built + `"; fi
|
|
if [ "$1" = go ] && [ "$2" = build ] && [ -n "$JUDGE_HOMES" ]; then echo "$HOME" >> "$JUDGE_HOMES"; fi
|
|
if [ "$1" = go ] && [ "$2" = test ] && [ -n "$REPLAYS" ]; then
|
|
echo "HOME=$HOME GOCACHE=$GOCACHE GOMODCACHE=$GOMODCACHE GOENV=$GOENV GOFLAGS=$GOFLAGS DIR=$workdir" >> "$REPLAYS"
|
|
fi
|
|
cd "$workdir" && exec "$@" ;;
|
|
esac
|
|
exit 1
|
|
`
|
|
if err := os.WriteFile(filepath.Join(dir, "docker"), []byte(runtime), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
|
t.Setenv("GOTOOLCHAIN", "local")
|
|
return built
|
|
}
|
|
|
|
// builds is how many times the check ran go to build a judge.
|
|
func builds(t *testing.T, file string) int {
|
|
t.Helper()
|
|
body, err := os.ReadFile(file)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return strings.Count(string(body), "go build")
|
|
}
|
|
|
|
// theReleasedMesh is the core beside a release-branch check: a controller whose main and release branch judge
|
|
// differently, a node-engine with a validator of each, and a lab with no release branch.
|
|
func theReleasedMesh(t *testing.T) (map[string]Beside, string, string, string) {
|
|
t.Helper()
|
|
controller, _, controllerRelease := aRepositoryWithARelease(t, aJudgeOf("main's controller"),
|
|
aJudgeOf("the release branch's controller"))
|
|
host, _, hostRelease := aRepositoryWithARelease(t, aHostSaying("main's validator"),
|
|
aHostSaying("the release branch's validator"))
|
|
lab, labMain, _ := aRepositoryWithARelease(t, map[string]string{"README": "the lab"}, nil)
|
|
return map[string]Beside{
|
|
"mesh-controller": {Repository: controller, Ref: releaseBranch},
|
|
"mesh-controller-main": {Repository: controller, Ref: "main"},
|
|
"mesh-host": {Repository: host, Ref: releaseBranch, Else: "main"},
|
|
"mesh-lab": {Repository: lab, Ref: releaseBranch, Else: "main"},
|
|
}, controllerRelease, hostRelease, labMain
|
|
}
|
|
|
|
// aPullRequestInto is a repository whose pull request's head is cut from base, with a change.
|
|
func aPullRequestInto(t *testing.T, files map[string]string, base string, change map[string]string) (string, string) {
|
|
t.Helper()
|
|
repo, _, _ := aRepositoryWithARelease(t, files, map[string]string{"released": "r"})
|
|
gitInRepo(t, repo, "checkout", "--quiet", "-b", "change", base)
|
|
head := commitFiles(t, repo, change)
|
|
gitInRepo(t, repo, "checkout", "--quiet", "main")
|
|
return repo, head
|
|
}
|
|
|
|
func TestACataloguePullRequestIntoAReleaseBranchIsJudgedByThatBranchsController(t *testing.T) {
|
|
built := aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
beside, controllerRelease, hostRelease, labMain := theReleasedMesh(t)
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
|
cache := t.TempDir()
|
|
check := func(id string) CheckVerdict {
|
|
t.Helper()
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: id, Repository: catalogue, Ref: head, Owner: "novox",
|
|
Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch, JudgeCache: cache,
|
|
Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside, Toolchain: "golang"},
|
|
t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return v
|
|
}
|
|
v := check("check-704-catalogue")
|
|
// The release branch's controller judged, with mesh-host's validator of the release branch.
|
|
if v.Gate == nil || v.Gate.Verdict != "pass" ||
|
|
!strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the release branch's validator") {
|
|
t.Fatalf("the gate was not judged by the release branch's controller with its node-engine's validator: %+v\n%s",
|
|
v.Gate, v.Report)
|
|
}
|
|
// It names the release branch, the judge's commit, and each repository read beside it at its branch and commit.
|
|
mark := "[release " + releaseBranch + ", judged by its controller " + controllerRelease[:12] + "] "
|
|
if !strings.HasPrefix(v.Gate.Summary, mark) || !strings.HasPrefix(v.Repo.Summary, mark) {
|
|
t.Fatalf("the statuses do not name the release branch and its judge first:\n%s\n%s", v.Gate.Summary, v.Repo.Summary)
|
|
}
|
|
if v.Release != releaseBranch || v.JudgedBy != controllerRelease[:12] {
|
|
t.Fatalf("the verdict names release %q judged by %q, not %s by %s", v.Release, v.JudgedBy, releaseBranch,
|
|
controllerRelease[:12])
|
|
}
|
|
if v.BesideAt["mesh-host"] != releaseBranch || v.Beside["mesh-host"] != hostRelease[:12] {
|
|
t.Fatalf("mesh-host was read at %q (%s), not its release branch at %s", v.BesideAt["mesh-host"],
|
|
v.Beside["mesh-host"], hostRelease[:12])
|
|
}
|
|
// A repository with no release branch is read at its trunk, and says so.
|
|
if v.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] {
|
|
t.Fatalf("the lab, with no release branch, was read at %q (%s), not main at %s", v.BesideAt["mesh-lab"],
|
|
v.Beside["mesh-lab"], labMain[:12])
|
|
}
|
|
if !strings.Contains(v.Report, "read beside it: ") || !strings.Contains(v.Report, "mesh-host "+releaseBranch+" at "+hostRelease[:12]) {
|
|
t.Fatalf("the report does not say what was read beside it:\n%s", v.Report)
|
|
}
|
|
// It says which validator judged: mesh-host's of the release branch, with its commit.
|
|
if want := "mesh-host's validator of " + releaseBranch + " at " + hostRelease[:12]; v.Validator != want ||
|
|
!strings.Contains(v.Report, want) {
|
|
t.Fatalf("the verdict says the validator was %q, not %q:\n%s", v.Validator, want, v.Report)
|
|
}
|
|
// Each repository read beside it is named with the branch and the commit read.
|
|
for dir := range beside {
|
|
if v.BesideAt[dir] == "" || v.Beside[dir] == "" ||
|
|
!strings.Contains(v.Report, dir+" "+v.BesideAt[dir]+" at "+v.Beside[dir]) {
|
|
t.Fatalf("%s is not named with its branch and commit: at %q, commit %q\n%s", dir, v.BesideAt[dir],
|
|
v.Beside[dir], v.Report)
|
|
}
|
|
}
|
|
if n := builds(t, built); n != 1 {
|
|
t.Fatalf("the judge was built %d times for one check", n)
|
|
}
|
|
// **The judge is built with the judge cache's Go cache** (ADR 0358 §6), which no check's container mounts, and no
|
|
// Go environment file.
|
|
said, _ := os.ReadFile(built)
|
|
if !strings.Contains(string(said), "GOCACHE="+filepath.Join(cache, "go-cache")+" GOENV=off go build") {
|
|
t.Fatalf("the release judge was not built with the judge cache's Go cache:\n%s", said)
|
|
}
|
|
// **Clause 5**: the head was merged onto the release branch's head, which the verdict names as its trunk commit,
|
|
// so the release branch's move puts it out of date as a trunk's move does (ADR 0323 §4, the forge's holder).
|
|
if releaseHead := gitInRepo(t, catalogue, "rev-parse", releaseBranch); v.Trunk != releaseHead[:12] {
|
|
t.Fatalf("the verdict names trunk %q, not the release branch's head %s", v.Trunk, releaseHead[:12])
|
|
}
|
|
|
|
// **Built once per release-branch head**: a second check while that head stands takes the judge kept.
|
|
v = check("check-704-catalogue-again")
|
|
if !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the release branch's validator") {
|
|
t.Fatalf("the kept judge did not judge: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if n := builds(t, built); n != 1 {
|
|
t.Fatalf("the judge of the same release-branch head was built again: %d builds", n)
|
|
}
|
|
}
|
|
|
|
func TestAControllerPullRequestIntoAReleaseBranchReadsTheNodeEngineAtThatBranch(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
beside, _, hostRelease, _ := theReleasedMesh(t)
|
|
delete(beside, "mesh-controller")
|
|
controller, head := aPullRequestInto(t, aJudgeOf("the change's controller"), releaseBranch,
|
|
map[string]string{"cmd/x.txt": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-controller", Repository: controller, Ref: head,
|
|
Owner: "novox", Repo: "mesh-controller", Number: 315, Base: releaseBranch, Release: releaseBranch,
|
|
JudgeCache: t.TempDir(), Paths: []string{"cmd/x.txt"}, Modules: []string{"mesh-controller"}, Judge: "self",
|
|
Beside: beside, Toolchain: "golang"}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "the change's controller with the vendored validator") {
|
|
t.Fatalf("a controller change into a release branch did not judge itself: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if v.BesideAt["mesh-host"] != releaseBranch || v.Beside["mesh-host"] != hostRelease[:12] {
|
|
t.Fatalf("mesh-host was read beside it at %q (%s), not at the release branch's %s", v.BesideAt["mesh-host"],
|
|
v.Beside["mesh-host"], hostRelease[:12])
|
|
}
|
|
if v.Release != releaseBranch || v.JudgedBy == "" || v.JudgedBy != v.Merge[:12] {
|
|
t.Fatalf("the verdict names release %q judged by %q, not its own merge %s", v.Release, v.JudgedBy, v.Merge)
|
|
}
|
|
}
|
|
|
|
func TestAPullRequestIntoTheTrunkIsJudgedAsBefore(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("main's controller"),
|
|
aJudgeOf("the release branch's controller"))
|
|
host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"),
|
|
aHostSaying("the release branch's validator"))
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, "main", map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-trunk", Repository: catalogue, Ref: head,
|
|
Owner: "novox", Repo: "mesh-catalog", Number: 1, Base: "main", JudgeCache: t.TempDir(),
|
|
Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Toolchain: "golang",
|
|
Beside: map[string]Beside{"mesh-controller": {Repository: controller, Ref: controllerMain},
|
|
"mesh-host": {Repository: host, Ref: hostMain}}}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Summary != "main's controller with the vendored validator" {
|
|
t.Fatalf("a trunk pull request was not judged by the controller beside it as before: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if v.Release != "" || v.JudgedBy != "" || v.BesideAt != nil || strings.Contains(v.Report, "release branch") {
|
|
t.Fatalf("a trunk pull request's verdict speaks of a release branch: %q %q %v", v.Release, v.JudgedBy, v.BesideAt)
|
|
}
|
|
if v.Beside["mesh-host"] != hostMain[:12] {
|
|
t.Fatalf("mesh-host was read at %s, not the commit asked %s", v.Beside["mesh-host"], hostMain[:12])
|
|
}
|
|
}
|
|
|
|
// **Without mesh-host's release branch, the release branch's controller keeps the validator it vendors** (ADR 0358
|
|
// §2), and the verdict says so.
|
|
func TestWithoutTheNodeEnginesReleaseBranchTheJudgeKeepsItsVendoredValidator(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
beside, controllerRelease, _, _ := theReleasedMesh(t)
|
|
host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"), nil)
|
|
beside["mesh-host"] = Beside{Repository: host, Ref: releaseBranch, Else: "main"}
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-vendored", Repository: catalogue, Ref: head,
|
|
Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch,
|
|
JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside,
|
|
Toolchain: "golang"}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with the vendored validator") {
|
|
t.Fatalf("without mesh-host's release branch the judge did not keep its vendored validator: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if v.JudgedBy != controllerRelease[:12] || v.BesideAt["mesh-host"] != "main" || v.Beside["mesh-host"] != hostMain[:12] {
|
|
t.Fatalf("judged by %q, mesh-host read at %q (%s)", v.JudgedBy, v.BesideAt["mesh-host"], v.Beside["mesh-host"])
|
|
}
|
|
if !strings.Contains(v.Validator, "controller vendors") || !strings.Contains(v.Report, v.Validator) {
|
|
t.Fatalf("the verdict does not say the vendored validator judged: %q\n%s", v.Validator, v.Report)
|
|
}
|
|
}
|
|
|
|
// **Clause 7** (ADR 0358 §7): a release branch's own pull request into its trunk is checked as any pull request into a
|
|
// trunk — by the controller beside it as the mesh runs it, the repositories at what a trunk check reads — and its
|
|
// verdict names no release branch.
|
|
func TestAReleaseBranchsPullRequestIntoItsTrunkIsJudgedByTheControllerTheMeshRuns(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("the controller the mesh runs"),
|
|
aJudgeOf("the release branch's controller"))
|
|
host, hostMain, _ := aRepositoryWithARelease(t, aHostSaying("main's validator"),
|
|
aHostSaying("the release branch's validator"))
|
|
catalogue, _, releaseHead := aRepositoryWithARelease(t, map[string]string{
|
|
"modules/gitea/module.json": `{"module":"gitea"}`, CheckScript: "true\n"},
|
|
map[string]string{"modules/gitea/index.ts": "released"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-release-into-trunk", Repository: catalogue,
|
|
Ref: releaseHead, Owner: "novox", Repo: "mesh-catalog", Number: 300, Base: "main", JudgeCache: t.TempDir(),
|
|
Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Toolchain: "golang",
|
|
Beside: map[string]Beside{"mesh-controller": {Repository: controller, Ref: controllerMain},
|
|
"mesh-host": {Repository: host, Ref: hostMain}}}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Summary != "the controller the mesh runs with the vendored validator" {
|
|
t.Fatalf("the release branch's pull request into its trunk was not judged by the controller the mesh runs: %+v\n%s",
|
|
v.Gate, v.Report)
|
|
}
|
|
if v.Release != "" || v.JudgedBy != "" || v.BesideAt != nil || v.Validator != "" ||
|
|
strings.Contains(v.Report, "release branch") || strings.Contains(v.Gate.Summary, "[release") {
|
|
t.Fatalf("its verdict names a release branch: %q %q %v %q", v.Release, v.JudgedBy, v.BesideAt, v.Validator)
|
|
}
|
|
if v.Beside["mesh-host"] != hostMain[:12] || v.Beside["mesh-controller"] != controllerMain[:12] {
|
|
t.Fatalf("beside it were read %v, not the trunk's %s and %s", v.Beside, controllerMain[:12], hostMain[:12])
|
|
}
|
|
}
|
|
|
|
// **mesh-lab is read at its trunk, always** (ADR 0358): its replays run with the container runtime's socket, so a
|
|
// branch of the release's name in mesh-lab — which anyone may create — is never what a check reads.
|
|
func TestAReleaseBranchCheckReadsMeshLabAtItsTrunkEvenWhenItHasThatBranch(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
beside, _, _, _ := theReleasedMesh(t)
|
|
lab, labMain, labRelease := aRepositoryWithARelease(t, map[string]string{"README": "the lab"},
|
|
map[string]string{"README": "a branch anyone could push"})
|
|
beside["mesh-lab"] = Beside{Repository: lab, Ref: releaseBranch, Else: "main"}
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-lab", Repository: catalogue, Ref: head,
|
|
Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch,
|
|
JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside,
|
|
Toolchain: "golang"}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] || v.Beside["mesh-lab"] == labRelease[:12] {
|
|
t.Fatalf("mesh-lab was read at %q (%s), not its trunk main at %s", v.BesideAt["mesh-lab"], v.Beside["mesh-lab"],
|
|
labMain[:12])
|
|
}
|
|
}
|
|
|
|
// aPlantedWorkspace is a workspace a pull request's own script has written into, as it can: a git configuration in its
|
|
// HOME whose fsmonitor hook says each directory git runs in, a git configuration under XDG_CONFIG_HOME doing the same,
|
|
// and a Go environment file that would hand the judge's build a tool of its own. It answers the workspace and the
|
|
// file the hook writes to.
|
|
func aPlantedWorkspace(t *testing.T) (string, string) {
|
|
t.Helper()
|
|
workspace, logs := t.TempDir(), t.TempDir()
|
|
ran := filepath.Join(logs, "hook-ran")
|
|
hook := filepath.Join(workspace, "hook.sh")
|
|
files := map[string]string{
|
|
hook: "#!/bin/sh\necho \"$PWD\" >> " + ran + "\n",
|
|
filepath.Join(workspace, ".gitconfig"): "[core]\n\tfsmonitor = " + hook + "\n",
|
|
filepath.Join(workspace, ".config", "git", "config"): "[core]\n\tfsmonitor = " + hook + "\n",
|
|
filepath.Join(workspace, ".config", "go", "env"): "GOFLAGS=-toolexec=" + hook + "\n",
|
|
}
|
|
for name, body := range files {
|
|
if err := os.MkdirAll(filepath.Dir(name), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(name, []byte(body), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
return workspace, ran
|
|
}
|
|
|
|
// judgeRanTheHook says whether the planted hook ran in a judge's tree: the controller beside the check, or the
|
|
// change's own when it judges itself.
|
|
func judgeRanTheHook(t *testing.T, ran string) string {
|
|
t.Helper()
|
|
body, _ := os.ReadFile(ran)
|
|
for _, line := range strings.Split(string(body), "\n") {
|
|
if strings.HasSuffix(line, "/check/mesh-controller") {
|
|
return string(body)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// **Every judge is built where no pull request reaches** (issue 482, ADR 0358 review): a git configuration with a
|
|
// hook, and a Go environment file, planted in the workspace by an earlier pull request's script, do not run during
|
|
// the release judge's build — while the pull request's own script, in the workspace, still meets them.
|
|
func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheReleaseJudgesBuild(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
homes := filepath.Join(t.TempDir(), "homes")
|
|
t.Setenv("JUDGE_HOMES", homes)
|
|
registry := aFactsRegistry(t)
|
|
workspace, ran := aPlantedWorkspace(t)
|
|
beside, _, _, _ := theReleasedMesh(t)
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "git status >/dev/null 2>&1; true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-planted", Repository: catalogue, Ref: head,
|
|
Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch,
|
|
JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside,
|
|
Toolchain: "golang"}, workspace, registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Verdict != "pass" {
|
|
t.Fatalf("the check did not judge: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if body, _ := os.ReadFile(ran); !strings.Contains(string(body), "/check/mesh-catalog") {
|
|
t.Fatalf("the planted hook never ran, so this test proves nothing: %q", body)
|
|
}
|
|
if said := judgeRanTheHook(t, ran); said != "" {
|
|
t.Fatalf("the hook planted in the workspace ran during the judge's build:\n%s", said)
|
|
}
|
|
if body, _ := os.ReadFile(homes); len(body) == 0 || strings.Contains(string(body), workspace) {
|
|
t.Fatalf("the judge was built with a HOME in the workspace: %q", body)
|
|
}
|
|
}
|
|
|
|
// The same for the judge built with a node-engine change's own validator, into a release branch (ADR 0358 §2).
|
|
func TestAHookPlantedInTheWorkspaceDoesNotRunDuringTheValidatorJudgesBuild(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
homes := filepath.Join(t.TempDir(), "homes")
|
|
t.Setenv("JUDGE_HOMES", homes)
|
|
registry := aFactsRegistry(t)
|
|
workspace, ran := aPlantedWorkspace(t)
|
|
beside, _, _, _ := theReleasedMesh(t)
|
|
delete(beside, "mesh-host")
|
|
files := aHostSaying("the release branch's validator")
|
|
files["module.json"] = `{"module":"mesh-host"}`
|
|
files[CheckScript] = "git status >/dev/null 2>&1; true\n"
|
|
host, head := aPullRequestInto(t, files, releaseBranch, aHostSaying("this change's validator"))
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-planted-validator", Repository: host, Ref: head,
|
|
Owner: "novox", Repo: "mesh-host", Number: 111, Base: releaseBranch, Release: releaseBranch, Judge: "validator",
|
|
JudgeCache: t.TempDir(), Paths: []string{"validate/v.go"}, Modules: []string{"mesh-host"}, Beside: beside,
|
|
Toolchain: "golang"}, workspace, registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || !strings.HasSuffix(v.Gate.Summary, "the release branch's controller with this change's validator") {
|
|
t.Fatalf("a node-engine change into a release branch was not judged by that branch's controller with its own "+
|
|
"validator: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if body, _ := os.ReadFile(ran); !strings.Contains(string(body), "/check/mesh-host") {
|
|
t.Fatalf("the planted hook never ran, so this test proves nothing: %q", body)
|
|
}
|
|
if said := judgeRanTheHook(t, ran); said != "" {
|
|
t.Fatalf("the hook planted in the workspace ran during a judge's build:\n%s", said)
|
|
}
|
|
if body, _ := os.ReadFile(homes); strings.Count(string(body), "\n") != 2 || strings.Contains(string(body), workspace) {
|
|
t.Fatalf("the judges were not both built with a HOME outside the workspace: %q", body)
|
|
}
|
|
}
|
|
|
|
// **mesh-lab is never read at a release branch**, asked with a fallback or without (ADR 0358).
|
|
func TestTheBuildSeatNeverReadsMeshLabAtAReleaseBranch(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
registry := aFactsRegistry(t)
|
|
lab, labMain, _ := aRepositoryWithARelease(t, map[string]string{"README": "the lab"},
|
|
map[string]string{"README": "a branch anyone could push"})
|
|
for _, asked := range []Beside{{Repository: lab, Ref: releaseBranch}, {Repository: lab, Ref: releaseBranch, Else: "main"}} {
|
|
beside, _, _, _ := theReleasedMesh(t)
|
|
beside["mesh-lab"] = asked
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, releaseBranch, map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-704-lab-asked", Repository: catalogue, Ref: head,
|
|
Owner: "novox", Repo: "mesh-catalog", Number: 286, Base: releaseBranch, Release: releaseBranch,
|
|
JudgeCache: t.TempDir(), Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Beside: beside,
|
|
Toolchain: "golang"}, t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.BesideAt["mesh-lab"] != "main" || v.Beside["mesh-lab"] != labMain[:12] {
|
|
t.Fatalf("mesh-lab asked as %+v was read at %q (%s), not main at %s", asked, v.BesideAt["mesh-lab"],
|
|
v.Beside["mesh-lab"], labMain[:12])
|
|
}
|
|
}
|
|
}
|
|
|
|
// **Judges are built warm and where no pull request reaches** (ADR 0358 §6): every judge build uses the judge cache's
|
|
// one Go cache, kept between checks; its home is made fresh under the judge cache and removed after; nothing is left in
|
|
// a temporary directory of the host; and no container a check runs for the pull request mounts the judge cache.
|
|
func TestJudgesAreBuiltWarmFromAJudgeCacheNoCheckContainerMounts(t *testing.T) {
|
|
built := aRuntimeThatBuildsTheJudge(t)
|
|
runs := filepath.Join(t.TempDir(), "runs")
|
|
t.Setenv("RUNS", runs)
|
|
tmp := t.TempDir()
|
|
t.Setenv("TMPDIR", tmp)
|
|
registry := aFactsRegistry(t)
|
|
cache := t.TempDir()
|
|
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("the controller the mesh runs"), nil)
|
|
beside := map[string]Beside{"mesh-controller": {Repository: controller, Ref: controllerMain}}
|
|
for i := range 2 {
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, "main", map[string]string{"modules/gitea/index.ts": fmt.Sprint(i)})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: fmt.Sprintf("check-704-warm-%d", i), Repository: catalogue,
|
|
Ref: head, Owner: "novox", Repo: "mesh-catalog", Number: 1, Base: "main", JudgeCache: cache,
|
|
Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"}, Toolchain: "golang", Beside: beside},
|
|
t.TempDir(), registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Summary != "the controller the mesh runs with the vendored validator" {
|
|
t.Fatalf("check %d was not judged: %+v\n%s", i, v.Gate, v.Report)
|
|
}
|
|
if i == 0 {
|
|
if entries, _ := os.ReadDir(filepath.Join(cache, "go-cache")); len(entries) == 0 {
|
|
t.Fatal("the judge's build left nothing in the judge cache's Go cache to build the next judge warm")
|
|
}
|
|
}
|
|
}
|
|
// Both judges built with the one Go cache of the judge cache.
|
|
said, _ := os.ReadFile(built)
|
|
if n := strings.Count(string(said), "GOCACHE="+filepath.Join(cache, "go-cache")+" GOENV=off go build"); n != 2 {
|
|
t.Fatalf("%d of 2 judge builds used the judge cache's Go cache:\n%s", n, said)
|
|
}
|
|
// Nothing left behind: no home under the judge cache, nothing in the host's temporary directory.
|
|
if left, _ := os.ReadDir(filepath.Join(cache, "homes")); len(left) != 0 {
|
|
t.Fatalf("judge builds left %d home(s) under the judge cache", len(left))
|
|
}
|
|
if left, _ := os.ReadDir(tmp); len(left) != 0 {
|
|
t.Fatalf("a check left %d entr(ies) in the temporary directory, e.g. %s", len(left), left[0].Name())
|
|
}
|
|
// Only a judge build mounts the judge cache: no container the check runs for the pull request does.
|
|
body, _ := os.ReadFile(runs)
|
|
judgeBuilds := 0
|
|
for _, line := range strings.Split(strings.TrimSpace(string(body)), "\n") {
|
|
judging := strings.Contains(line, " go build ")
|
|
if judging {
|
|
judgeBuilds++
|
|
if !strings.Contains(line, "--volume "+filepath.Join(cache, "go-cache")+":") {
|
|
t.Fatalf("a judge build did not mount the judge cache's Go cache: %s", line)
|
|
}
|
|
continue
|
|
}
|
|
if strings.Contains(line, cache) {
|
|
t.Fatalf("a container the check runs for the pull request names the judge cache: %s", line)
|
|
}
|
|
}
|
|
if judgeBuilds != 2 {
|
|
t.Fatalf("%d judge builds were run, not 2", judgeBuilds)
|
|
}
|
|
}
|
|
|
|
// Without a judge cache, a judge is built cold under the check's own root — never in a temporary directory of the
|
|
// host — and a judge cache inside the workspace is refused.
|
|
func TestWithoutAJudgeCacheAJudgeIsBuiltColdUnderTheChecksRoot(t *testing.T) {
|
|
built := aRuntimeThatBuildsTheJudge(t)
|
|
tmp := t.TempDir()
|
|
t.Setenv("TMPDIR", tmp)
|
|
registry := aFactsRegistry(t)
|
|
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("the controller the mesh runs"), nil)
|
|
beside := map[string]Beside{"mesh-controller": {Repository: controller, Ref: controllerMain}}
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, "main", map[string]string{"modules/gitea/index.ts": "x"})
|
|
workspace := t.TempDir()
|
|
spec := CheckSpec{ID: "check-704-cold", Repository: catalogue, Ref: head, Owner: "novox", Repo: "mesh-catalog",
|
|
Number: 1, Base: "main", Paths: []string{"modules/gitea/index.ts"}, Modules: []string{"gitea"},
|
|
Toolchain: "golang", Beside: beside}
|
|
v, err := Check(t.Context(), Command, spec, workspace, registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Verdict != "pass" {
|
|
t.Fatalf("the check was not judged: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
said, _ := os.ReadFile(built)
|
|
if !strings.Contains(string(said), "GOCACHE="+filepath.Join(workspace, "check", "judge-go-cache")+" GOENV=off go build") {
|
|
t.Fatalf("without a judge cache the judge was not built under the check's root:\n%s", said)
|
|
}
|
|
if left, _ := os.ReadDir(tmp); len(left) != 0 {
|
|
t.Fatalf("a check left %d entr(ies) in the temporary directory", len(left))
|
|
}
|
|
spec.JudgeCache = filepath.Join(workspace, "judge-cache")
|
|
if _, err := Check(t.Context(), Command, spec, workspace, registry, GitCredential{}, nil); err == nil ||
|
|
!strings.Contains(err.Error(), "inside the workspace") {
|
|
t.Fatalf("a judge cache inside the workspace was taken: %v", err)
|
|
}
|
|
}
|
|
|
|
// aLabWithReplays is mesh-lab with replays: a Go module of its own whose one test passes, needing nothing fetched.
|
|
func aLabWithReplays(t *testing.T) (string, string) {
|
|
t.Helper()
|
|
lab, labMain, _ := aRepositoryWithARelease(t, map[string]string{
|
|
"replays/go.mod": "module example.org/replays\n\ngo 1.22\n",
|
|
"replays/replay_test.go": "package replays\n\nimport \"testing\"\n\nfunc TestAReplay(t *testing.T) {}\n",
|
|
}, nil)
|
|
return lab, labMain
|
|
}
|
|
|
|
// **mesh-lab's replays run where no pull request reaches** (novox/hq issue 709, ADR 0358 §6): they hold the container
|
|
// runtime's socket, so a Go environment file with a -toolexec hook and a Go cache and module cache planted in the
|
|
// workspace by a pull request's script are neither run nor read — the replays run from a copy of mesh-lab outside
|
|
// the workspace with a fresh home and the judge cache's caches, all removed or kept outside every check container.
|
|
func TestTheReplaysRunWhereNoPullRequestReaches(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
replays := filepath.Join(t.TempDir(), "replays")
|
|
t.Setenv("REPLAYS", replays)
|
|
registry := aFactsRegistry(t)
|
|
workspace, ran := aPlantedWorkspace(t)
|
|
for _, poisoned := range []string{filepath.Join(workspace, "go-cache", "README"),
|
|
filepath.Join(workspace, "go-modules", "cache", "download", "planted")} {
|
|
if err := os.MkdirAll(filepath.Dir(poisoned), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(poisoned, []byte("planted by a pull request"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
cache := t.TempDir()
|
|
controller, controllerMain, _ := aRepositoryWithARelease(t, aJudgeOf("the controller the mesh runs"), nil)
|
|
lab, labMain := aLabWithReplays(t)
|
|
catalogue, head := aPullRequestInto(t, map[string]string{"modules/gitea/module.json": `{"module":"gitea"}`,
|
|
CheckScript: "true\n"}, "main", map[string]string{"modules/gitea/index.ts": "x"})
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-709", Repository: catalogue, Ref: head, Owner: "novox",
|
|
Repo: "mesh-catalog", Number: 1, Base: "main", JudgeCache: cache, Paths: []string{"modules/gitea/index.ts"},
|
|
Modules: []string{"gitea"}, Toolchain: "golang", Beside: map[string]Beside{
|
|
"mesh-controller": {Repository: controller, Ref: controllerMain}, "mesh-lab": {Repository: lab, Ref: labMain}}},
|
|
workspace, registry, GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Report, "--- the replays") {
|
|
t.Fatalf("the replays did not run and pass: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
body, _ := os.ReadFile(replays)
|
|
said := strings.TrimSpace(string(body))
|
|
if said == "" || strings.Count(said, "\n") != 0 {
|
|
t.Fatalf("the replays ran %q, not once", said)
|
|
}
|
|
for _, want := range []string{"GOCACHE=" + filepath.Join(cache, "go-cache") + " ",
|
|
"GOMODCACHE=" + filepath.Join(cache, "go-modules") + " ", "GOENV=off ", "GOFLAGS=-buildvcs=false ",
|
|
"HOME=" + filepath.Join(cache, "homes", "check-709-replays", "home") + " ",
|
|
"DIR=" + filepath.Join(cache, "homes", "check-709-replays", "mesh-lab", "replays")} {
|
|
if !strings.Contains(said, want) {
|
|
t.Fatalf("the replays did not run with %q: %s", want, said)
|
|
}
|
|
}
|
|
if strings.Contains(said, workspace) {
|
|
t.Fatalf("the replays ran with something of the workspace: %s", said)
|
|
}
|
|
if body, _ := os.ReadFile(ran); strings.Contains(string(body), "replays") || strings.Contains(string(body), "mesh-lab") {
|
|
t.Fatalf("a hook planted in the workspace ran during the replays:\n%s", body)
|
|
}
|
|
if left, _ := os.ReadDir(filepath.Join(cache, "homes")); len(left) != 0 {
|
|
t.Fatalf("the replays left %d entr(ies) under the judge cache's homes", len(left))
|
|
}
|
|
}
|
|
|
|
// aSelfJudgingControllerThatWritesIntoTheReplays is a controller pull request whose own judge, while the gate runs it,
|
|
// writes a test into the replays beside the check — the reviewer's case for issue 709: code of the pull request's,
|
|
// run before the replays, reaching what they run.
|
|
func aSelfJudgingControllerThatWritesIntoTheReplays(t *testing.T, marker string) (string, string) {
|
|
t.Helper()
|
|
files := aJudgeOf("the change's controller")
|
|
evil := "package replays\n\nimport (\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestEvil(t *testing.T) {\n\t_ = os.WriteFile(" +
|
|
fmt.Sprintf("%q", marker) + ", []byte(\"ran\"), 0o644)\n}\n"
|
|
files["cmd/mesh-controller/main.go"] = strings.Replace(files["cmd/mesh-controller/main.go"], "func main() {",
|
|
"func main() {\n\tif len(os.Args) > 2 && os.Args[1] == \"merge-gate\" {\n\t\t_ = os.WriteFile(os.Getenv(\"MESH_CHECK_BESIDE\")+"+
|
|
"\"/mesh-lab/replays/evil_test.go\", []byte("+fmt.Sprintf("%q", evil)+"), 0o644)\n\t\t_ = os.WriteFile("+
|
|
fmt.Sprintf("%q", marker+".wrote")+", nil, 0o644)\n\t}", 1)
|
|
return aPullRequestInto(t, files, "main", map[string]string{"cmd/x.txt": "x"})
|
|
}
|
|
|
|
// **Without a judge cache, the replays still never read what a pull request wrote** (issue 709, review 12303): the
|
|
// copy of mesh-lab, its home and throwaway caches are in a container volume of the check's own, made before anything
|
|
// of the change runs and removed after — never the workspace. With a judge cache, under it. In both: a self-judging
|
|
// controller pull request writing a test into the replays beside the check, a planted GOFLAGS hook and a poisoned
|
|
// cache entry in the workspace neither run nor are read.
|
|
func TestTheReplaysNeverReadWhatAPullRequestWroteWithOrWithoutAJudgeCache(t *testing.T) {
|
|
for _, withCache := range []bool{false, true} {
|
|
t.Run(fmt.Sprintf("judge cache %v", withCache), func(t *testing.T) {
|
|
aRuntimeThatBuildsTheJudge(t)
|
|
replays, volumes := filepath.Join(t.TempDir(), "replays"), filepath.Join(t.TempDir(), "volumes")
|
|
t.Setenv("REPLAYS", replays)
|
|
t.Setenv("VOLUMES", volumes)
|
|
tmp := t.TempDir()
|
|
t.Setenv("TMPDIR", tmp)
|
|
mount := filepath.Join(t.TempDir(), "mesh-replays")
|
|
t.Setenv("REPLAY_MOUNT", mount)
|
|
was := ReplayMount
|
|
ReplayMount = mount
|
|
t.Cleanup(func() { ReplayMount = was })
|
|
registry := aFactsRegistry(t)
|
|
workspace, ran := aPlantedWorkspace(t)
|
|
if err := os.MkdirAll(filepath.Join(workspace, "go-cache"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(workspace, "go-cache", "README"), []byte("planted"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
evilRan := filepath.Join(t.TempDir(), "evil-ran")
|
|
controller, head := aSelfJudgingControllerThatWritesIntoTheReplays(t, evilRan)
|
|
lab, labMain := aLabWithReplays(t)
|
|
cache := ""
|
|
if withCache {
|
|
cache = t.TempDir()
|
|
}
|
|
v, err := Check(t.Context(), Command, CheckSpec{ID: "check-709-self", Repository: controller, Ref: head,
|
|
Owner: "novox", Repo: "mesh-controller", Number: 330, Base: "main", Judge: "self", JudgeCache: cache,
|
|
Paths: []string{"cmd/x.txt"}, Modules: []string{"mesh-controller"}, Toolchain: "golang",
|
|
Beside: map[string]Beside{"mesh-lab": {Repository: lab, Ref: labMain}}}, workspace, registry,
|
|
GitCredential{}, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v.Gate == nil || v.Gate.Verdict != "pass" || !strings.Contains(v.Report, "--- the replays") {
|
|
t.Fatalf("the replays did not run and pass: %+v\n%s", v.Gate, v.Report)
|
|
}
|
|
if _, err := os.Stat(evilRan + ".wrote"); err != nil {
|
|
t.Fatalf("the self-judging pull request did not write into the replays beside it, so this proves nothing: %v", err)
|
|
}
|
|
if _, err := os.Stat(evilRan); err == nil {
|
|
t.Fatal("the test the pull request wrote into the replays ran with the socket")
|
|
}
|
|
if body, _ := os.ReadFile(ran); strings.Contains(string(body), "replays") || strings.Contains(string(body), "mesh-lab") {
|
|
t.Fatalf("a hook planted in the workspace ran during the replays:\n%s", body)
|
|
}
|
|
body, _ := os.ReadFile(replays)
|
|
said := strings.TrimSpace(string(body))
|
|
if said == "" || strings.Contains(said, workspace) || !strings.Contains(said, "GOENV=off ") {
|
|
t.Fatalf("the replays ran with something of the workspace, or not at all: %q", said)
|
|
}
|
|
base := mount
|
|
if withCache {
|
|
base = filepath.Join(cache, "homes", "check-709-self-replays")
|
|
}
|
|
if !strings.Contains(said, "DIR="+filepath.Join(base, "mesh-lab", "replays")) {
|
|
t.Fatalf("the replays did not run from the copy under %s: %s", base, said)
|
|
}
|
|
vols, _ := os.ReadFile(volumes)
|
|
if !withCache && (!strings.Contains(string(vols), "create --label "+BuildLabel+"=check-709-self") ||
|
|
!strings.Contains(string(vols), "rm --force mesh-check-check-709-self-replays")) {
|
|
t.Fatalf("the check's own replay volume was not made and removed: %q", vols)
|
|
}
|
|
if _, err := os.Stat(base); err == nil {
|
|
t.Fatalf("the replays' place %s was left after the check", base)
|
|
}
|
|
if left, _ := os.ReadDir(tmp); len(left) != 0 {
|
|
t.Fatalf("a check left %d entr(ies) in the temporary directory", len(left))
|
|
}
|
|
})
|
|
}
|
|
}
|