DLV-B9 readers: the indexer and the view pass over build-log entries that are no build's start or end
The builder writes design 51's old-form close into its build log; the indexer's Builds.Put refused it (no repository) and the view's history stopped on it. Both read the entry's event first and skip any other kind, whatever its form. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
@@ -28,9 +29,13 @@ type Builds struct {
|
||||
func NewBuilds() *Builds { return &Builds{records: map[string]delivery.BuildRecord{}} }
|
||||
|
||||
// Put takes one entry of the builder's log. A build's end is an outcome: done registers, failed or queued does not; its
|
||||
// start is a request, which registers nothing either. An entry that does not read, or names no module or repository, is
|
||||
// refused and said.
|
||||
// start is a request, which registers nothing either. An entry of another kind (the old reference form's close, design 51
|
||||
// *The node-engine's own build*) is no build and is passed over, whatever its form. A build's entry that does not read, or
|
||||
// names no module or repository, is refused and said.
|
||||
func (b *Builds) Put(value []byte) error {
|
||||
if !IsBuildEntry(value) {
|
||||
return nil
|
||||
}
|
||||
e, err := delivery.DecodeBuildLogEntry(value)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -179,3 +184,16 @@ func (b *Builds) Registered() []Registration {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// IsBuildEntry says whether an entry of the builder's log is a build's start or end, read from its event alone, so an
|
||||
// entry of another kind is passed over before its form is read. One whose event cannot be read is taken as a build's, so
|
||||
// its decoding says what is wrong with it.
|
||||
func IsBuildEntry(value []byte) bool {
|
||||
var e struct {
|
||||
Event *string `json:"event"`
|
||||
}
|
||||
if json.Unmarshal(value, &e) != nil || e.Event == nil {
|
||||
return true
|
||||
}
|
||||
return *e.Event == delivery.BuildStartedEvent || *e.Event == delivery.BuildEndedEvent
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ import (
|
||||
// Imports that cannot be resolved (a package the forge cannot list) are kept with no ImportForm: the next recompute that
|
||||
// resolves them writes the import form, one rebuild too many, never a stale module.
|
||||
func (ix *Indexer) ApplyBuildEnd(ctx context.Context, value []byte) error {
|
||||
if !IsBuildEntry(value) {
|
||||
return nil
|
||||
}
|
||||
e, err := delivery.DecodeBuildLogEntry(value)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,6 +3,7 @@ package index
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -313,4 +314,17 @@ func TestARecordOfAPullRequestDoesNotReplaceTheRegisteredRef(t *testing.T) { //
|
||||
if got := b.RegistrationsOf("photos"); len(got) != 1 || got[0].Ref != "main" {
|
||||
t.Fatalf("registrations = %+v", got)
|
||||
}
|
||||
// An entry of another kind in the builder's log (the old reference form's close, design 51), of this form or another,
|
||||
// is no build: passed over, never refused.
|
||||
for _, other := range []string{
|
||||
fmt.Sprintf(`{"v":%d,"event":"old-form-closed","module":"node-engine","at":"2026-10-04T09:00:00Z"}`, delivery.Version),
|
||||
`{"v":99,"event":"old-form-closed","at":"2026-10-04T09:00:00Z","nodes":["ace"]}`,
|
||||
} {
|
||||
if err := b.Put([]byte(other)); err != nil {
|
||||
t.Errorf("an entry of another kind is refused: %v", err)
|
||||
}
|
||||
}
|
||||
if got := b.RegistrationsOf("photos"); len(got) != 1 || got[0].Ref != "main" {
|
||||
t.Fatalf("after entries of another kind, registrations = %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,9 @@ func TestBuilderBuildsListsKeysAsStored(t *testing.T) {
|
||||
r.BuildLog = func(at time.Time, keys string, limit int) (*stdio.LogPage, error) {
|
||||
asked = keys
|
||||
return &stdio.LogPage{Entries: []stdio.LogEntry{{Sequence: 1, Value: entry(delivery.BuildStartedEvent, 0)},
|
||||
{Sequence: 2, Value: entry(delivery.BuildEndedEvent, 90*time.Second)}, {Sequence: 3, Value: entry(delivery.BuildStartedEvent, time.Hour)}}}, nil
|
||||
{Sequence: 2, Value: entry(delivery.BuildEndedEvent, 90*time.Second)}, {Sequence: 3, Value: entry(delivery.BuildStartedEvent, time.Hour)},
|
||||
// an entry of another kind (the old reference form's close, design 51), of a form no build has: passed over
|
||||
{Sequence: 4, Value: []byte(`{"v":99,"event":"old-form-closed","at":"2026-10-29T23:00:00Z","nodes":["ace"]}`)}}}, nil
|
||||
}
|
||||
out, err = run(t, r, view.BuildHistoryTool, map[string]any{"module": "netcheck", "since": t0.Format(time.RFC3339)})
|
||||
if h, _ := out.(view.HistoryAnswer); err != nil || len(h.Builds) != 1 || h.Builds[0].Seconds != 90 || h.Builds[0].Outcome != delivery.BuildDone || asked != "netcheck" || h.More {
|
||||
|
||||
@@ -870,8 +870,9 @@ type HistoryAnswer struct {
|
||||
}
|
||||
|
||||
// BuildHistory is every build of module (every module when empty) that ended at or after since, oldest first, each with its
|
||||
// duration from the start the log holds of the same module and fingerprint before it (delivery.BuildDuration). An entry that
|
||||
// does not read is refused, naming its sequence.
|
||||
// duration from the start the log holds of the same module and fingerprint before it (delivery.BuildDuration). An entry of
|
||||
// another kind than a build's start or end is passed over, whatever its form; a build's entry that does not read is
|
||||
// refused, naming its sequence.
|
||||
func (r Reader) BuildHistory(module string, since time.Time) (HistoryAnswer, error) {
|
||||
out := HistoryAnswer{Since: since.UTC(), Builds: []HistoryRow{}}
|
||||
if r.BuildLog == nil {
|
||||
@@ -894,6 +895,9 @@ func (r Reader) BuildHistory(module string, since time.Time) (HistoryAnswer, err
|
||||
}
|
||||
var entries []delivery.BuildLogEntry
|
||||
for _, le := range page.Entries {
|
||||
if !isBuildEntry(le.Value) {
|
||||
continue // an entry of another kind (the old reference form's close, design 51) is no build
|
||||
}
|
||||
e, err := delivery.DecodeBuildLogEntry(le.Value)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("the builder's log %s, entry %d: %w", delivery.BuildLogRead, le.Sequence, err)
|
||||
@@ -923,3 +927,16 @@ func (r Reader) BuildHistory(module string, since time.Time) (HistoryAnswer, err
|
||||
out.More = page.Next != 0
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// isBuildEntry says whether an entry of the builder's log is a build's start or end, read from its event alone, so an entry
|
||||
// of another kind is passed over before its form is read. One whose event cannot be read is taken as a build's, so its
|
||||
// decoding says what is wrong with it.
|
||||
func isBuildEntry(value []byte) bool {
|
||||
var e struct {
|
||||
Event *string `json:"event"`
|
||||
}
|
||||
if json.Unmarshal(value, &e) != nil || e.Event == nil {
|
||||
return true
|
||||
}
|
||||
return *e.Event == delivery.BuildStartedEvent || *e.Event == delivery.BuildEndedEvent
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user