A seat that cannot remove a tag alone (another tag names its digest) now answers and says kept. The builder ends that remove there, with no retry, and marks the version kept on its store with the reason and the other tags; builder_versions shows it, and a later branch end asks it no more. The four seats' kept are consumed. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
96 lines
3.0 KiB
Go
96 lines
3.0 KiB
Go
package versions
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
// The registry seats' events the builder hears on its durable consumer of each `<seat>.events` (design 51 *How a
|
|
// publish ends*, DLV-B4).
|
|
const (
|
|
HeardPublished = "published"
|
|
HeardRefused = "refused"
|
|
HeardRemoved = "removed"
|
|
HeardKept = "kept"
|
|
)
|
|
|
|
// Outcome is the body of a registry seat's published, refused and removed (mesh-contracts parts/registry Outcome), the
|
|
// fields the builder reads.
|
|
type Outcome struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version,omitempty"`
|
|
Digest string `json:"digest"`
|
|
Kind string `json:"kind"`
|
|
Holder string `json:"holder"`
|
|
Reason string `json:"reason,omitempty"`
|
|
// Tags are, on kept, the other tags still naming its digest.
|
|
Tags []string `json:"tags,omitempty"`
|
|
}
|
|
|
|
// RemoveOutcome is what a seat's remove answered: kept whole (another tag names its digest), with why and those tags; or
|
|
// asked, the seat saying removed on its events.
|
|
type RemoveOutcome struct {
|
|
Kept bool
|
|
Reason string
|
|
Tags []string
|
|
}
|
|
|
|
// keep marks a tagged version kept by its seat, so it is asked no more.
|
|
func (n *Numberer) keep(ctx context.Context, e Entry, reason string, tags []string) error {
|
|
if reason == "" {
|
|
reason = "shared-digest"
|
|
}
|
|
_, err := n.Versions.Mark(ctx, e.Name, e.Version, func(x *Entry) {
|
|
x.State, x.Reason, x.KeptWith = Kept, reason, append([]string(nil), tags...)
|
|
})
|
|
return err
|
|
}
|
|
|
|
// Heard takes one seat event in: a numbered version's entry turns published (with its digest when it held none),
|
|
// refused with the holder's reason, removed with its reason and the event's id, or kept (another tag names its digest)
|
|
// with the other tags; a by-digest publish (no version)
|
|
// marks nothing. It answers the names waiting on their floor that the event wakes: a `published` says the seat
|
|
// answers (design 51 *The floor*).
|
|
func (n *Numberer) Heard(ctx context.Context, seat, event, id string, o Outcome) ([]string, error) {
|
|
var wake []string
|
|
if event == HeardPublished && n.Waits != nil {
|
|
wake = n.Waits.Wake(WakePublished, seat, "", "")
|
|
}
|
|
if o.Version == "" {
|
|
return wake, nil
|
|
}
|
|
var change func(*Entry)
|
|
switch event {
|
|
case HeardPublished:
|
|
change = func(e *Entry) {
|
|
e.State, e.Reason = Published, ""
|
|
if e.Digest == "" {
|
|
e.Digest = o.Digest
|
|
}
|
|
}
|
|
case HeardRefused:
|
|
change = func(e *Entry) {
|
|
if e.State != Published {
|
|
e.State, e.Reason, e.RefusedDigest = Refused, o.Reason, o.Digest
|
|
}
|
|
}
|
|
case HeardRemoved:
|
|
change = func(e *Entry) { e.State, e.Reason, e.RemovedBy = Removed, o.Reason, id }
|
|
case HeardKept:
|
|
change = func(e *Entry) {
|
|
if e.State != Removed {
|
|
e.State, e.Reason, e.KeptWith = Kept, o.Reason, append([]string(nil), o.Tags...)
|
|
if e.Reason == "" {
|
|
e.Reason = "shared-digest"
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
return wake, fmt.Errorf("%s says %q, which the builder does not hear", seat, event)
|
|
}
|
|
if _, err := n.Versions.Mark(ctx, o.Name, o.Version, change); err != nil {
|
|
return wake, fmt.Errorf("%s's %s of %s@%s: %w", seat, event, o.Name, o.Version, err)
|
|
}
|
|
return wake, nil
|
|
}
|