B1g-5: the catalog's revision verb answers a candidate's group alone and a branch's source revision
Builds: design 47 G A-205, A-222
This commit is contained in:
@@ -63,4 +63,36 @@ func TestACatalogRevisionIsKeptPerWatchedBranch(t *testing.T) {
|
||||
if a := answer("beta", 0); !a.NoneYet {
|
||||
t.Errorf("a branch with no revision answered %+v, want none-yet", a)
|
||||
}
|
||||
// A-205: `source` answers the branch's revision taken in from that source revision; a candidate's group alone answers its
|
||||
// newest revision, whatever its heads.
|
||||
bySource := func(branch string, source int64) (meshcatalog.RevisionAnswer, error) {
|
||||
return Revision(ctx, ix.Revisions, "indexer", "reader", meshcatalog.RevisionInput{Branch: branch, Source: source})
|
||||
}
|
||||
if a, err := bySource("main", 5); err != nil || a.Revision.Revision != 1 || a.Entries[0].Commit != "c1" {
|
||||
t.Errorf("main's source 5 answered %+v (%v)", a.Revision, err)
|
||||
}
|
||||
if a, err := bySource("feature/x=2ey", 7); err != nil || a.Revision.Revision != 1 || a.Entries[0].Commit != "c2" {
|
||||
t.Errorf("the branch's source 7 answered %+v (%v), want its revision 1 at c2", a.Revision, err)
|
||||
}
|
||||
if a, err := bySource("feature/x=2ey", 8); err != nil || a.Revision.Revision != 2 {
|
||||
t.Errorf("the branch's source 8 answered %+v (%v)", a.Revision, err)
|
||||
}
|
||||
if _, err := bySource("feature/x=2ey", 9); err == nil {
|
||||
t.Error("a source no revision was taken in from was answered")
|
||||
}
|
||||
for i, heads := range []string{"h1", "h2"} {
|
||||
rev, objects, err := CatalogRevisionOf(meshcatalog.CatalogRevision{Revision: int64(3 + i), Kind: "candidate", Group: "run-ab", Heads: heads}, tip("c9"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := KeepRevision(ctx, ix.Revisions, rev, objects); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if a, err := Revision(ctx, ix.Revisions, "indexer", "reader", meshcatalog.RevisionInput{Group: "run-ab"}); err != nil || a.Revision.Revision != 4 || a.Revision.Heads != "h2" || a.Entries[0].Commit != "c9" {
|
||||
t.Errorf("a group alone answered %+v (%v), want its newest revision 4", a.Revision, err)
|
||||
}
|
||||
if _, err := Revision(ctx, ix.Revisions, "indexer", "reader", meshcatalog.RevisionInput{Group: "run-none"}); err == nil {
|
||||
t.Error("a candidate never kept was answered")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +71,21 @@ func Revision(ctx context.Context, o Objects, holder, caller string, in meshcata
|
||||
}
|
||||
name := RevisionObject(newest)
|
||||
switch {
|
||||
case in.Group != "" && in.Heads == "":
|
||||
// A candidate's group alone answers its newest revision, whatever its heads (A-205).
|
||||
if name, err = newestCandidate(ctx, o, in.Group); err != nil {
|
||||
return out, err
|
||||
}
|
||||
case in.Group != "" || in.Heads != "":
|
||||
if in.Group == "" || in.Heads == "" {
|
||||
return out, fmt.Errorf("a candidate's revision is named by its group and its heads, both")
|
||||
}
|
||||
name = RevisionObject(meshcatalog.CatalogRevision{Kind: "candidate", Group: in.Group, Heads: in.Heads})
|
||||
case in.Source != 0:
|
||||
// The branch's revision taken in from that source revision (A-205).
|
||||
if name, err = revisionOfSource(ctx, o, branch, in.Source); err != nil {
|
||||
return out, err
|
||||
}
|
||||
case in.Revision != 0 && in.Revision != newest.Revision:
|
||||
name = RevisionObject(meshcatalog.CatalogRevision{Branch: branch, Revision: in.Revision})
|
||||
}
|
||||
@@ -121,3 +131,62 @@ func RevisionTool(o Objects, holder string) stdio.Tool {
|
||||
return Revision(context.Background(), o, holder, stdio.CallerOf(args), in)
|
||||
}}
|
||||
}
|
||||
|
||||
// newestCandidate is the object of the newest revision a candidate group keeps, over whatever heads it was kept at (A-205).
|
||||
func newestCandidate(ctx context.Context, o Objects, group string) (string, error) {
|
||||
names, err := o.Names(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the candidate revisions cannot be listed: %w", err)
|
||||
}
|
||||
best, newest := "", int64(-1)
|
||||
for _, n := range names {
|
||||
if !strings.HasPrefix(n, "candidate."+group+".") {
|
||||
continue
|
||||
}
|
||||
rev, _, err := ReadRevision(ctx, o, n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rev.Group == group && rev.Revision > newest {
|
||||
best, newest = n, rev.Revision
|
||||
}
|
||||
}
|
||||
if best == "" {
|
||||
return "", fmt.Errorf("the catalog keeps no candidate of the group %s", group)
|
||||
}
|
||||
return best, nil
|
||||
}
|
||||
|
||||
// revisionOfSource is the object of the newest revision of branch taken in from the source revision source (A-205); a source
|
||||
// none was taken in from is an error.
|
||||
func revisionOfSource(ctx context.Context, o Objects, branch string, source int64) (string, error) {
|
||||
names, err := o.Names(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("the catalog graph revisions cannot be listed: %w", err)
|
||||
}
|
||||
number := TrunkRevisionOf
|
||||
if !isTrunkBranch(branch) {
|
||||
number = func(n string) (int64, bool) { return BranchRevisionOf(branch, n) }
|
||||
}
|
||||
var found []int64
|
||||
for _, n := range names {
|
||||
if r, ok := number(n); ok {
|
||||
found = append(found, r)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(found, func(a, b int64) int { return int(b - a) })
|
||||
for _, r := range found {
|
||||
name := RevisionObject(meshcatalog.CatalogRevision{Branch: branch, Revision: r})
|
||||
rev, _, err := ReadRevision(ctx, o, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rev.Source == source {
|
||||
return name, nil
|
||||
}
|
||||
if rev.Source < source {
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no revision of %q was taken in from the source revision %d", branch, source)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user