The state tag-asks and its bounds, builder_tag, builder_tag_asks and builder_untag, the checker's tag-wanted consumer, the branch end's removal of tagged versions, the numbering of a check's build under a tag and the event version.numbered (its contract in builds, its emit, its manifest line) are removed; a forge message still wakes a floor. builder_publish_again refuses a held build, since nothing says a version numbered. Builds: design 47 G §373, §321, §349, §413 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
142 lines
6.9 KiB
Go
142 lines
6.9 KiB
Go
package versions
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// What builder_publish_again does with one fingerprint of a name (design 51 *Tools of the builder*), after a holder failed.
|
|
const (
|
|
// AgainPublish is a held build whose recorded version is published again: refused since design 47 G §349 (no version.numbered is said).
|
|
AgainPublish = "publish"
|
|
// AgainBuild builds the fingerprint again under the same number: the build's file is gone and no published was said
|
|
// for the number, so the new digest replaces the recorded one (Numberer.Built).
|
|
AgainBuild = "build"
|
|
// AgainRenumber takes a new number: a published was said for the number, so other bytes never go under it.
|
|
AgainRenumber = "renumber"
|
|
)
|
|
|
|
// Again is what builder_publish_again does, with the entry it acts on and why.
|
|
type Again struct {
|
|
Do string `json:"do"`
|
|
Entry Entry `json:"entry"`
|
|
Why string `json:"why"`
|
|
}
|
|
|
|
// PublishAgain decides builder_publish_again for fingerprint among a name's entries; built says whether the builder still
|
|
// holds the build's file (its done key). It refuses a fingerprint not numbered, and a version refused for other bytes
|
|
// than the ones recorded under it.
|
|
func PublishAgain(entries []Entry, fingerprint string, built bool) (Again, error) {
|
|
fingerprint = strings.TrimSpace(fingerprint)
|
|
if fingerprint == "" {
|
|
return Again{}, errors.New("fp is required: the fingerprint whose version is published again")
|
|
}
|
|
for _, e := range latestFirst(entries) {
|
|
if e.Tag != "" || e.Fingerprint != fingerprint {
|
|
continue
|
|
}
|
|
switch {
|
|
case e.State == Refused && e.RefusedDigest != "" && e.Digest != "" && e.RefusedDigest != e.Digest:
|
|
return Again{}, fmt.Errorf("%s@%s was refused for other bytes (%s, recorded %s): it is not published again; a new build takes a new number",
|
|
e.Name, e.Version, e.RefusedDigest, e.Digest)
|
|
case e.State == Published || e.State == Removed:
|
|
return Again{Do: AgainRenumber, Entry: e, Why: fmt.Sprintf("%s@%s was %s, so the fingerprint takes a new number", e.Name, e.Version, e.State)}, nil
|
|
case !built:
|
|
return Again{Do: AgainBuild, Entry: e, Why: fmt.Sprintf("the build's file is gone and no published was said for %s@%s: built again under the same number", e.Name, e.Version)}, nil
|
|
}
|
|
return Again{Do: AgainPublish, Entry: e, Why: fmt.Sprintf("%s@%s (%s) is held", e.Name, e.Version, e.State)}, nil
|
|
}
|
|
return Again{}, fmt.Errorf("fingerprint %s is not numbered for this name: builder_versions lists what is", fingerprint)
|
|
}
|
|
|
|
// The tools' descriptions.
|
|
const (
|
|
PublishAgainDescription = "Publish one fingerprint's version again after its holder failed (design 51): with the build's file held, the " +
|
|
"nothing says a version numbered since design 47 G §349, so a held build is refused; with the file gone, it builds the fingerprint " +
|
|
"again under the same number, the new digest replacing the recorded one, since no published was said for it; after a published (or " +
|
|
"a removal) it takes a new number. It refuses a fingerprint not numbered and a version the holder refused for other bytes. A hand " +
|
|
"act, recorded with its caller and why in the builder's log."
|
|
FloorAgainDescription = "Ask the floor of one name again now, by hand (design 51 *The floor*): the seat's versions, the forge bridge's " +
|
|
"bridge_tags and the indexer's indexer_requirements, as the builder asks them by itself when a silent source says something. " +
|
|
"Answers which sources were silent and whether the floor is now known. A name the builder's versions already hold needs no floor: " +
|
|
"refused, saying so. A hand act."
|
|
)
|
|
|
|
// Acts are the builder's hand acts on its versions, answered by its program (the tools bundle writes them to reasks).
|
|
type Acts struct {
|
|
Numberer *Numberer
|
|
// Done says whether the build key of module and fingerprint is done: the build's file is held.
|
|
Done func(ctx context.Context, module, fp string) (bool, error)
|
|
// BuildAgain builds a failed key again (builder_build_again's path): its answer, or why it refused.
|
|
BuildAgain func(ctx context.Context, module, fp, why, by string) (answer string, refused bool)
|
|
// Reconsider looks at every module again, so a claim asks a name's floor.
|
|
Reconsider func(ctx context.Context)
|
|
// DropOldForm drops a retired node from the old form's count by hand (builder_old_form): its answer, or why not.
|
|
DropOldForm func(ctx context.Context, node, by, why string, at time.Time) (string, error)
|
|
// DropReserved drops a module's reservation of one MAJOR.MINOR by hand (builder_reserved_drop): its answer, or why not.
|
|
DropReserved func(ctx context.Context, module, series, by, why string, at time.Time) (string, error)
|
|
}
|
|
|
|
// PublishAgain is builder_publish_again of name's fingerprint: what it did, the version, and its answer; an error refuses.
|
|
func (a Acts) PublishAgain(ctx context.Context, name, fp, why, by string, at time.Time) (Again, string, error) {
|
|
module, _, _ := strings.Cut(name, "/")
|
|
entries, err := a.Numberer.Versions.Of(ctx, name)
|
|
if err != nil {
|
|
return Again{}, "", err
|
|
}
|
|
done, err := a.Done(ctx, module, fp)
|
|
if err != nil {
|
|
return Again{}, "", fmt.Errorf("the build key of %s at %s cannot be read: %w", module, fp, err)
|
|
}
|
|
ag, err := PublishAgain(entries, fp, done)
|
|
if err != nil {
|
|
return Again{}, "", err
|
|
}
|
|
if ag.Do == AgainPublish || (ag.Do == AgainRenumber && done) {
|
|
return Again{}, "", errors.New("nothing says a version numbered since design 47 G §349: the version is published under its reservation (§369)")
|
|
}
|
|
e := ag.Entry
|
|
if ag.Do == AgainRenumber {
|
|
_, ne, err := a.Numberer.Versions.Number(ctx, Claim{Name: e.Name, Kind: e.Kind, Series: e.Series, Module: e.Module, Fingerprint: e.Fingerprint,
|
|
Commit: e.Commit, Revision: e.Revision, Seat: e.Seat, Merge: e.Merge, Fresh: true})
|
|
if err != nil {
|
|
return Again{}, "", err
|
|
}
|
|
ag.Entry = ne
|
|
}
|
|
answer := ag.Why
|
|
if ag.Do == AgainBuild || (ag.Do == AgainRenumber && !done) {
|
|
said, refused := a.BuildAgain(ctx, module, fp, why, by)
|
|
if refused {
|
|
return Again{}, "", fmt.Errorf("%s; and the build again was refused: %s", ag.Why, said)
|
|
}
|
|
answer += "; " + said
|
|
}
|
|
return ag, answer, nil
|
|
}
|
|
|
|
// FloorAgain is builder_floor_again of name: the floor counts only for a name the versions hold nothing of.
|
|
func (a Acts) FloorAgain(ctx context.Context, name string) (string, error) {
|
|
entries, err := a.Numberer.Versions.Of(ctx, name)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(entries) > 0 {
|
|
return "", fmt.Errorf("%s holds %d versions: its floor counts only for its first number, so nothing is asked", name, len(entries))
|
|
}
|
|
answer := name + " waits on no floor in this builder: the floor is asked at its next claim"
|
|
if a.Numberer.Waits != nil {
|
|
for _, w := range a.Numberer.Waits.Waiting() {
|
|
if w.Name == name {
|
|
answer = fmt.Sprintf("the floor of %s is asked again (%s were silent)", name, strings.Join(w.Silent, ", "))
|
|
}
|
|
}
|
|
a.Numberer.Waits.Wake(WakeByHand, "", "", name)
|
|
}
|
|
a.Reconsider(ctx)
|
|
return answer + "; builder_versions shows its version once numbered", nil
|
|
}
|