166 lines
5.3 KiB
Go
166 lines
5.3 KiB
Go
package index
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
|
|
"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/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/views"
|
|
)
|
|
|
|
// PackageUsersTool lists, per mesh package, its users and the series each accepts (DLV-I8; ADR 0606 §4; design 32 *A
|
|
// package, its series, and the series its users accept*), so a user left on an older series is seen.
|
|
const PackageUsersTool = "indexer_package_users"
|
|
|
|
// PackageUser is one trunk module using a package: the series it accepts (Named when its manifest's `packages` names it,
|
|
// else the default), that series' newest published version ("" while none is), and the version its fingerprint pins.
|
|
type PackageUser struct {
|
|
Module string `json:"module"`
|
|
Accepts string `json:"accepts"`
|
|
Named bool `json:"named"`
|
|
Newest string `json:"newest"`
|
|
Pinned string `json:"pinned,omitempty"`
|
|
}
|
|
|
|
// PackageUses is one package of indexer_package_users: its series held in the packages state, the newest each published
|
|
// (by series), its Go module path for a Go package, and its users in module order.
|
|
type PackageUses struct {
|
|
Package string `json:"package"`
|
|
GoModule string `json:"go-module,omitempty"`
|
|
Series map[string]string `json:"series"`
|
|
Users []PackageUser `json:"users"`
|
|
}
|
|
|
|
// PackageUsersOf answers indexer_package_users from the indexer's own states (modules, manifests, packages), for one
|
|
// package or, with only empty, every package. A package is known by a series key of the packages state or by a name some
|
|
// trunk manifest's `packages` names. A trunk module uses it when its manifest names it, or, for a Go package, when its
|
|
// go.sum or its build's imports reach the package's Go module from another repository (a sibling by path is no user).
|
|
// A value that cannot be read is an error, never a user silently left out.
|
|
func PackageUsersOf(modules, manifests, packages views.Source, only string) ([]PackageUses, error) {
|
|
known := map[string]*PackageUses{}
|
|
get := func(name string) *PackageUses {
|
|
if known[name] == nil {
|
|
known[name] = &PackageUses{Package: name, Series: map[string]string{}, Users: []PackageUser{}}
|
|
}
|
|
return known[name]
|
|
}
|
|
keys, err := packages.Keys()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the packages state cannot be listed: %w", err)
|
|
}
|
|
for _, key := range keys {
|
|
if !IsSeriesKey(key) {
|
|
continue
|
|
}
|
|
e, ok, err := packages.Get(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("packages.%s cannot be read: %w", key, err)
|
|
}
|
|
if !ok {
|
|
continue
|
|
}
|
|
s, err := DecodeSeriesPackage(e.Value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("packages.%s holds what is not a series: %w", key, err)
|
|
}
|
|
p := get(s.Name)
|
|
if s.GoModule != "" {
|
|
p.GoModule = s.GoModule
|
|
}
|
|
p.Series[s.Series] = ""
|
|
if s.Newest != nil {
|
|
p.Series[s.Series] = s.Newest.Version
|
|
}
|
|
}
|
|
type trunkMod struct {
|
|
mod meshcatalog.ModulesByModuleValue
|
|
manifest catalogue.Manifest
|
|
}
|
|
var mods []string
|
|
held := map[string]trunkMod{}
|
|
names, err := modules.Keys()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the modules state cannot be listed: %w", err)
|
|
}
|
|
for _, name := range names {
|
|
e, ok, err := modules.Get(name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("modules.%s cannot be read: %w", name, err)
|
|
}
|
|
if !ok {
|
|
continue
|
|
}
|
|
mod, err := meshcatalogclient.DecodeModule(e.Value)
|
|
if err != nil || mod.Manifest == "" {
|
|
continue // no manifest held for it
|
|
}
|
|
me, ok, err := manifests.Get(mod.Manifest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the manifest of %s cannot be read: %w", name, err)
|
|
}
|
|
if !ok {
|
|
continue
|
|
}
|
|
kept, err := meshcatalogclient.DecodeManifest(me.Value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the manifest of %s: %w", name, err)
|
|
}
|
|
m, err := catalogue.Decode([]byte(kept.Manifest))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("the manifest of %s does not read: %w", name, err)
|
|
}
|
|
for pkg := range m.Packages {
|
|
get(pkg)
|
|
}
|
|
held[name], mods = trunkMod{mod, m}, append(mods, name)
|
|
}
|
|
slices.Sort(mods)
|
|
reaches := func(mod meshcatalog.ModulesByModuleValue, goModule string) bool {
|
|
if goModule == "" || mod.Inputs2 == nil {
|
|
return false
|
|
}
|
|
if repo, _, ok := trunk.PackageRepository(goModule); !ok || trunk.SameRepository(repo, mod.Repository) {
|
|
return false
|
|
}
|
|
for _, p := range append(slices.Clone(mod.Inputs2.Packages), mod.Inputs2.Imports...) {
|
|
if p == goModule || strings.HasPrefix(p, goModule+"/") {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
var out []PackageUses
|
|
for _, name := range slices.Sorted(maps.Keys(known)) {
|
|
if only != "" && name != only {
|
|
continue
|
|
}
|
|
p := known[name]
|
|
for _, m := range mods {
|
|
t := held[m]
|
|
_, named := t.manifest.Packages[name]
|
|
if !named && !reaches(t.mod, p.GoModule) {
|
|
continue
|
|
}
|
|
accepts := t.manifest.AcceptedSeries(name)
|
|
u := PackageUser{Module: m, Accepts: accepts, Named: named, Newest: p.Series[accepts]}
|
|
if p.GoModule != "" {
|
|
u.Pinned = t.mod.Packages[p.GoModule]
|
|
}
|
|
p.Users = append(p.Users, u)
|
|
}
|
|
out = append(out, *p)
|
|
}
|
|
if only != "" && len(out) == 0 {
|
|
return nil, fmt.Errorf("no series of %s is held and no trunk manifest names it", only)
|
|
}
|
|
if out == nil {
|
|
out = []PackageUses{}
|
|
}
|
|
return out, nil
|
|
}
|