package chain import ( "crypto/sha256" "encoding/hex" "slices" "strings" ) // The builds bucket's keys (novox/hq issue 1121). A build is under its module, so a read of one module is a read of the keys // `.>` and no other, and a lookup that has what it looks up by is one Get of a pointer key, whose value is a build's id. // // .build. a build's record, its outcome (a records-only read is the filter `.build.>`) // req. a request, and a build that knew no module // id. the module of the build with that id (the pointer a lookup by id alone reads) // .newest the newest build of the module, of any state // .worked the newest successful build of the module // .held the build standing for the module's newest successful build (issue 280) // .commit. the newest successful build from that commit: a CommitPointer // .source. the newest successful build with that reuse key (ADR 0350 §1), spelled by SpellReuse // manifest.sha256- a manifest, once, by the digest of its bytes // // **A pointer is a hint** (issue 1121): the record is written first, then each pointer by compare-and-set; a reader Gets the // record a pointer names and checks it, and when the pointer is missing or wrong reads the module's records, repairs the // pointers and says so. // // No module's name contains a dot. A key is never removed (ADR 0408 §4); the bare `` keys of before the move stay, and are // never read once the kind `builds-layout` is moved. const ( RequestPrefix = "req" IDPrefix = "id" ) // BuildKey is a build's record under its module; a build that knew none is the request's key. func BuildKey(module, id string) string { if module == "" { return RequestKey(id) } return module + ".build." + id } func RequestKey(id string) string { return RequestPrefix + "." + id } func IDKey(id string) string { return IDPrefix + "." + id } func NewestKey(module string) string { return module + ".newest" } func WorkedKey(module string) string { return module + ".worked" } func HeldKey(module string) string { return module + ".held" } func CommitKey(module, sha string) string { return module + ".commit." + sha } func SourceKey(module, fp string) string { return module + ".source." + fp } func ModuleFilter(module string) string { return module + ".build.>" } // ManifestPrefix is where a build's manifest is kept, in the builds bucket itself: a new bucket needs a grant the bus gets // only at a later push, and the push needs the builds. No module is named so. const ManifestPrefix = "manifest" // ReservedModules are the names that are not a module's: a key under one is not a build's record. var ReservedModules = []string{ManifestPrefix, RequestPrefix, IDPrefix, LayoutPrefix} // LayoutPrefix is the name of the key that says the builds are laid out under their modules: LayoutMarkerKey, written once by // the controller's start after it has laid the old keys out. Nothing reads the builds before it exists. const LayoutPrefix = "layout" // LayoutMarkerKey is the key whose existence says the layout is made. const LayoutMarkerKey = LayoutPrefix + ".done" // ManifestKey is where a manifest is kept: `manifest.sha256-`. func ManifestKey(raw []byte) string { sum := sha256.Sum256(raw) return ManifestPrefix + ".sha256-" + hex.EncodeToString(sum[:]) } // CommitPointer is the value of `.commit.`: the build, and what the surveys of every module's commits read // without a Get of each build: its source fingerprint and what it would put on a machine. type CommitPointer struct { Build string `json:"build"` Source string `json:"source,omitempty"` Fingerprint string `json:"fingerprint,omitempty"` } // IsRecordKey is whether a key of the builds bucket is a build's record under a module: `.build.`. func IsRecordKey(key string) bool { parts := strings.Split(key, ".") return len(parts) == 3 && parts[1] == "build" && !slices.Contains(ReservedModules, parts[0]) } // SpellReuse is a reuse key (`v1:`) as a part of a key the bus takes: every character it does not take becomes `-`. func SpellReuse(key string) string { return strings.Map(func(r rune) rune { if r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r >= '0' && r <= '9' || r == '-' || r == '_' { return r } return '-' }, key) } // ModuleOf is the module a record key is under. func ModuleOf(key string) string { m, _, _ := strings.Cut(key, "."); return m } // DecodeKept is a watched entry as a typed record, for a read that took the entries of several kinds and decodes those // that hold a record. func DecodeKept[T any](e Entry) (Kept[T], error) { return decode[T](e) } // AsAnswered is the state of a request record whose outcome has been heard: the record keeps the request and says which // build answered it, so a read of the requests needs no read of the builds. const BuildAnswered = "answered"