mesh/merge-gate head ef3e5ae9b0dc merged into trunk 1d244d5aa24e — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head ef3e5ae9b0dc merged into trunk 1d244d5aa24e — pass: its merge-check.sh passed
mesh/delivery delivered
The genesis image copies the whole clone with .git, so its controller carried the commit it was built at, as the bundles did.
146 lines
5.0 KiB
Go
146 lines
5.0 KiB
Go
package builder
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// **A Go bundle does not vary with the commit it was built at** (novox/hq ADR 0267 rule 6, issue 363). A
|
|
// module built from its repository's root — the controller — is compiled with the clone's `.git` in what
|
|
// the toolchain sees, and Go stamps the commit, its time and whether the tree was modified into every
|
|
// binary built there unless told not to. Two builds of one program at commits that differ only in a
|
|
// README then made two binaries, so an unchanged build source was a new digest.
|
|
//
|
|
// The toolchain's own compile line is run here, as the build seat runs it inside the toolchain image: in
|
|
// two clones at two paths, of two commits that differ outside the program. One digest is wanted.
|
|
func TestAGoBundleBuiltTwiceFromOneBuildSourceIsOneDigest(t *testing.T) {
|
|
for _, tool := range []string{"go", "git"} {
|
|
if _, err := exec.LookPath(tool); err != nil {
|
|
t.Skipf("%s is not on this machine: %v", tool, err)
|
|
}
|
|
}
|
|
chain, err := ToolchainFor("go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
root := t.TempDir()
|
|
repo := filepath.Join(root, "origin")
|
|
files := map[string]string{
|
|
"go.mod": "module example.com/ctl\n\ngo 1.22\n",
|
|
"cmd/ctl/main.go": "package main\n\nfunc main() { println(\"ctl\") }\n",
|
|
"README.md": "one\n",
|
|
}
|
|
for name, body := range files {
|
|
if err := os.MkdirAll(filepath.Dir(filepath.Join(repo, name)), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(repo, name), []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
git := func(dir string, args ...string) string {
|
|
t.Helper()
|
|
cmd := exec.Command("git", append([]string{"-c", "user.name=t", "-c", "user.email=t@example.invalid",
|
|
"-c", "commit.gpgsign=false", "-c", "init.defaultBranch=main"}, args...)...)
|
|
cmd.Dir = dir
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, out)
|
|
}
|
|
return strings.TrimSpace(string(out))
|
|
}
|
|
git(repo, "init", "-q")
|
|
git(repo, "add", ".")
|
|
git(repo, "commit", "-q", "-m", "one")
|
|
first := git(repo, "rev-parse", "HEAD")
|
|
if err := os.WriteFile(filepath.Join(repo, "README.md"), []byte("two\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
git(repo, "commit", "-q", "-am", "only the README")
|
|
second := git(repo, "rev-parse", "HEAD")
|
|
|
|
build := func(commit, at string) string {
|
|
t.Helper()
|
|
clone := filepath.Join(root, at)
|
|
git(root, "clone", "-q", repo, clone)
|
|
git(clone, "checkout", "-q", commit)
|
|
out := filepath.Join(clone, Out("server"), "ctl")
|
|
line := append([]string(nil), chain.Compile...)
|
|
line = append(line, "-ldflags", strings.Join(chain.LinkerFlags, " "), chain.OutputFlag, out, "./cmd/ctl")
|
|
cmd := exec.Command(line[0], line[1:]...)
|
|
cmd.Dir = clone
|
|
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local", "GOPROXY=off", "GOWORK=off")
|
|
if b, err := cmd.CombinedOutput(); err != nil {
|
|
t.Fatalf("%s: %v\n%s", strings.Join(line, " "), err, b)
|
|
}
|
|
body, err := os.ReadFile(out)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Contains(body, []byte(commit)) {
|
|
t.Errorf("the binary built at %s carries the commit it was built at", commit[:12])
|
|
}
|
|
sum := sha256.Sum256(body)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
a, b := build(first, "a"), build(second, "elsewhere/b")
|
|
if a != b {
|
|
t.Fatalf("one build source, two digests: %s at %s, %s at %s (compiled with %q)",
|
|
a[:12], first[:12], b[:12], second[:12], strings.Join(chain.Compile, " "))
|
|
}
|
|
}
|
|
|
|
// The toolchain says so itself, so a reader of the compile line sees it without running a build.
|
|
func TestTheGoToolchainStampsNoVersionControl(t *testing.T) {
|
|
chain, err := ToolchainFor("go")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if joined := strings.Join(chain.Compile, " "); !strings.Contains(joined, "-buildvcs=false") {
|
|
t.Fatalf("the go toolchain stamps the commit into what it compiles: %q", joined)
|
|
}
|
|
}
|
|
|
|
// **And so does every recipe in this repository that compiles Go** (review of issue 363's fix): the
|
|
// controller's genesis image copies the whole clone, `.git` with it, and the reference images beside it
|
|
// are built the same way by the Makefile.
|
|
func TestEveryGoRecipeInThisRepositoryStampsNoVersionControl(t *testing.T) {
|
|
root := filepath.Join("..", "..")
|
|
var recipes []string
|
|
err := filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if d.IsDir() && (d.Name() == "vendor" || d.Name() == "testdata" || d.Name() == ".git") {
|
|
return filepath.SkipDir
|
|
}
|
|
if !d.IsDir() && d.Name() == "Dockerfile" {
|
|
recipes = append(recipes, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(recipes) == 0 {
|
|
t.Fatal("no recipe found in this repository")
|
|
}
|
|
for _, path := range recipes {
|
|
body, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, line := range strings.Split(string(body), "\n") {
|
|
if strings.Contains(line, "go build") && !strings.Contains(line, "-buildvcs=false") {
|
|
t.Errorf("%s compiles Go and stamps the commit into it: %s", path, strings.TrimSpace(line))
|
|
}
|
|
}
|
|
}
|
|
}
|