Read a module whole while a plan has overtaken its build source, and plan every view alike (hq ADR 0267, review)
A failed build, or a plan closed before reaching a module, left the closure its last good build said, and a fix-forward to a newly imported package would have moved nothing. A missed merge moving only a module that packages the repository was never caught up, and an older merge read as history for it through a look that was not its own. The gate, a pull request's check, the what-if and a delivery's order now read the same view the merge handler does.
This commit is contained in:
@@ -91,6 +91,10 @@ type ReadRepository struct {
|
||||
// Own is the module's own repository, whose Paths narrow what its own directory — or, for a module
|
||||
// built from its repository's root, the whole repository — would otherwise be.
|
||||
Own bool `json:"own,omitempty"`
|
||||
// Built is when the build these were read from was asked, and Looked when the module's newest build of
|
||||
// any outcome was: what the planner judges a build source's age, and a merge's news, by. Never stored.
|
||||
Built time.Time `json:"-"`
|
||||
Looked time.Time `json:"-"`
|
||||
}
|
||||
|
||||
// BuildSource is the build source a build read in one repository (novox/hq ADR 0267): Repository and Ref
|
||||
@@ -338,8 +342,41 @@ func (i *Inventory) BuiltAgainst(ctx context.Context) (map[string][]string, erro
|
||||
// none — off the trunk, from a builder that predates it, or of a source not known — leaves its module read
|
||||
// as before: every file of a context, and its own directory or root.
|
||||
func (i *Inventory) ReadRepositories(ctx context.Context) (map[string][]ReadRepository, error) {
|
||||
// The newest build of each module whatever its outcome: a failed one newer than the newest that
|
||||
// worked leaves that one's build source stale — the merge it was asked for may have changed the closure
|
||||
// (novox/hq ADR 0267), so its module is read whole until a build works again.
|
||||
newest := map[string]struct {
|
||||
at time.Time
|
||||
failed bool
|
||||
}{}
|
||||
tried, err := i.store.Pool().Query(ctx,
|
||||
`select distinct on (module) module, coalesce(asked, at), failed <> ''
|
||||
from build
|
||||
where module is not null and module <> ''
|
||||
order by module, `+newestRequestFirst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for tried.Next() {
|
||||
var module string
|
||||
var at time.Time
|
||||
var failed bool
|
||||
if err := tried.Scan(&module, &at, &failed); err != nil {
|
||||
tried.Close()
|
||||
return nil, err
|
||||
}
|
||||
newest[module] = struct {
|
||||
at time.Time
|
||||
failed bool
|
||||
}{at, failed}
|
||||
}
|
||||
tried.Close()
|
||||
if err := tried.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select distinct on (module) module, built_contexts, build_sources
|
||||
`select distinct on (module) module, built_contexts, build_sources, coalesce(asked, at)
|
||||
from build
|
||||
where module is not null and module <> '' and failed = ''
|
||||
order by module, `+newestRequestFirst)
|
||||
@@ -352,7 +389,8 @@ func (i *Inventory) ReadRepositories(ctx context.Context) (map[string][]ReadRepo
|
||||
for rows.Next() {
|
||||
var module string
|
||||
var raw, rawSources []byte
|
||||
if err := rows.Scan(&module, &raw, &rawSources); err != nil {
|
||||
var built time.Time
|
||||
if err := rows.Scan(&module, &raw, &rawSources, &built); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var of []ReadRepository
|
||||
@@ -367,7 +405,13 @@ func (i *Inventory) ReadRepositories(ctx context.Context) (map[string][]ReadRepo
|
||||
sources = nil
|
||||
}
|
||||
}
|
||||
if n, known := newest[module]; known && n.failed && n.at.After(built) {
|
||||
sources = nil
|
||||
}
|
||||
if of = WithBuildSources(of, sources); len(of) > 0 {
|
||||
for k := range of {
|
||||
of[k].Built, of[k].Looked = built, newest[module].at
|
||||
}
|
||||
read[module] = of
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A build result was answered to whoever asked and kept nowhere, so "when did this last build",
|
||||
@@ -200,6 +201,12 @@ func TestABuildsBuildSourceComesBackOverWhatItRead(t *testing.T) {
|
||||
{Repository: "novox/other"},
|
||||
{Paths: []string{"modules/route-proxy/Dockerfile", "modules/route-proxy/module.json"}, Own: true},
|
||||
}
|
||||
for k := range read["route-proxy"] {
|
||||
if read["route-proxy"][k].Built.IsZero() {
|
||||
t.Errorf("no build time on %+v", read["route-proxy"][k])
|
||||
}
|
||||
read["route-proxy"][k].Built, read["route-proxy"][k].Looked = time.Time{}, time.Time{}
|
||||
}
|
||||
if !reflect.DeepEqual(read["route-proxy"], want) {
|
||||
t.Fatalf("read back %+v\nwanted %+v", read["route-proxy"], want)
|
||||
}
|
||||
@@ -214,3 +221,29 @@ func TestABuildsBuildSourceComesBackOverWhatItRead(t *testing.T) {
|
||||
t.Fatalf("the build source was stored among what the build read: %s", stored)
|
||||
}
|
||||
}
|
||||
|
||||
// A build newer than the newest that worked, and failed, leaves that one's build source stale: its module is
|
||||
// read whole until a build works again (novox/hq ADR 0267).
|
||||
func TestAFailedNewerBuildLeavesTheBuildSourceStale(t *testing.T) {
|
||||
inv := fresh(t)
|
||||
ctx := context.Background()
|
||||
worked := aBuild("worked", "route-proxy", "")
|
||||
worked.Asked = time.Now().Add(-time.Hour)
|
||||
worked.Read = []ReadRepository{{Repository: "novox/mesh-controller", Ref: "main"}}
|
||||
worked.Sources = []BuildSource{{Paths: []string{"modules/route-proxy/module.json"}},
|
||||
{Repository: "novox/mesh-controller", Ref: "main", Paths: []string{"examples/route-proxy/"}}}
|
||||
failed := aBuild("failed", "route-proxy", "compile error")
|
||||
failed.Asked = time.Now()
|
||||
for _, b := range []Build{worked, failed} {
|
||||
if err := inv.RecordBuild(ctx, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
read, err := inv.ReadRepositories(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := read["route-proxy"]; len(got) != 1 || got[0].Paths != nil || got[0].Own || !got[0].Looked.After(got[0].Built) {
|
||||
t.Fatalf("after a newer failed build the proxy reads as %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user