asked, waiting on the seat every five minutes, stored or refused, as the ask's phase on its step key, so a publish or a copy that stands still is readable on the bus. PublishOne and PublishFiles narrate for every build module; the image copy and the late publish use the same narration. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
87 lines
3.5 KiB
Go
87 lines
3.5 KiB
Go
package buildstep
|
|
|
|
// The publish of a done build's file through a registry seat (novox/hq design 51 *Publishing*, ADR 0611; CAT-BG1,
|
|
// CAT-BN3): from the build module's own process, after the build step ended, through the seat's verbs publish and
|
|
// publish-part, which each build module's manifest `invokes`, in chunks, only what the holder lacks. The holder checks
|
|
// the build with the builder and says published or refused on the seat's events. One place for every build module, so
|
|
// the move to the registry clients is made here once.
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
reg "git.novox.be/novox/mesh-contracts/go/seats/containerimageregistry"
|
|
"git.novox.be/novox/mesh-sdk/go/publish"
|
|
)
|
|
|
|
// The registry seats a build module publishes on.
|
|
const (
|
|
ArtifactSeat = "oci-artifact-registry"
|
|
ImageSeat = reg.Seat
|
|
GoSeat = "go-package-registry"
|
|
NpmSeat = "npm-package-registry"
|
|
)
|
|
|
|
// SeatCall asks one verb of a seat, `seat:<seat>.<verb>`, with a body, and answers its JSON (the bundle's stdio.Ask).
|
|
type SeatCall = func(key string, body any) (json.RawMessage, error)
|
|
|
|
// TransferName is a transfer's name, the build module's own: the name, a qualifier and the digest's head, safe as a
|
|
// transfer id.
|
|
func TransferName(name, qualifier, digest string) string {
|
|
head := strings.TrimPrefix(digest, "sha256:")
|
|
if len(head) > 16 {
|
|
head = head[:16]
|
|
}
|
|
return strings.NewReplacer("/", "-", ".", "-", " ", "-", "*", "-", ">", "-", "@", "-").Replace(name + "-" + qualifier + "-" + head)
|
|
}
|
|
|
|
// PublishOne publishes body, of digest, as name (at version, when it has one) of kind on seat, in one part.
|
|
func PublishOne(ctx context.Context, call SeatCall, seat, name, kind, version, digest, transfer string, body []byte) error {
|
|
in := reg.PublishInput{Name: name, Kind: kind, Version: version, Digest: digest, Transfer: transfer}
|
|
_, err := OnSeat(ctx, seat, publishWhat(in), func() (publish.Sent, error) {
|
|
return publish.Publish(ctx, publish.Call(call), seat, in, []publish.Part{{Digest: digest, Size: int64(len(body)),
|
|
Open: func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(body)), nil }}})
|
|
}, SentHow(1))
|
|
return err
|
|
}
|
|
|
|
// publishWhat names a publish in a narration: its name, its version when it has one, and its digest.
|
|
func publishWhat(in reg.PublishInput) string {
|
|
what := "publish of " + in.Name
|
|
if in.Version != "" {
|
|
what += " " + in.Version
|
|
}
|
|
return what + " at " + in.Digest
|
|
}
|
|
|
|
// SentHow says what of a publish of parts travelled, for its stored line.
|
|
func SentHow(parts int) func(publish.Sent) string {
|
|
return func(s publish.Sent) string {
|
|
return fmt.Sprintf("%d of its %d parts sent, the rest held", len(s.Parts), parts)
|
|
}
|
|
}
|
|
|
|
// File is one part of a publish kept on disk: an image's config, layer or manifest, by its digest and size.
|
|
type File struct {
|
|
Digest string
|
|
Size int64
|
|
Path string
|
|
}
|
|
|
|
// PublishFiles publishes in with files as its parts (an image's config, layers and manifest, CAT-BI1): the holder
|
|
// answers the parts it lacks, and only those are read and sent.
|
|
func PublishFiles(ctx context.Context, call SeatCall, seat string, in reg.PublishInput, files []File) (publish.Sent, error) {
|
|
ps := make([]publish.Part, 0, len(files))
|
|
for _, f := range files {
|
|
ps = append(ps, publish.Part{Digest: f.Digest, Size: f.Size, Open: func() (io.ReadCloser, error) { return os.Open(f.Path) }})
|
|
}
|
|
return OnSeat(ctx, seat, publishWhat(in), func() (publish.Sent, error) {
|
|
return publish.Publish(ctx, publish.Call(call), seat, in, ps)
|
|
}, SentHow(len(files)))
|
|
}
|