The builder's tools bundle serves reserved given a package's name: it follows the catalog half's packages view itself, finds the module from the name, answers the reserved version, or for a version no reservation holds asks the seat mesh-catalog's versions and answers released with the release's digests; a name no entry carries, two entries carry, or a version neither reserved nor released is answered error. builder_versions by name calls reserved through its invokes. The builder's process serves no call. Builds: design 47 G §572, §582 (A-70) Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
106 lines
4.2 KiB
Go
106 lines
4.2 KiB
Go
package versions
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshbuilds"
|
|
)
|
|
|
|
// The builder's tools of its versions (design 51 *Events of the builder*, *Tools of the builder*).
|
|
const (
|
|
VersionsTool = "builder_versions"
|
|
PublishAgainTool = "builder_publish_again"
|
|
FloorAgainTool = "builder_floor_again"
|
|
OldFormTool = "builder_old_form"
|
|
// OldFormClosedEvent is the builder's own family `version`, kept seven days,
|
|
// apart from its builds' ends, so a waiter on a build's end does not wake on them.
|
|
OldFormClosedEvent = "version.old-form-closed"
|
|
)
|
|
|
|
// Reader reads the state as the tools bundle may: every key, and one.
|
|
type Reader struct {
|
|
Keys func() ([]string, error)
|
|
Get func(key string) (json.RawMessage, bool, error)
|
|
}
|
|
|
|
// readStore is a Store over a Reader that refuses every write: the tools bundle only reads versions.
|
|
type readStore struct{ r Reader }
|
|
|
|
var errReadOnly = errors.New("the versions are written by the builder alone")
|
|
|
|
func (s readStore) Get(_ context.Context, key string) ([]byte, uint64, bool, error) {
|
|
v, ok, err := s.r.Get(key)
|
|
return v, 0, ok, err
|
|
}
|
|
func (readStore) Create(context.Context, string, []byte) (uint64, error) { return 0, errReadOnly }
|
|
func (readStore) Update(context.Context, string, []byte, uint64) (uint64, error) {
|
|
return 0, errReadOnly
|
|
}
|
|
func (s readStore) KeysOf(_ context.Context, filter string) ([]string, error) {
|
|
all, err := s.r.Keys()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if filter == AllFilter {
|
|
return all, nil
|
|
}
|
|
prefix := strings.TrimSuffix(filter, "*")
|
|
var out []string
|
|
for _, k := range all {
|
|
if strings.HasPrefix(k, prefix) && !strings.Contains(k[len(prefix):], ".") {
|
|
out = append(out, k)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// VersionsAnswer is what builder_versions answers.
|
|
type VersionsAnswer struct {
|
|
Name string `json:"name"`
|
|
Versions []Entry `json:"versions"`
|
|
// Asked, Recorded and Why answer a holder's question for one version and digest (ADR 0605 §4).
|
|
Asked string `json:"asked,omitempty"`
|
|
Recorded *bool `json:"recorded,omitempty"`
|
|
Why string `json:"why,omitempty"`
|
|
// Reserved is what the seat mesh-builds' verb reserved answers for the name (and version): the reserved or released version
|
|
// with each artifact's digest, or its error (design 47 G §572, §582).
|
|
Reserved *meshbuilds.ReservedAnswer `json:"reserved,omitempty"`
|
|
}
|
|
|
|
// AnswerVersions is builder_versions: a name's versions in order, in one call; with a version (and a digest), whether
|
|
// the builder numbered it with that digest, which the holder asks before it stores (ADR 0605 §4).
|
|
func AnswerVersions(ctx context.Context, r Reader, name, version, digest string) (VersionsAnswer, error) {
|
|
name = strings.TrimSpace(name)
|
|
if name == "" {
|
|
return VersionsAnswer{}, errors.New("name is required: the package, image, bundle or archive name")
|
|
}
|
|
v := Versions{Store: readStore{r}}
|
|
all, err := v.Of(ctx, name)
|
|
if err != nil {
|
|
return VersionsAnswer{}, err
|
|
}
|
|
out := VersionsAnswer{Name: name, Versions: all}
|
|
if out.Versions == nil {
|
|
out.Versions = []Entry{}
|
|
}
|
|
if version = strings.TrimSpace(version); version != "" {
|
|
_, ok, why, err := v.Recorded(ctx, name, version, strings.TrimSpace(digest))
|
|
if err != nil {
|
|
return VersionsAnswer{}, fmt.Errorf("%s@%s: %w", name, version, err)
|
|
}
|
|
out.Asked, out.Recorded, out.Why = version, &ok, why
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// VersionsDescription is builder_versions' description.
|
|
const VersionsDescription = "A name's versions as the builder numbered them (its state versions, design 51): every trunk and tagged " +
|
|
"version of a package, image, bundle or archive in order, each with its kind, series, fingerprint, commit, source revision, digest, " +
|
|
"seat, state (numbered, published, refused with its reason, removed with its time, reason and the event that removed it, or kept: its seat kept it whole on remove, its digest shared, with the reason and the other tags naming it, kept_with) and when. " +
|
|
"With version, and digest, it also answers recorded: whether the builder numbered that version with that digest, and why not: the " +
|
|
"question a registry seat's holder asks before it stores a publish (ADR 0605 §4). Read-only."
|