Find the catalogue's repository by its URL or seat source, and fail a plan whose catalogue repository is nowhere, so a take-in from a key no longer waits for ever (issue 1902)
This commit is contained in:
@@ -92,16 +92,42 @@ var predefinedAtTrunk = func(ctx context.Context, inv *inventory.Inventory) (pre
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return predefinedHead{}, err
|
return predefinedHead{}, err
|
||||||
}
|
}
|
||||||
repository := ""
|
return readPredefinedHead(ctx, entries, formsOfSources(ctx, inv))
|
||||||
|
}
|
||||||
|
|
||||||
|
// formsOfSources reads how the sources are spelled on this mesh; a variable so a test names the forge's address.
|
||||||
|
var formsOfSources = newSourceForms
|
||||||
|
|
||||||
|
// errNoCatalogueRepository says no module of the mesh is built from the catalogue's repository on the forge: not a wait,
|
||||||
|
// since no read of the forge makes one appear (issue 1902); the plan fails with these words.
|
||||||
|
var errNoCatalogueRepository = errors.New("no module of the catalogue names the repository " +
|
||||||
|
catalogue.CatalogueRepository + " on the forge")
|
||||||
|
|
||||||
|
// catalogueRepositoryAmong is the catalogue's repository on the forge, as owner/name, found as catch-up finds the trunks
|
||||||
|
// of the repositories the catalogue builds from: a source in the URL form or the seat form, `.git` dropped (issue 1902).
|
||||||
|
func catalogueRepositoryAmong(entries []inventory.Entry, forms *sourceForms) (string, error) {
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
if e.Source.Seat == gitSeat && path.Base(strings.Trim(e.Source.Repository, "/")) == catalogue.CatalogueRepository {
|
if e.Source.Repository == "" {
|
||||||
repository = strings.Trim(e.Source.Repository, "/")
|
continue
|
||||||
break
|
}
|
||||||
|
owner, name, onForge := forms.onTheForge(e.Source.Repository, e.Source.Seat)
|
||||||
|
if onForge && strings.EqualFold(name, catalogue.CatalogueRepository) {
|
||||||
|
return owner + "/" + name, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if repository == "" {
|
return "", errNoCatalogueRepository
|
||||||
return predefinedHead{}, fmt.Errorf("no module of the catalogue names the repository %s on the forge",
|
}
|
||||||
catalogue.CatalogueRepository)
|
|
||||||
|
// readPredefinedHead is the list at the trunk head of the catalogue's repository, found among the modules' sources and
|
||||||
|
// read from the forge.
|
||||||
|
func readPredefinedHead(ctx context.Context, entries []inventory.Entry, forms *sourceForms) (predefinedHead, error) {
|
||||||
|
// A URL source is on the forge only once the forge's address is known; an address that cannot be read is a wait.
|
||||||
|
if _, err := forms.forgeAddress(ctx, forgeAddressWait); err != nil {
|
||||||
|
return predefinedHead{}, fmt.Errorf("the forge's address cannot be read: %w", err)
|
||||||
|
}
|
||||||
|
repository, err := catalogueRepositoryAmong(entries, forms)
|
||||||
|
if err != nil {
|
||||||
|
return predefinedHead{}, err
|
||||||
}
|
}
|
||||||
var heads []struct {
|
var heads []struct {
|
||||||
SHA string `json:"sha"`
|
SHA string `json:"sha"`
|
||||||
@@ -143,7 +169,7 @@ var predefinedAtTrunk = func(ctx context.Context, inv *inventory.Inventory) (pre
|
|||||||
}
|
}
|
||||||
|
|
||||||
// askGitea asks the forge's module one tool on the bus and reads its result into into.
|
// askGitea asks the forge's module one tool on the bus and reads its result into into.
|
||||||
func askGitea(ctx context.Context, tool string, args map[string]any, into any) error {
|
var askGitea = func(ctx context.Context, tool string, args map[string]any, into any) error {
|
||||||
js, err := aBus()
|
js, err := aBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -183,6 +209,10 @@ type keyTake struct {
|
|||||||
func takeInKey(ctx context.Context, inv *inventory.Inventory, keys keyReads, t keyTake) (
|
func takeInKey(ctx context.Context, inv *inventory.Inventory, keys keyReads, t keyTake) (
|
||||||
catalogue.Manifest, inventory.Build, error) {
|
catalogue.Manifest, inventory.Build, error) {
|
||||||
head, err := predefinedAtTrunk(ctx, inv)
|
head, err := predefinedAtTrunk(ctx, inv)
|
||||||
|
if errors.Is(err, errNoCatalogueRepository) {
|
||||||
|
// Fails the plan, loudly, with these words (issue 1902): waiting would never end.
|
||||||
|
return catalogue.Manifest{}, inventory.Build{}, fmt.Errorf("%s is not judged: %w", t.Module, err)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return catalogue.Manifest{}, inventory.Build{}, fmt.Errorf("%w: the list of predefined keys at the catalogue's "+
|
return catalogue.Manifest{}, inventory.Build{}, fmt.Errorf("%w: the list of predefined keys at the catalogue's "+
|
||||||
"trunk head cannot be read, so %s is not judged: %v", errKeyWaits, t.Module, err)
|
"trunk head cannot be read, so %s is not judged: %v", errKeyWaits, t.Module, err)
|
||||||
|
|||||||
@@ -249,3 +249,23 @@ func TestTheSwitchsListReadsTheHeldBuildsAndTheKeys(t *testing.T) {
|
|||||||
t.Fatalf("A13: the switch's list is %+v", lines)
|
t.Fatalf("A13: the switch's list is %+v", lines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 1902: the take-in with the lookup of the catalogue's repository NOT stubbed - the catalogue's modules are registered
|
||||||
|
// with a URL source and no seat, as every live row is - reads the list from the forge's wire alone and registers the module.
|
||||||
|
func TestATakeInFindsTheCataloguesRepositoryInTheURLForm(t *testing.T) {
|
||||||
|
open := theCatalogue(t)
|
||||||
|
ctx := t.Context()
|
||||||
|
was, wasForms := formsOfSources, askGitea
|
||||||
|
formsOfSources = func(context.Context, *inventory.Inventory) *sourceForms { return aFormsOnTheForge() }
|
||||||
|
t.Cleanup(func() { formsOfSources, askGitea = was, wasForms })
|
||||||
|
aForgeAnsweringTheCatalogue(t)
|
||||||
|
if err := open.inventory.RegisterModule(ctx, catalogue.Manifest{Module: "catalogue-anchor", Version: "1"},
|
||||||
|
inventory.Source{Repository: "http://anchor.internal:20000/novox/mesh-catalog.git", Path: "modules/catalogue-anchor",
|
||||||
|
Ref: "main", BuiltFrom: "c0", Head: "c0"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keys, trunk := aKeyFor("sudo", "6", fpOf('7'), "novox/mesh-catalog", "modules/sudo", "c0ffee0123456789")
|
||||||
|
if _, _, err := takeInKey(ctx, open.inventory, keys, keyTake{Module: "sudo", Trunk: trunk, Ask: registrationAsk{Merge: trunk.Commit}}); err != nil {
|
||||||
|
t.Fatalf("a key was not taken in with the real lookup of the catalogue's repository: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||||
|
"github.com/novox/mesh-controller/internal/inventory"
|
||||||
|
)
|
||||||
|
|
||||||
|
// aForgeAnsweringTheCatalogue stands in for the forge's wire alone (askGitea): the head of the catalogue's trunk and its
|
||||||
|
// predefined.json. It says which repository path was asked.
|
||||||
|
func aForgeAnsweringTheCatalogue(t *testing.T) *[]string {
|
||||||
|
t.Helper()
|
||||||
|
var asked []string
|
||||||
|
was := askGitea
|
||||||
|
askGitea = func(_ context.Context, _ string, args map[string]any, into any) error {
|
||||||
|
p, _ := args["path"].(string)
|
||||||
|
asked = append(asked, p)
|
||||||
|
switch {
|
||||||
|
case strings.Contains(p, "/commits"):
|
||||||
|
return json.Unmarshal([]byte(`[{"sha":"abcdef0123456789","commit":{"committer":{"date":"2026-10-23T12:00:00Z"}}}]`), into)
|
||||||
|
case strings.Contains(p, "/contents/"):
|
||||||
|
body, _ := json.Marshal(map[string]string{"encoding": "base64", "content": base64.StdEncoding.EncodeToString([]byte(`{}`))})
|
||||||
|
return json.Unmarshal(body, into)
|
||||||
|
}
|
||||||
|
return errors.New("asked " + p)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() { askGitea = was })
|
||||||
|
return &asked
|
||||||
|
}
|
||||||
|
|
||||||
|
func aFormsOnTheForge() *sourceForms {
|
||||||
|
return sourceFormsRead(context.Background(), time.Now, func(context.Context) (catalogue.World, error) {
|
||||||
|
return forgeHolding(float64(20000)), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue 1902: the catalogue's repository is found among the modules' sources in the URL form (what every live row has) and
|
||||||
|
// in the seat form, `.git` dropped; the real lookup runs, only the forge's wire is a stand-in.
|
||||||
|
func TestTheCataloguesRepositoryIsFoundInTheURLFormAndTheSeatForm(t *testing.T) {
|
||||||
|
for name, source := range map[string]inventory.Source{
|
||||||
|
"the URL form": {Repository: "http://anchor.internal:20000/novox/mesh-catalog.git", Path: "modules/sudo"},
|
||||||
|
"the seat form": {Repository: "novox/mesh-catalog", Seat: "git", Path: "modules/sudo"},
|
||||||
|
} {
|
||||||
|
asked := aForgeAnsweringTheCatalogue(t)
|
||||||
|
entries := []inventory.Entry{
|
||||||
|
{Manifest: catalogue.Manifest{Module: "other"}, Source: inventory.Source{Repository: "http://anchor.internal:20000/novox/mesh-delivery.git"}},
|
||||||
|
{Manifest: catalogue.Manifest{Module: "sudo"}, Source: source},
|
||||||
|
}
|
||||||
|
head, err := readPredefinedHead(t.Context(), entries, aFormsOnTheForge())
|
||||||
|
if err != nil || head.Repository != "novox/mesh-catalog" || head.Commit != "abcdef0123456789" {
|
||||||
|
t.Fatalf("%s: the head is %+v (%v)", name, head, err)
|
||||||
|
}
|
||||||
|
if len(*asked) != 2 || !strings.HasPrefix((*asked)[0], "/repos/novox/mesh-catalog/commits") {
|
||||||
|
t.Errorf("%s: the forge was asked %v", name, *asked)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And a mesh where nothing is built from the catalogue's repository says so by name, as an error that is not a wait.
|
||||||
|
func TestNoModuleBuiltFromTheCataloguesRepositoryIsNamedNotWaitedFor(t *testing.T) {
|
||||||
|
aForgeAnsweringTheCatalogue(t)
|
||||||
|
entries := []inventory.Entry{{Manifest: catalogue.Manifest{Module: "other"},
|
||||||
|
Source: inventory.Source{Repository: "http://anchor.internal:20000/novox/mesh-delivery.git"}}}
|
||||||
|
_, err := readPredefinedHead(t.Context(), entries, aFormsOnTheForge())
|
||||||
|
if !errors.Is(err, errNoCatalogueRepository) || errors.Is(err, errKeyWaits) {
|
||||||
|
t.Fatalf("a mesh with no catalogue module: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user