diff --git a/internal/builder/check.go b/internal/builder/check.go index 9b299257..3d333a8c 100644 --- a/internal/builder/check.go +++ b/internal/builder/check.go @@ -1637,35 +1637,52 @@ func dialable(ctx context.Context, address string) error { return fmt.Errorf("nothing took a connection at %s", address) } -// sdkPin is the mesh-sdk version the controller in dir takes (its go.mod's go requirement), or "" when it names none. -func sdkPin(dir string) string { +// sdkPin is the mesh-sdk version the controller in dir takes (its go.mod's go requirement), or "" when it names none; and +// local says that version is no real one: the placeholder v0.0.0, or a replace of mesh-sdk by a directory, which a +// workspace or a fixture uses and no release holds. +func sdkPin(dir string) (version string, local bool) { body, err := os.ReadFile(filepath.Join(dir, "go.mod")) if err != nil { - return "" + return "", false } + const path = "git.novox.be/novox/mesh-sdk/go" + replaced := false for _, line := range strings.Split(string(body), "\n") { - f := strings.Fields(strings.TrimPrefix(strings.TrimSpace(line), "require ")) - if len(f) >= 2 && f[0] == "git.novox.be/novox/mesh-sdk/go" { - return f[1] + f := strings.Fields(strings.TrimPrefix(strings.TrimPrefix(strings.TrimSpace(line), "require "), "replace ")) + switch { + case len(f) >= 3 && f[0] == path && f[len(f)-2] == "=>" && (strings.HasPrefix(f[len(f)-1], ".") || strings.HasPrefix(f[len(f)-1], "/")): + replaced = true + case len(f) >= 2 && f[0] == path && version == "" && f[1] != "=>": + version = f[1] } } - return "" + return version, version == "v0.0.0" || (replaced && version != "") } -// validatorAt names the validator of the controller in dir as mesh-sdk's at that controller's pin (issue 1391). +// validatorAt names the validator of the controller in dir as mesh-sdk's at that controller's pin (issue 1391): the +// version when it is a real one, and a local mesh-sdk said as such when it is not. func validatorAt(dir string) string { - if v := sdkPin(dir); v != "" { + switch v, local := sdkPin(dir); { + case v == "": + return "mesh-sdk's validator at the controller's pin" + case local: + return "mesh-sdk's validator from a local mesh-sdk (" + v + ", replaced)" + default: return "mesh-sdk's validator at " + v } - return "mesh-sdk's validator at the controller's pin" } -// pinNote is the version of that pin, for a sentence that already says "the pin of": ", " and the version, or nothing. +// pinNote is the pin for a sentence that already says "the pin of": the version in brackets when it is a real one, a +// local mesh-sdk said as such when it is not, or nothing. func pinNote(dir string) string { - if v := sdkPin(dir); v != "" { + switch v, local := sdkPin(dir); { + case v == "": + return "" + case local: + return " (a local mesh-sdk, " + v + ", replaced)" + default: return " (" + v + ")" } - return "" } // judge builds the controller that judges a change: the one the mesh runs, beside the check as diff --git a/internal/builder/check_pin_test.go b/internal/builder/check_pin_test.go index 0814009d..721db0d8 100644 --- a/internal/builder/check_pin_test.go +++ b/internal/builder/check_pin_test.go @@ -3,6 +3,7 @@ package builder import ( "os" "path/filepath" + "strings" "testing" ) @@ -23,4 +24,22 @@ func TestTheValidatorIsNamedAsMeshSDKsAtTheControllersPin(t *testing.T) { if got := validatorAt(t.TempDir()); got != "mesh-sdk's validator at the controller's pin" { t.Fatalf("a controller with no go.mod is said as %q", got) } + + // A placeholder or a replaced pin is no version: it is said as a local mesh-sdk. + for name, mod := range map[string]string{ + "placeholder": "module example.test/c\n\nrequire git.novox.be/novox/mesh-sdk/go v0.0.0\n", + "replaced": "module example.test/c\n\nrequire git.novox.be/novox/mesh-sdk/go v0.1.14\n\n" + + "replace git.novox.be/novox/mesh-sdk/go => ../mesh-sdk/go\n", + } { + d := t.TempDir() + if err := os.WriteFile(filepath.Join(d, "go.mod"), []byte(mod), 0o644); err != nil { + t.Fatal(err) + } + if got := validatorAt(d); !strings.Contains(got, "a local mesh-sdk") || !strings.Contains(got, "replaced") { + t.Fatalf("%s: the check says %q, which does not say the mesh-sdk is local", name, got) + } + if got := pinNote(d); !strings.HasPrefix(got, " (a local mesh-sdk, v") { + t.Fatalf("%s: the pin note is %q", name, got) + } + } } diff --git a/internal/builder/siblings_test.go b/internal/builder/siblings_test.go index edeab57a..e175158d 100644 --- a/internal/builder/siblings_test.go +++ b/internal/builder/siblings_test.go @@ -133,7 +133,8 @@ func TestAHostMemberIsCheckedBesideItsControllerSibling(t *testing.T) { if want := "sibling novox/mesh-controller#346 merge " + v.Beside["mesh-controller"]; v.JudgeFrom != want { t.Fatalf("the judge came from %q, not %q", v.JudgeFrom, want) } - if want := "mesh-sdk's validator at the pin of sibling novox/mesh-controller#346 merge " + v.Beside["mesh-controller"]; v.Validator != want { + if want := "mesh-sdk's validator at the pin of sibling novox/mesh-controller#346 merge " + v.Beside["mesh-controller"] + + " (a local mesh-sdk, v0.0.0, replaced)"; v.Validator != want { t.Fatalf("the validator was %q, not %q", v.Validator, want) } // The statuses' "beside siblings" part is the forge module's to write, from the verdict's siblings.