The runner, the toolchain guard, the fetch, the parsers, buildcpu, the conformance kit and mesh-pack move from mesh-sdk's buildstep into modules/mesh-build-go/buildstep (own go.mod). mesh-build-go, mesh-build-node and mesh-build-image require it by a same-repository replace; what the delivery parts read (the failure contract, the fetch request, the stale sweep, the module tree shape, NewID, CapacitySetting) stays imported from mesh-sdk as sdkstep. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
125 lines
4.5 KiB
Go
125 lines
4.5 KiB
Go
package buildstep
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
|
)
|
|
|
|
// Issue 890 C1 (ADR 0536 §2): a fetch through a bare mirror names the mirror in .git/objects/info/alternates, a
|
|
// plain file, and never links it.
|
|
func TestFetchCommitWithMirrorWritesAlternates(t *testing.T) {
|
|
mirror := t.TempDir()
|
|
ws := t.TempDir()
|
|
f := &fakeGit{}
|
|
// the fake's init does not create .git; make the objects directory as git init would.
|
|
if err := os.MkdirAll(filepath.Join(ws, ".git", "objects", "info"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
err := FetchCommit(context.Background(), f.run, ws, "ssh://mesh-forge/novox/mesh-sdk.git", sha, "", WithMirror(mirror))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := filepath.Join(ws, ".git", "objects", "info", "alternates")
|
|
b, err := os.ReadFile(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, want := strings.TrimSpace(string(b)), filepath.Join(mirror, "objects"); got != want {
|
|
t.Errorf("alternates %q, want %q", got, want)
|
|
}
|
|
if fi, err := os.Lstat(p); err != nil || !fi.Mode().IsRegular() {
|
|
t.Errorf("alternates is not a plain file: %v %v", fi, err)
|
|
}
|
|
// the alternates exist before the fetch runs
|
|
if len(f.calls) < 3 || !strings.Contains(f.calls[2], "fetch") {
|
|
t.Errorf("calls %v", f.calls)
|
|
}
|
|
}
|
|
|
|
func TestWithMirrorRefusesARelativeOrMissingMirror(t *testing.T) {
|
|
for _, m := range []string{"rel/mirror", filepath.Join(t.TempDir(), "absent")} {
|
|
f := &fakeGit{}
|
|
err := FetchCommit(context.Background(), f.run, t.TempDir(), "ssh://mesh-forge/novox/x.git", sha, "", WithMirror(m))
|
|
if err == nil || len(f.calls) != 0 {
|
|
t.Errorf("mirror %q: err %v, calls %v", m, err, f.calls)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUpdateMirrorInitsBareThenFetches(t *testing.T) {
|
|
mirror := filepath.Join(t.TempDir(), "mirror.git")
|
|
f := &fakeGit{}
|
|
if err := UpdateMirror(context.Background(), f.run, mirror, "ssh://mesh-forge/novox/x.git", sha, ""); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pm := "git -c credential.helper= -C " + mirror + " "
|
|
want := []string{
|
|
"git -c credential.helper= init -q --bare " + mirror,
|
|
pm + "remote add origin ssh://mesh-forge/novox/x.git",
|
|
pm + "fetch --no-tags origin " + sha,
|
|
pm + "cat-file -e " + sha + "^{commit}",
|
|
}
|
|
if strings.Join(f.calls, "\n") != strings.Join(want, "\n") {
|
|
t.Errorf("commands:\n%s\nwant:\n%s", strings.Join(f.calls, "\n"), strings.Join(want, "\n"))
|
|
}
|
|
}
|
|
|
|
func TestUpdateMirrorKeepsAnExistingMirrorAndFallsBackToTheRef(t *testing.T) {
|
|
mirror := t.TempDir() // exists: no init
|
|
if err := os.WriteFile(filepath.Join(mirror, "HEAD"), []byte("ref: refs/heads/main\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f := &fakeGit{}
|
|
f.rule = func(args string) (r forgekey.Ran) {
|
|
if strings.HasSuffix(args, "origin "+sha) {
|
|
r.Status = 1
|
|
r.Stderr = "not our ref"
|
|
}
|
|
return
|
|
}
|
|
if err := UpdateMirror(context.Background(), f.run, mirror, "ssh://mesh-forge/novox/x.git", sha, "refs/pull/3/head"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
all := strings.Join(f.calls, "\n")
|
|
if strings.Contains(all, "init") || strings.Contains(all, "remote add") || !strings.Contains(all, "fetch --no-tags origin refs/pull/3/head") {
|
|
t.Errorf("commands:\n%s", all)
|
|
}
|
|
}
|
|
|
|
func TestUpdateMirrorRefusesBeforeRunning(t *testing.T) {
|
|
f := &fakeGit{}
|
|
for _, c := range []struct{ remote, commit string }{
|
|
{"https://x/y.git", sha}, {"ssh://u@h/o/r.git", sha}, {"ssh://h/o/r.git", "abc"},
|
|
} {
|
|
if err := UpdateMirror(context.Background(), f.run, t.TempDir(), c.remote, c.commit, ""); err == nil {
|
|
t.Errorf("%v accepted", c)
|
|
}
|
|
}
|
|
if len(f.calls) != 0 {
|
|
t.Errorf("ran %v", f.calls)
|
|
}
|
|
}
|
|
|
|
// The forge's refusal reaches the reason in git's own words: the mirror of a repository its account cannot read said
|
|
// only "git -C: error:" (2026-10-26), since git writes a bare "error:" line before the forge's message.
|
|
func TestUpdateMirrorSaysTheForgesRefusal(t *testing.T) {
|
|
f := &fakeGit{}
|
|
f.rule = func(args string) (r forgekey.Ran) {
|
|
if strings.Contains(args, "fetch") {
|
|
r.Status = 128
|
|
r.Stderr = "error: \nerror: User: 22:nox-builder with Key: 30:mesh:ace.mesh-build-go is not authorized to read o/r.\nerror: \nfatal: Could not read from remote repository.\n"
|
|
}
|
|
return
|
|
}
|
|
err := UpdateMirror(context.Background(), f.run, filepath.Join(t.TempDir(), "m.git"), "ssh://mesh-forge/o/r.git", sha, "")
|
|
if err == nil || !strings.Contains(err.Error(), "nox-builder with Key: 30:mesh:ace.mesh-build-go is not authorized to read o/r.") ||
|
|
!strings.Contains(err.Error(), "Could not read from remote repository") || !strings.HasPrefix(err.Error(), "git fetch: ") {
|
|
t.Errorf("err = %v", err)
|
|
}
|
|
}
|