diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go index d826ebee..8809f9fc 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/judge.go @@ -45,7 +45,9 @@ type judgeDeps struct { nodes *nodeNames // predefined is the indexer's kept list of predefined keys; nil reads none. predefined check.PredefinedList - logf func(format string, args ...any) + // tags says a group's tag-wanted and hears its packages' published (design 51 *Tagged versions*); nil asks none. + tags check.TagAsks + logf func(format string, args ...any) } // keptPredefined reads the indexer's key predefined.list (delivery.PredefinedKept), bound once its state appears. @@ -157,7 +159,7 @@ func newJudge(d judgeDeps) check.Judge { nodes = d.nodes } ex := &check.Examiner{Nodes: nodes, Compositions: d.compositions, Asks: d.asks, Toolchains: d.tools, - Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Packages: d.repos, Published: d.published, Forge: d.forge, Runs: d.repos, Builds: d.builds, BuildEnds: d.ends, BuildWait: d.buildWait, Announce: d.announce, Trunks: d.trunks, Manifests: d.repos, Read: d.repos, Predefined: d.predefined, Logf: d.logf} + Kept: check.Kept{Store: d.kept}, Previews: d.previews, Modules: d.repos, Packages: d.repos, Published: d.published, Forge: d.forge, Runs: d.repos, Builds: d.builds, BuildEnds: d.ends, BuildWait: d.buildWait, Announce: d.announce, Trunks: d.trunks, Manifests: d.repos, Read: d.repos, Predefined: d.predefined, Tags: d.tags, Logf: d.logf} if d.nodes != nil && d.builds != nil && d.repos != nil { ex.Serving = runningServes{have: d.nodes, builds: d.builds, read: d.repos, logf: d.logf} } diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go index 1cc2b977..c42e6923 100644 --- a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/main.go @@ -214,11 +214,30 @@ func run(ctx context.Context, getenv func(string) string, logOut io.Writer) erro }() }) ch.Runs = repos // the node-engine a check reads beside at its running commit (ADR 0548 §6) + // A group's tagged versions (design 51 *Tagged versions*): tag-wanted on the checker's stream asks, and the package + // registry seats' published heard on the checker's durable consumer of the shared events stream. Where that consumer is + // not there yet a group's check asks and waits in vain until its bound, said in its verdict. + tags := tagAsks{js: js, node: cred.Node, heard: &check.PublishedHeard{}, logf: logger.Printf, now: time.Now} + switch cons, err := cred.EventConsumer(ctx, js, process.EventsStream); { + case err != nil: + logger.Printf("the consumer %s_%s on %s does not exist yet, so no package registry's published is heard and a group's check fails at its publish bound until the checker starts again: %v", + cred.Node, cred.Module, process.EventsStream, err) + default: + cc, err := process.HearEvents(cons, func(m jetstream.Msg) { + if !tags.heardPublished(m) { + _ = m.Ack() // nothing else the checker consumes rides the shared stream + } + }, logger.Printf) + if err != nil { + return err + } + defer cc.Stop() + } ch.Judge = newJudge(judgeDeps{ asks: buildrun.Asker{Publisher: asks, Waiter: waiter, Bound: stepBound}, tools: tools, trunks: trunks, kept: kvStore{buckets[delivery.CheckerKeptBucket]}, previews: kvStore{buckets[delivery.PreviewsBucket]}, repos: repos, published: published, builds: builds, ends: ends, buildWait: set.buildWait, forge: forgeReads{call: caller.Call}, logf: logger.Printf, - announce: announce, compositions: compositions, + announce: announce, compositions: compositions, tags: tags, nodes: openNodeNames(ctx, js, logger.Printf), predefined: keptPredefined{process.AwaitBucket(ctx, js, conn, delivery.IndexerModule+"_"+delivery.IndexerPredefinedState, "a change's manifests are judged with no predefined keys, each fallback as its value declares it", logger.Printf)}, diff --git a/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/tags.go b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/tags.go new file mode 100644 index 00000000..3562d9fc --- /dev/null +++ b/modules/mesh-delivery-checker/cmd/mesh-delivery-checker/tags.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/nats-io/nats.go" + "github.com/nats-io/nats.go/jetstream" + + "git.novox.be/novox/mesh-contracts/go/seats/gopackageregistry" + "git.novox.be/novox/mesh-contracts/go/seats/npmpackageregistry" + "git.novox.be/novox/mesh-sdk/go/delivery" + + "mesh-delivery-checker/internal/check" +) + +// publishedSeats are the two package registry seats' published, by subject (their contracts in mesh-contracts): heard on +// the checker's durable consumer of the shared events stream, made from the manifest's consumes. +var publishedSeats = map[string]string{ + gopackageregistry.SubjectPublished: gopackageregistry.Seat, + npmpackageregistry.SubjectPublished: npmpackageregistry.Seat, +} + +// tagAsks is check.TagAsks on the bus: tag-wanted published on the checker's own stream asks, and the published heard. +type tagAsks struct { + js jetstream.JetStream + node string + heard *check.PublishedHeard + logf func(string, ...any) + now func() time.Time + nextID func() string +} + +// Want publishes one tag-wanted with the checker's event headers; the message id makes a second publish of the same +// member's head and package within the stream's duplicate window one event. +func (t tagAsks) Want(ctx context.Context, w check.TagWanted) error { + body, err := json.Marshal(w) + if err != nil { + return err + } + msg := nats.NewMsg(eventSubject(check.EventTagWanted)) + id := fmt.Sprintf("tag-wanted.%s.%s.%s.%s", w.Repository, w.Head, w.Module, w.Package) + msg.Header.Set("x-event-id", id) + msg.Header.Set("x-source", delivery.CheckerModule) + msg.Header.Set("x-node", t.node) + msg.Header.Set("x-time", t.now().UTC().Format(time.RFC3339Nano)) + msg.Header.Set("content-type", "application/json") + msg.Data = body + _, err = t.js.PublishMsg(ctx, msg, jetstream.WithMsgID(id)) + return err +} + +func (t tagAsks) Await(ctx context.Context, seat, head string) (check.TaggedPublished, bool) { + return t.heard.Await(ctx, seat, head) +} + +// heardPublished takes a message of the events consumer in when it is a package registry seat's published, and answers +// whether it was: a tagged version wakes the checks waiting on it; any other published is passed over. +func (t tagAsks) heardPublished(m jetstream.Msg) bool { + seat, ok := publishedSeats[m.Subject()] + if !ok { + return false + } + var p check.TaggedPublished + if err := json.Unmarshal(m.Data(), &p); err != nil { + t.logf("%s holds what is no published of %s, left: %v", m.Subject(), seat, err) + } else if t.heard.Heard(seat, p) { + t.logf("%s published %s %s from %s (tag %s)", seat, p.Name, p.Version, p.Commit, p.Tag) + } + if err := m.Ack(); err != nil { + t.logf("%s cannot be acknowledged, so the bus gives it again: %v", m.Subject(), err) + } + return true +} diff --git a/modules/mesh-delivery-checker/go.mod b/modules/mesh-delivery-checker/go.mod index bf775228..3158ab69 100644 --- a/modules/mesh-delivery-checker/go.mod +++ b/modules/mesh-delivery-checker/go.mod @@ -11,6 +11,8 @@ require ( require git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep v0.0.0-20261102193506-d221c633799a require ( + git.novox.be/novox/mesh-contracts/go/seats/gopackageregistry v0.0.0-00010101000000-000000000000 + git.novox.be/novox/mesh-contracts/go/seats/npmpackageregistry v0.0.0-00010101000000-000000000000 git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/buildrun v0.0.0-00010101000000-000000000000 github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op // indirect github.com/google/go-tpm v0.9.8 // indirect diff --git a/modules/mesh-delivery-checker/internal/check/judge.go b/modules/mesh-delivery-checker/internal/check/judge.go index 3480dca7..c253d199 100644 --- a/modules/mesh-delivery-checker/internal/check/judge.go +++ b/modules/mesh-delivery-checker/internal/check/judge.go @@ -109,6 +109,10 @@ type Examiner struct { BuildWait time.Duration // Announce says a preview was written under the key (EventPreviewWritten); nil says nothing. Announce Announcer + // Tags says a group's tag-wanted and hears its packages' published (design 51 *Tagged versions*); nil asks none. + Tags TagAsks + // PublishBound is how long a group's check waits for each package's published; DefaultPublishBound when 0. + PublishBound time.Duration // Packages names, of a repository, the paths (repository-relative) its TypeScript package artifacts are built from, from // the indexer's modules (delivery.Module.ArtifactPaths, lead decision L8); nil serves no group package. Packages interface { @@ -388,6 +392,11 @@ func (st *state) judgeAll(ctx context.Context) *delivery.Verdict { } return 0 }) + // A group's members are built only after each package they declare is published as the group's tagged version + // (design 51 *Tagged versions*): a package not published within the bound fails the check before any member is built. + if v := st.tagged(ctx); v != nil { + return v + } var groupPackage map[string]any var failures []*delivery.Verdict for _, p := range pulls { diff --git a/modules/mesh-delivery-checker/internal/check/pins_test.go b/modules/mesh-delivery-checker/internal/check/pins_test.go index 81a7616f..a4de20ef 100644 --- a/modules/mesh-delivery-checker/internal/check/pins_test.go +++ b/modules/mesh-delivery-checker/internal/check/pins_test.go @@ -1,12 +1,16 @@ package check import ( + "context" "maps" "slices" "strings" + "sync" "testing" + "time" "git.novox.be/novox/mesh-sdk/go/buildstep" + "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" ) @@ -60,6 +64,84 @@ func TestAGroupMemberIsBuiltAgainstTheSDKMembersHeadWhateverItsGoModNames(t *tes if got := packagesOfFetch(t, b, "novox/mesh-catalog"); len(got) != 2 || got[SDKModulePath] != trunkS || got[contract] != trunkA { t.Errorf("a pull request alone is built against %v, want every package at its latest published version", got) } + + // Design 51 *Tagged versions* (DLV-C3): a group's check says tag-wanted for each package a member declares and builds no + // member before its published; a lone pull request asks nothing; no published within the bound fails naming the package. + sdk := catalogue.Manifest{Module: "mesh-sdk", Build: &catalogue.Build{Artifacts: []catalogue.Artifact{ + {Name: "go", Kind: catalogue.ArtifactPackage, Language: "go"}, {Name: "lib", Kind: catalogue.ArtifactPackage, Language: "typescript"}}}} + digest := strings.Repeat("c", 64) + sdkPull := func() delivery.CandidatePull { + p := pullOf(repoSDK, 12, head2) + p.Modules = []string{"mesh-sdk"} + p.Fps = map[string]delivery.CandidateModule{"mesh-sdk": {FP2: "src2:" + strings.Repeat("f", 64), Manifest: digest}} + return p + } + for _, row := range []struct { + name string + pulls []delivery.CandidatePull + published []string // the seats that publish the SDK member's head + wants int + fails string + }{ + {name: "a group", pulls: []delivery.CandidatePull{pullOf(repoA, 7, head1), sdkPull()}, + published: []string{"go-package-registry", "npm-package-registry"}, wants: 2}, + {name: "a lone pull request", pulls: []delivery.CandidatePull{sdkPull()}}, + {name: "a package never published", pulls: []delivery.CandidatePull{pullOf(repoA, 7, head1), sdkPull()}, + published: []string{"go-package-registry"}, wants: 2, + fails: "no published of the tagged version of lib of mesh-sdk (novox/mesh-sdk#12) on npm-package-registry within 50ms"}, + } { + b := newBench(t) + b.fetch.Modules = []string{"modules/m"} + b.ex.Read = fixedManifests{digest: sdk} + tags := &recordedTags{heard: &PublishedHeard{}, publish: row.published} + b.ex.Tags, b.ex.PublishBound = tags, 50*time.Millisecond + b.asks.Answer = func(a delivery.StepAsk) delivery.Step { + if len(tags.wanted) != row.wants { + t.Errorf("%s: a member was asked before every tag-wanted was said: %d of %d", row.name, len(tags.wanted), row.wants) + } + return b.answer(a) + } + v := b.judge(t, caseOf(t, row.pulls...)).Verdict + if len(tags.wanted) != row.wants { + t.Errorf("%s: tag-wanted said %d times, want %d: %+v", row.name, len(tags.wanted), row.wants, tags.wanted) + } + for _, w := range tags.wanted { + if w.Repository != repoSDK || w.Branch != "feat/x" || w.Head != head2 || w.Module != "mesh-sdk" || w.Pull != 12 || w.Tag != "" { + t.Errorf("%s: tag-wanted = %+v", row.name, w) + } + } + switch { + case row.fails == "" && v.State != delivery.VerdictPass: + t.Errorf("%s: verdict = %+v", row.name, v) + case row.fails != "" && (v.State != delivery.VerdictFail || !strings.Contains(v.Failed.Words, row.fails)): + t.Errorf("%s: verdict = %+v, want it to fail saying %q", row.name, v, row.fails) + case row.fails != "" && len(b.steps.tool("fetch")) != 0: + t.Errorf("%s: a member was built though a package was not published", row.name) + } + } +} + +// recordedTags are the checker's tag asks as a test hears them: each tag-wanted kept, and the seats in publish answering it +// at once with a tagged version, as the builder and a registry seat would. +type recordedTags struct { + heard *PublishedHeard + publish []string + mu sync.Mutex + wanted []TagWanted +} + +func (r *recordedTags) Want(_ context.Context, w TagWanted) error { + r.mu.Lock() + r.wanted = append(r.wanted, w) + r.mu.Unlock() + for _, seat := range r.publish { + r.heard.Heard(seat, TaggedPublished{Name: w.Package, Kind: "package", Commit: w.Head, Tag: "feat-x", Version: "0.1.15-feat-x.1"}) + } + return nil +} + +func (r *recordedTags) Await(ctx context.Context, seat, head string) (TaggedPublished, bool) { + return r.heard.Await(ctx, seat, head) } type runsAt map[string]string diff --git a/modules/mesh-delivery-checker/internal/check/tagwanted.go b/modules/mesh-delivery-checker/internal/check/tagwanted.go new file mode 100644 index 00000000..40f76a6d --- /dev/null +++ b/modules/mesh-delivery-checker/internal/check/tagwanted.go @@ -0,0 +1,214 @@ +package check + +import ( + "context" + "fmt" + "slices" + "strings" + "sync" + "time" + + "git.novox.be/novox/mesh-sdk/go/catalogue" + "git.novox.be/novox/mesh-sdk/go/delivery" +) + +// EventTagWanted is the checker's event that asks the builder for a group member's tagged versions (design 51 *Tagged +// versions*, ADR 0610 §1), on the checker's own family asks (its stream asks, kept seven days), apart from the stream +// checks, so a waiter on a verdict does not wake on it. +const EventTagWanted = "tag-wanted" + +// AsksStream is the checker's stream that carries tag-wanted and verdict, in its old streams section (ADR 0619): the +// bus names it ASKS. +const AsksStream = "asks" + +// DefaultPublishBound is how long a group's check waits for the published of each package its members declare before it +// fails naming the package (design 51 *Tagged versions*: the publish's bound, 10 minutes). +const DefaultPublishBound = 10 * time.Minute + +// TagWanted is the body of tag-wanted: the seat definition of mesh-delivery-checker, its holder event tag-wanted, with +// the branch the builder keeps the ask under (the lead's ruling of 2026-11-04 12:5x). The tag is left to the builder, +// which makes it from the branch, so one part alone names a branch's tag. +type TagWanted struct { + Repository string `json:"repository"` + Branch string `json:"branch"` + Head string `json:"head"` + Tag string `json:"tag,omitempty"` + Module string `json:"module"` + Package string `json:"package"` + Pull int64 `json:"pull,omitempty"` +} + +// PackageSeats are the registry seats a package of each language is published through (design 51 *The seats*). +var PackageSeats = map[string]string{"go": "go-package-registry", "typescript": "npm-package-registry"} + +// TaggedPublished is what a registry seat's published says of a tagged version, as the checker reads it. +type TaggedPublished struct { + Name string `json:"name"` + Kind string `json:"kind"` + Commit string `json:"commit,omitempty"` + Tag string `json:"tag,omitempty"` + Version string `json:"version,omitempty"` +} + +// TagAsks says tag-wanted and hears the registry seats' published: nil asks nothing, and a group's check builds its +// members as it did before tagged versions. +type TagAsks interface { + // Want says one tag-wanted on the checker's family asks. + Want(ctx context.Context, t TagWanted) error + // Await answers the tagged version a seat published from a head, once heard; false when ctx ends first. + Await(ctx context.Context, seat, head string) (TaggedPublished, bool) +} + +// PublishedHeard is the registry seats' published of tagged versions as the checker heard them on its durable consumer +// of the shared events stream, by seat and source commit: a waiter wakes on the event, never on a timer. +type PublishedHeard struct { + mu sync.Mutex + heard map[string]TaggedPublished + waiters map[string][]chan TaggedPublished +} + +func publishedKey(seat, head string) string { return seat + " " + head } + +// Heard takes one published of a seat in; one of no tagged version (no tag, or no commit) is left out and answers false. +func (h *PublishedHeard) Heard(seat string, p TaggedPublished) bool { + if p.Tag == "" || p.Commit == "" { + return false + } + h.mu.Lock() + defer h.mu.Unlock() + if h.heard == nil { + h.heard, h.waiters = map[string]TaggedPublished{}, map[string][]chan TaggedPublished{} + } + k := publishedKey(seat, p.Commit) + h.heard[k] = p + for _, w := range h.waiters[k] { + w <- p + } + delete(h.waiters, k) + return true +} + +// Await is TagAsks.Await over what was heard. +func (h *PublishedHeard) Await(ctx context.Context, seat, head string) (TaggedPublished, bool) { + k := publishedKey(seat, head) + h.mu.Lock() + if h.heard == nil { + h.heard, h.waiters = map[string]TaggedPublished{}, map[string][]chan TaggedPublished{} + } + if p, ok := h.heard[k]; ok { + h.mu.Unlock() + return p, true + } + w := make(chan TaggedPublished, 1) + h.waiters[k] = append(h.waiters[k], w) + h.mu.Unlock() + select { + case p := <-w: + return p, true + case <-ctx.Done(): + h.mu.Lock() + h.waiters[k] = slices.DeleteFunc(h.waiters[k], func(c chan TaggedPublished) bool { return c == w }) + h.mu.Unlock() + return TaggedPublished{}, false + } +} + +// declaredPackage is one package a group member declares: the module, the artifact's name and the seat it is published +// through. +type declaredPackage struct { + pull Pull + module, name string + language, seat string +} + +// declaredPackages are the packages the case's members declare, read from each touched module's manifest at the merge +// preview (the candidate's), else at the trunk (the indexer's manifests): a member that declares none is not asked for. +func (st *state) declaredPackages(ctx context.Context) ([]declaredPackage, error) { + if st.j.Read == nil { + return nil, nil + } + var trunk map[string]string + if st.j.Manifests != nil { + trunk = st.j.Manifests.ManifestDigests() + } + var out []declaredPackage + for _, p := range st.c.Pulls { + for _, module := range p.Modules { + digest := p.Fps[module].Manifest + if digest == "" { + digest = trunk[module] + } + if digest == "" { + continue + } + m, err := st.j.Read.Manifest(ctx, digest) + if err != nil { + return nil, fmt.Errorf("the manifest of %s (%s) cannot be read, so the packages it declares are not known: %w", module, pullKey(p), err) + } + if m.Build == nil { + continue + } + for _, a := range m.Build.Artifacts { + if a.Kind != catalogue.ArtifactPackage { + continue + } + seat, ok := PackageSeats[a.Language] + if !ok { + return nil, fmt.Errorf("the package %s of %s (%s) is in %q, which no registry seat publishes", a.Name, module, pullKey(p), a.Language) + } + out = append(out, declaredPackage{pull: p, module: module, name: a.Name, language: a.Language, seat: seat}) + } + } + } + return out, nil +} + +// tagged asks a group's tagged versions before any member is built (design 51 *Tagged versions*): one tag-wanted per +// package a member declares, then each package's published awaited within the publish bound. A lone pull request and a +// try ask nothing. A package not published within the bound fails the check naming it. +func (st *state) tagged(ctx context.Context) *delivery.Verdict { + if st.j.Tags == nil || st.c.Try != nil || len(st.c.Pulls) < 2 { + return nil + } + decl, err := st.declaredPackages(ctx) + if err != nil { + return fail(delivery.CheckerModule, err.Error()) + } + if len(decl) == 0 { + return nil + } + st.c.Progress.Set("tagged versions", "") + var names []string + for _, d := range decl { + t := TagWanted{Repository: d.pull.Repository, Branch: st.c.Group, Head: d.pull.Head, Module: d.module, Package: d.name, Pull: int64(d.pull.Number)} + if err := st.j.Tags.Want(ctx, t); err != nil { + return fail(delivery.CheckerModule, fmt.Sprintf("the tagged version of %s of %s (%s) cannot be asked: %v", d.name, d.module, pullKey(d.pull), err)) + } + names = append(names, d.module+"/"+d.name) + } + bound := st.j.PublishBound + if bound <= 0 { + bound = DefaultPublishBound + } + waits := fmt.Sprintf("waits on the tagged versions of %s before its members are built, at most %s (design 51 *Tagged versions*)", strings.Join(names, ", "), bound) + st.c.note(waits) + st.j.logf("%s %s", st.c.Key, waits) + at, cancel := context.WithTimeout(ctx, bound) + defer cancel() + var late []string + for _, d := range decl { + p, ok := st.j.Tags.Await(at, d.seat, d.pull.Head) + if !ok { + late = append(late, fmt.Sprintf("%s of %s (%s) on %s", d.name, d.module, pullKey(d.pull), d.seat)) + continue + } + st.c.note(fmt.Sprintf("%s of %s is built as %s %s (tag %s)", d.name, d.module, p.Name, p.Version, p.Tag)) + } + if len(late) > 0 { + if ctx.Err() != nil { + return fail(delivery.CheckerModule, ctx.Err().Error()) + } + return fail(delivery.CheckerModule, fmt.Sprintf("no published of the tagged version of %s within %s, so no member is built (design 51 *Tagged versions*)", strings.Join(late, "; "), bound)) + } + return nil +} diff --git a/modules/mesh-delivery-checker/manifest_test.go b/modules/mesh-delivery-checker/manifest_test.go index 6a379ebe..c70b5bc6 100644 --- a/modules/mesh-delivery-checker/manifest_test.go +++ b/modules/mesh-delivery-checker/manifest_test.go @@ -33,8 +33,9 @@ type artifact struct { } type stream struct { - Name string `json:"name"` - Events []string `json:"events"` + Name string `json:"name"` + Events []string `json:"events"` + MaxAgeDays int `json:"max-age-days,omitempty"` } type manifest struct { @@ -121,7 +122,8 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 t.Errorf("uses = %v, want the Go and Node build seats", m.Uses) } // The checker is woken by a build module's report of a toolchain, as the indexer is: the bucket may not exist when it starts. - if want := []string{"mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported"}; !reflect.DeepEqual(sorted(m.Consumes), want) { + // And a group's check by the package registry seats' published of its tagged versions (design 51 *Tagged versions*). + if want := []string{"go-package-registry.published", "mesh-build-go.toolchain.reported", "mesh-build-node.toolchain.reported", "npm-package-registry.published"}; !reflect.DeepEqual(sorted(m.Consumes), want) { t.Errorf("consumes = %v, want %v", m.Consumes, want) } if b, ok := m.OwnSecrets["broker"]; !ok || len(m.OwnSecrets) != 1 || b != "${dir:state}/broker" { @@ -131,7 +133,9 @@ func TestManifestDeclarations(t *testing.T) { // 1678/AC1 t.Errorf("secrets-owner = %q", m.SecretsOwner) } // The checker says each time it wrote a preview or a status, so the indexer's and the forge bridge's watches find the buckets (ADR 0484 clauses 2 and 3). - if want := []stream{{Name: "checks", Events: []string{"preview.**", "owed.**"}}}; !reflect.DeepEqual(m.Streams, want) { + // Its family asks (design 51 *Tagged versions*, design 26): tag-wanted and verdict, seven days, apart from checks so a + // waiter on a verdict does not wake on a tag ask; in the old streams section (ADR 0619). + if want := []stream{{Name: "checks", Events: []string{"preview.**", "owed.**"}}, {Name: "asks", Events: []string{"verdict", "tag-wanted"}, MaxAgeDays: 7}}; !reflect.DeepEqual(m.Streams, want) { t.Errorf("streams = %+v, want %+v", m.Streams, want) } // The read tools (issue 1686) are served by the tools bundle, not by the checker's process. diff --git a/modules/mesh-delivery-checker/module.json b/modules/mesh-delivery-checker/module.json index 537551f1..8c054afd 100644 --- a/modules/mesh-delivery-checker/module.json +++ b/modules/mesh-delivery-checker/module.json @@ -14,11 +14,14 @@ } ], "consumes": [ + "go-package-registry.published", "mesh-build-go.toolchain.reported", - "mesh-build-node.toolchain.reported" + "mesh-build-node.toolchain.reported", + "npm-package-registry.published" ], "streams": [ - { "name": "checks", "events": ["preview.**", "owed.**"] } + { "name": "checks", "events": ["preview.**", "owed.**"] }, + { "name": "asks", "events": ["verdict", "tag-wanted"], "max-age-days": 7 } ], "state": [ { "name": "asks", "history": 1 },