Files
mesh-delivery/modules/mesh-delivery-builder/internal/build/numbers.go
T

143 lines
6.1 KiB
Go

package build
import (
"context"
"fmt"
"strconv"
"time"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-delivery/modules/mesh-delivery-builder/builds"
"git.novox.be/novox/mesh-delivery/sdk/trunk"
"mesh-delivery-builder/internal/versions"
)
// Numbers numbers a trunk build at its claim (novox/hq design 51 *The versions*, ADR 0606, ADR 0617 §1) and records the
// digests it was built with. Nil numbers nothing.
type Numbers interface {
// tag is a check's build's tag (TagOf), empty for a trunk build; done the digests of its key when it is done already
// (versions.BuildClaim.Done), nil otherwise.
Number(ctx context.Context, module string, m meshcatalog.ModulesByModuleValue, plan Plan, tag string, done map[string]string) (versions.Numbering, error)
Built(ctx context.Context, module string, m meshcatalog.ModulesByModuleValue, plan Plan, got versions.Numbering, made []builds.BuiltArtifact) error
}
// VersionNumbers is Numbers over the builder's state versions.
type VersionNumbers struct {
N *versions.Numberer
// TagNumbered is told the versions a check's build of a tag ask's head was numbered (the ask is then marked numbered
// in tag-asks, design 51 *Tagged versions*); nil tells nobody.
TagNumbered func(ctx context.Context, c versions.BuildClaim, got versions.Numbering)
}
// ClaimOf is the claim a module value's trunk build is numbered by: a package per source revision (Src), the rest per
// fingerprint, in merge order (Src).
func ClaimOf(module string, m meshcatalog.ModulesByModuleValue, plan Plan) versions.BuildClaim {
c := versions.BuildClaim{Module: module, Path: m.Path, Repository: trunk.RepositoryName(m.Repository), Fingerprint: m.Fp2,
Commit: m.Commit, Merge: uint64(m.Src)}
if uint64(m.Src) != 0 {
c.Revision = strconv.FormatUint(uint64(m.Src), 10)
}
for _, t := range plan.Tasks {
c.Artifacts = append(c.Artifacts, versions.ClaimArtifact{Artifact: t.Artifact, Kind: t.Kind, Language: t.Language})
}
return c
}
func (v VersionNumbers) Number(ctx context.Context, module string, m meshcatalog.ModulesByModuleValue, plan Plan, tag string, done map[string]string) (versions.Numbering, error) {
c := ClaimOf(module, m, plan)
c.Done = done
if tag != "" {
// A check's build of a head a tag ask names: its packages tagged, below the next version (design 51 *Tagged versions*).
c.Tag, c.Head, c.Revision, c.Merge = tag, m.Commit, "", 0
}
got, err := v.N.Number(ctx, c)
if err == nil && tag != "" && len(got.Versions) > 0 && v.TagNumbered != nil {
v.TagNumbered(ctx, c, got)
}
return got, err
}
func (v VersionNumbers) Built(ctx context.Context, module string, m meshcatalog.ModulesByModuleValue, plan Plan, got versions.Numbering, made []builds.BuiltArtifact) error {
digests := map[string]string{}
for _, a := range made {
digests[a.Name] = a.Digest
}
return v.N.Built(ctx, ClaimOf(module, m, plan), got, digests)
}
// TagOf answers the cleaned tag a kept tag ask names for a repository's head (the builder's state tag-asks, design 51
// *Tagged versions*): a check's build of that head numbers its packages tagged at its claim.
type TagOf func(ctx context.Context, repository, head string) (tag string, ok bool)
// number numbers a build before its claim, a trunk build's artifacts or a check's build's packages under its tag: false
// when it waits for its floor (a package, design 51 *The floor*), and then nothing is claimed; the floor's wake looks at
// the module again.
func (b *Builder) number(ctx context.Context, module string, m meshcatalog.ModulesByModuleValue, plan Plan, tag string) (versions.Numbering, bool) {
if b.Numbers == nil {
return versions.Numbering{}, true
}
var done map[string]string
if tag == "" {
done = b.doneDigests(ctx, module, m.Fp2)
}
got, err := b.Numbers.Number(ctx, module, m, plan, tag, done)
for _, s := range got.Said {
b.logf("%s at %s: %s", module, m.Fp2, s.Line)
// And in the build log (design 51 *The versions*: it says a version was kept, and why), between the build's start and end.
// A look at a key already done builds nothing and has no start or end: a version kept there says nothing new, and is
// not logged (each later look, a first view answered or a name heard, would add one).
if b.Log != nil && !(done != nil && s.Why == versions.SaidKept) {
b.Log(builds.BuildLogEntry{Event: builds.BuildSaidEvent, Module: module, FP: m.Fp2, Commit: m.Commit, Node: b.Node,
At: time.Now().UTC(), Line: s.Line, Why: s.Why})
}
}
if err != nil {
// Said, and built as before: an unnumbered build is published by its digest (ADR 0617 §1), numbered at its next look.
b.logf("%s at %s: %v", module, m.Fp2, fmt.Errorf("not numbered, the versions cannot be used: %w", err))
return versions.Numbering{}, true
}
if got.Wait != "" {
b.logf("%s at %s is not built yet: %s", module, m.Fp2, got.Wait)
return got, false
}
return got, true
}
// doneDigests are the digests of a trunk build's key by artifact when the key is done, nil otherwise or when it cannot be
// read: a look at a done build numbers what it left unnumbered with them (versions.BuildClaim.Done).
func (b *Builder) doneDigests(ctx context.Context, module, fp string) map[string]string {
key, err := builds.BuilderBuildKey(module, fp)
if err != nil || b.Claimer.Store == nil {
return nil
}
raw, _, found, err := b.Claimer.Store.Get(ctx, key)
if err != nil || !found {
return nil
}
cur, err := builds.DecodeBuilderBuild(raw)
if err != nil || cur.State != builds.BuildDone {
return nil
}
out := map[string]string{}
for _, a := range cur.Artifacts {
if a.Digest != "" {
out[a.Name] = a.Digest
}
}
return out
}
// PackageNamed looks at a module again when the indexer's packages state names a name for one of its packages
// (versions.NamedModule): a done trunk build that left the package unnumbered, its name not known at its claim, is
// numbered then with the done build's digests, and version.numbered says them for its build module's late publish.
func (b *Builder) PackageNamed(ctx context.Context, module string) {
b.init()
b.mu.Lock()
m, ok := b.known[module]
b.mu.Unlock()
if ok {
b.consider(ctx, module, m)
}
}