Files
mesh-delivery/modules/mesh-delivery-indexer/internal/index/packages_values.go
T

190 lines
8.0 KiB
Go

package index
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"time"
"git.novox.be/novox/mesh-delivery/contracts/go/clients/meshcatalogclient"
"git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
"git.novox.be/novox/mesh-delivery/sdk/trunk"
"git.novox.be/novox/mesh-sdk/go/keys"
)
// The values of the state packages (novox/hq ADR 0593 §2, ADR 0606 §1): the contract's types, written by the indexer
// alone, so their writing rules are the indexer's (ADR 0634 §1: a holder's own logic stays with it).
// PackagesKey is the key of a mesh Go module (its module path) in the packages state: the path as a key part. It refuses
// a path that is no mesh Go module's.
func PackagesKey(module string) (string, error) {
if _, _, ok := trunk.PackageRepository(module); !ok {
return "", fmt.Errorf("index: %q is no mesh Go module's path (it starts with %s)", module, trunk.ForgeModulePrefix)
}
return keys.EscapeName(module), nil
}
func checkPublished(p meshcatalog.PackagesByModuleValue) error {
pkgs := make([][2]string, len(p.Packages))
for i, d := range p.Packages {
pkgs[i] = [2]string{d.Package, d.Digest}
}
return checkPublishedFields(p.Module, p.Repository, p.Dir, p.Commit, p.Requires, p.At, pkgs)
}
var commitFull = regexp.MustCompile(`^[0-9a-f]{40}$`)
// checkPublishedFields is the rule of a published module's value, over its fields (a series' contents hold the same).
func checkPublishedFields(module, repository, dir, commit, requires string, at *time.Time, packages [][2]string) error {
want, _, ok := trunk.PackageRepository(module)
switch {
case !ok:
return fmt.Errorf("index: %q is no mesh Go module's path (it starts with %s)", module, trunk.ForgeModulePrefix)
case !trunk.SameRepository(repository, want):
return fmt.Errorf("index: the module %s is published from %s, and the value names the repository %q", module, want, repository)
case dir == "":
return fmt.Errorf("index: the published module %s names no directory (%q for the repository's root)", module, meshcatalogclient.RootPath)
case !commitFull.MatchString(commit):
return fmt.Errorf("index: the published module %s names the commit %q, which is no full commit", module, commit)
case requires == "":
return fmt.Errorf("index: the published module %s names no digest of its requirements", module)
case at == nil || at.IsZero():
return fmt.Errorf("index: the published module %s names no time", module)
}
for i, d := range packages {
if d[0] != module && !strings.HasPrefix(d[0], module+"/") {
return fmt.Errorf("index: the package %q is not in the module %s", d[0], module)
}
if d[1] == "" {
return fmt.Errorf("index: the package %s names no digest", d[0])
}
if i > 0 && packages[i-1][0] >= d[0] {
return fmt.Errorf("index: the packages of %s are not sorted and each once (%s after %s)", module, d[0], packages[i-1][0])
}
}
return nil
}
// EncodePublishedPackages is p as JSON at the kept values' version; it refuses what a reader could not take.
func EncodePublishedPackages(p meshcatalog.PackagesByModuleValue) ([]byte, error) {
if err := checkPublished(p); err != nil {
return nil, err
}
p.V = keys.Version
return json.Marshal(p)
}
// DecodePublishedPackages reads a per-module packages value; it refuses an unknown version (keys.ErrUnknownVersion) and
// what EncodePublishedPackages refuses.
func DecodePublishedPackages(data []byte) (meshcatalog.PackagesByModuleValue, error) {
if err := keys.CheckVersion(data); err != nil {
return meshcatalog.PackagesByModuleValue{}, err
}
var p meshcatalog.PackagesByModuleValue
if err := json.Unmarshal(data, &p); err != nil {
return meshcatalog.PackagesByModuleValue{}, fmt.Errorf("index: a packages value does not fit its shape: %w", err)
}
if err := checkPublished(p); err != nil {
return meshcatalog.PackagesByModuleValue{}, err
}
return p, nil
}
// SeriesPackageKey is the key of a package's series in the packages state: its ecosystem name as a key part and the series.
func SeriesPackageKey(name, series string) string { return keys.EscapeName(name) + "." + series }
// IsSeriesPackageKey says whether a key of the packages state is a series key, not a per-module one (a series key always
// holds a dot, which keys.EscapeName never leaves in a per-module key).
func IsSeriesPackageKey(key string) bool { return strings.Contains(key, ".") }
// builtFrom says whether pkg is `<module>/<artifact>`: a module name (no slash) and an artifact named after it.
func builtFrom(pkg string) bool {
module, artifact, ok := strings.Cut(pkg, "/")
return ok && module != "" && artifact != ""
}
var seriesForm = regexp.MustCompile(`^[0-9]+\.[0-9]+$`)
func checkSeries(s meshcatalog.PackagesByPackageSeriesValue) error {
switch {
case s.Name == "":
return fmt.Errorf("index: a series of %q names no package in its ecosystem", s.Package)
case !seriesForm.MatchString(s.Series):
return fmt.Errorf("index: the series %q of %s is no <major>.<minor>", s.Series, s.Name)
case s.Package != "" && !builtFrom(s.Package):
return fmt.Errorf("index: the series %s of %s is built from %q, which is no <module>/<artifact>", s.Series, s.Name, s.Package)
}
if v := s.Newest; v != nil {
if v.Version == "" {
return fmt.Errorf("index: the newest version of %s %s names no version", s.Name, s.Series)
}
if c := v.Contents; c != nil {
pkgs := make([][2]string, len(c.Packages))
for i, d := range c.Packages {
pkgs[i] = [2]string{d.Package, d.Digest}
}
if err := checkPublishedFields(c.Module, c.Repository, c.Dir, c.Commit, c.Requires, c.At, pkgs); err != nil {
return err
}
}
}
if v := s.Numbered; v != nil {
if v.Version == "" {
return fmt.Errorf("index: the numbered version of %s %s names no version", s.Name, s.Series)
}
if c := v.Contents; c != nil {
pkgs := make([][2]string, len(c.Packages))
for i, d := range c.Packages {
pkgs[i] = [2]string{d.Package, d.Digest}
}
if err := checkPublishedFields(c.Module, c.Repository, c.Dir, c.Commit, c.Requires, c.At, pkgs); err != nil {
return err
}
}
}
return nil
}
// EncodeSeriesPackage is s as JSON at the kept values' version; it refuses what a reader could not take.
func EncodeSeriesPackage(s meshcatalog.PackagesByPackageSeriesValue) ([]byte, error) {
if err := checkSeries(s); err != nil {
return nil, err
}
s.V = keys.Version
return json.Marshal(s)
}
// DecodeSeriesPackage reads a series key's value; it refuses an unknown version and what EncodeSeriesPackage refuses.
func DecodeSeriesPackage(data []byte) (meshcatalog.PackagesByPackageSeriesValue, error) {
if err := keys.CheckVersion(data); err != nil {
return meshcatalog.PackagesByPackageSeriesValue{}, err
}
var s meshcatalog.PackagesByPackageSeriesValue
if err := json.Unmarshal(data, &s); err != nil {
return meshcatalog.PackagesByPackageSeriesValue{}, fmt.Errorf("index: a series value does not fit its shape: %w", err)
}
if err := checkSeries(s); err != nil {
return meshcatalog.PackagesByPackageSeriesValue{}, err
}
return s, nil
}
// publishedOfContents is a series version's contents as the value of a per-module packages key (the same fields).
func publishedOfContents(c meshcatalog.PackagesByPackageSeriesValueNewestContents) meshcatalog.PackagesByModuleValue {
p := meshcatalog.PackagesByModuleValue{At: c.At, Commit: c.Commit, Dir: c.Dir, Module: c.Module, Repository: c.Repository, Requires: c.Requires, V: c.V}
for _, d := range c.Packages {
p.Packages = append(p.Packages, meshcatalog.PackagesByModuleValuePackagesItem{Package: d.Package, Digest: d.Digest})
}
return p
}
// contentsOfPublished is the value of a per-module packages key as a series version's contents.
func contentsOfPublished(p meshcatalog.PackagesByModuleValue) *meshcatalog.PackagesByPackageSeriesValueNewestContents {
c := &meshcatalog.PackagesByPackageSeriesValueNewestContents{At: p.At, Commit: p.Commit, Dir: p.Dir, Module: p.Module, Repository: p.Repository, Requires: p.Requires, V: p.V}
for _, d := range p.Packages {
c.Packages = append(c.Packages, meshcatalog.PackagesByPackageSeriesValueNewestContentsPackagesItem{Package: d.Package, Digest: d.Digest})
}
return c
}