mesh-build-go: bring the mirror to the trunk commit so the merge preview deepens from it, and place the siblings side by side (issue 1921, try speed S6)

This commit is contained in:
2026-10-25 01:10:19 +02:00
parent 23307935b9
commit 8536021f82
7 changed files with 110 additions and 21 deletions
@@ -184,6 +184,12 @@ func (m *Module) fetchPhases(ctx context.Context, req buildstep.FetchRequest, id
}
remote := m.remote(repository)
opts := m.fetchOptions(ctx, repository, commit, ref)
mergeOpts := m.FetchOptions
if base != "" {
// The mirror is brought to the trunk commit too, so the merge preview deepens the tree from it and asks the forge
// for no history the mirror holds (issue 1921, try speed S6).
mergeOpts = m.fetchOptions(ctx, repository, base, "")
}
clock.lap(&took.Mirror)
if err := buildstep.FetchCommit(ctx, m.Run, tree, remote, commit, ref, opts...); err != nil {
return FetchResult{}, err
@@ -191,7 +197,7 @@ func (m *Module) fetchPhases(ctx context.Context, req buildstep.FetchRequest, id
clock.lap(&took.Fetch)
res := FetchResult{Workspace: id, Commit: commit, Took: took}
if base != "" {
if err := m.merge(ctx, &res, tree, remote, base, commit); err != nil {
if err := m.merge(ctx, &res, tree, remote, base, commit, mergeOpts); err != nil {
return FetchResult{}, err
}
}
@@ -349,7 +349,8 @@ func TestTheFetchSaysTheTimeOfEachPhaseAndTheyAddUpToTheTotal(t *testing.T) {
t.Fatalf("the result says no phase times: %s", st.Result)
}
got := *res.Took
want := FetchTook{Mirror: 1, Fetch: 2, Merge: 3, Dissociate: 4, Modules: 5, Total: 15}
// the mirror is brought to the head and to the base (1 s each), and the preview deepens from it (1 s) before merging
want := FetchTook{Mirror: 2, Fetch: 2, Merge: 4, Dissociate: 4, Modules: 5, Total: 17}
if got != want {
t.Errorf("phases = %+v, want %+v", got, want)
}
@@ -12,6 +12,7 @@ import (
"time"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
func fetchOnce(t *testing.T, m *Module, states *keptStates, args map[string]any) FetchResult {
@@ -188,3 +189,61 @@ func TestTheWorkspaceIsSelfContainedAfterAFetchThroughTheMirror(t *testing.T) {
t.Errorf("the workspace is not whole: %v %s", err, out)
}
}
// 1921/S6: with a base, the mirror is brought to the trunk commit too, and the merge preview deepens the tree from the
// mirror: it asks the forge for no history the mirror holds.
func TestTheMirrorIsBroughtToTheBaseAndThePreviewFetchesFromIt(t *testing.T) {
f := &fakeStep{gomods: "go.mod\n", rule: previewRules(false)}
m, states := fetchModule(t, f)
mirror := filepath.Join(m.Cache, "mirror", "novox", "mesh-sdk.git")
f.files = map[string]string{"modules/m/module.json": "{}\n", "modules/m/go.sum": "sum\n", "go.mod": "module x\n", "go.sum": ""}
fetchOnce(t, m, states, map[string]any{"repository": "novox/mesh-sdk", "commit": commitSHA, "base": baseSHA})
lines := strings.Join(f.lines(), "\n")
if !strings.Contains(lines, "-C "+mirror+" fetch --no-tags origin "+baseSHA) {
t.Errorf("the mirror was not brought to the base:\n%s", lines)
}
if !strings.Contains(lines, "fetch --no-tags --unshallow "+mirror+" "+baseSHA) || strings.Contains(lines, "--unshallow origin") {
t.Errorf("the preview fetched the trunk's history from the forge:\n%s", lines)
}
}
// 1921/S6: the siblings are placed side by side: each sibling's fetch has started before any of them ends.
func TestTheSiblingsArePlacedAtOnce(t *testing.T) {
var mu sync.Mutex
started := map[string]bool{}
both := make(chan struct{})
f := &fakeStep{gomods: "go.mod\n"}
f.rule = func(line string) (forgekey.Ran, bool) {
if !strings.Contains(line, "/beside/") || !strings.Contains(line, " fetch --no-tags --depth 1 ") {
return forgekey.Ran{}, false
}
mu.Lock()
for _, name := range []string{"mesh-catalog", "mesh-host"} {
if strings.Contains(line, "/beside/"+name) {
started[name] = true
}
}
if len(started) == 2 {
select {
case <-both:
default:
close(both)
}
}
mu.Unlock()
select {
case <-both:
case <-time.After(2 * time.Second):
return forgekey.Ran{Status: 1, Stderr: "the other sibling's fetch never started"}, true
}
return forgekey.Ran{}, false
}
m, states := fetchModule(t, f)
res := fetchOnce(t, m, states, map[string]any{"repository": "novox/mesh-controller", "commit": commitSHA, "beside": []any{
map[string]any{"repository": "novox/mesh-host", "commit": commitSHA},
map[string]any{"repository": "novox/mesh-catalog", "commit": commitSHA},
}})
if len(res.Beside) != 2 || res.Beside[0].Dir != "mesh-catalog" || res.Beside[1].Dir != "mesh-host" {
t.Errorf("beside = %+v, want both, in name order", res.Beside)
}
}
@@ -13,6 +13,7 @@ import (
"path/filepath"
"sort"
"strings"
"sync"
"git.novox.be/novox/mesh-sdk/go/buildstep"
)
@@ -34,8 +35,9 @@ func (m *Module) remote(repository string) string {
}
// merge makes the merge preview of the head onto base and writes the answer into res.
func (m *Module) merge(ctx context.Context, res *FetchResult, tree, remote, base, commit string) error {
mr, err := buildstep.MergeInto(ctx, m.Run, tree, remote, base, commit, m.FetchOptions...)
// opts are the fetch's options, with the mirror when it holds base.
func (m *Module) merge(ctx context.Context, res *FetchResult, tree, remote, base, commit string, opts []buildstep.Option) error {
mr, err := buildstep.MergeInto(ctx, m.Run, tree, remote, base, commit, opts...)
if err != nil {
return err
}
@@ -89,23 +91,22 @@ func (m *Module) besideDir(workspace string) string {
// placeSiblings fetches each sibling into beside/<name> of the workspace root, as a plain directory. A symlink anywhere
// in a placed tree fails the step naming it: the mesh creates none, and a test must not be led out of its sibling.
func (m *Module) placeSiblings(ctx context.Context, root string, siblings []siblingArg) ([]Sibling, error) {
var placed []Sibling
for _, s := range siblings {
name := s.repository[strings.Index(s.repository, "/")+1:]
dir := filepath.Join(root, "beside", name)
if err := os.MkdirAll(dir, 0o700); err != nil {
// The siblings are placed side by side (issue 1921, try speed S6); the first failure in the order given is the step's.
placed := make([]Sibling, len(siblings))
errs := make([]error, len(siblings))
var wg sync.WaitGroup
for i, s := range siblings {
wg.Add(1)
go func() {
defer wg.Done()
placed[i], errs[i] = m.placeSibling(ctx, root, s)
}()
}
wg.Wait()
for _, err := range errs {
if err != nil {
return nil, err
}
if err := buildstep.FetchCommit(ctx, m.Run, dir, m.remote(s.repository), s.commit, s.ref, m.fetchOptions(ctx, s.repository, s.commit, s.ref)...); err != nil {
return nil, fmt.Errorf("beside %s: %w", s.repository, err)
}
if err := m.dissociate(ctx, dir); err != nil {
return nil, fmt.Errorf("beside %s: %w", s.repository, err)
}
if link := firstSymlink(dir); link != "" {
return nil, fmt.Errorf("beside %s holds a symlink, %s", s.repository, link)
}
placed = append(placed, Sibling{Repository: s.repository, Commit: s.commit, Dir: name})
}
sort.Slice(placed, func(i, j int) bool { return placed[i].Dir < placed[j].Dir })
if len(placed) > 0 {
@@ -121,6 +122,25 @@ func (m *Module) placeSiblings(ctx context.Context, root string, siblings []sibl
return placed, nil
}
// placeSibling fetches one sibling into beside/<name> of the workspace root.
func (m *Module) placeSibling(ctx context.Context, root string, s siblingArg) (Sibling, error) {
name := s.repository[strings.Index(s.repository, "/")+1:]
dir := filepath.Join(root, "beside", name)
if err := os.MkdirAll(dir, 0o700); err != nil {
return Sibling{}, err
}
if err := buildstep.FetchCommit(ctx, m.Run, dir, m.remote(s.repository), s.commit, s.ref, m.fetchOptions(ctx, s.repository, s.commit, s.ref)...); err != nil {
return Sibling{}, fmt.Errorf("beside %s: %w", s.repository, err)
}
if err := m.dissociate(ctx, dir); err != nil {
return Sibling{}, fmt.Errorf("beside %s: %w", s.repository, err)
}
if link := firstSymlink(dir); link != "" {
return Sibling{}, fmt.Errorf("beside %s holds a symlink, %s", s.repository, link)
}
return Sibling{Repository: s.repository, Commit: s.commit, Dir: name}, nil
}
// besideRecord is the file in a workspace's root naming the siblings the fetch placed beside it.
const besideRecord = "beside.json"
@@ -85,7 +85,8 @@ func TestFetchWithABaseMergesAndAnswersThePreview(t *testing.T) {
t.Errorf("trees = %+v", res.Trees)
}
lines := strings.Join(f.lines(), "\n")
for _, want := range []string{"fetch --no-tags --unshallow origin " + baseSHA, "checkout -q --detach " + baseSHA,
mirror := filepath.Join(m.Cache, "mirror", "novox", "mesh-sdk.git")
for _, want := range []string{"fetch --no-tags --unshallow " + mirror + " " + baseSHA, "checkout -q --detach " + baseSHA,
"core.hooksPath=/dev/null", "merge --no-ff --no-edit --no-verify"} {
if !strings.Contains(lines, want) {
t.Errorf("git was not asked %q:\n%s", want, lines)
+1 -1
View File
@@ -3,7 +3,7 @@ module meshbuildgo
go 1.26.0
require (
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024225846-cb548a6a588c
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024230839-4a12ccb78d19
github.com/distribution/distribution/v3 v3.1.2
github.com/nats-io/nats-server/v2 v2.11.17
github.com/nats-io/nats.go v1.54.0
+2
View File
@@ -2,6 +2,8 @@ git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024214154-bc16740feb0d h1:oJREw0gg
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024214154-bc16740feb0d/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024225846-cb548a6a588c h1:GWHP9ozfkFXHVEL2gE4Xa5kc5e8uPO+UL9+mau6iaIs=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024225846-cb548a6a588c/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024230839-4a12ccb78d19 h1:NqU4hkk/jHdTEcGFnnXvdOJwD8dH5u+qWaCdQQ+JYMc=
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261024230839-4a12ccb78d19/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=