Files
mesh-sdk/go/delivery/packages.go
T
jschoubben af4e4e60a3 Carry mesh package versions in build asks and fingerprints
ADR 0573 §2 has every module build against the latest published version of
each mesh package it uses, chosen by the planner and written by the build,
never pinned by hand (issue 1585, S4). The ask, the fetch request and the
merge preview now carry the packages and their versions, and the new-form
fingerprint takes each version, so a package's trunk merge changes every
dependent's fingerprint. A module using no package keeps its fingerprint.

Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
2026-11-01 01:57:26 +01:00

108 lines
4.1 KiB
Go

package delivery
import (
"maps"
"slices"
"strings"
)
// **Every module builds against the latest published version of each mesh package it uses** (novox/hq ADR 0573 §2;
// issue 1585). A mesh package is a Go module of a repository of the mesh's forge (ForgeModulePrefix): mesh-sdk's Go
// module, and a seat's contract its holder's repository publishes (ADR 0573 §1). Its version is the trunk merge the
// indexer holds for the module whose directory holds the package (PackageVersionsOf): a merge that changes it changes
// the fingerprint of every module that uses it, and the build writes that version into its own go.mod and go.sum
// (StepAsk.Packages). Which packages a module uses is read from its lock file by MeshPackagesOf, the same way by the
// forge bridge and by the build modules (buildstep.ModuleTrees), so nobody names a version by hand.
// ForgeModulePrefix is the module path prefix of the Go modules the mesh's forge serves: a Go module whose path starts
// with it is a mesh package, git.novox.be/novox/<repository>[/<directory of the module in it>].
const ForgeModulePrefix = "git.novox.be/novox/"
// MeshPackagesOf are the mesh packages a Go module's go.sum names (the module path of each line that starts with
// ForgeModulePrefix), sorted and each once. go.sum names every module of the build's graph, so a package used only
// through another is named too. Nil for none. A package of the module's own repository comes with its own tree, and
// its reader leaves it out (OtherPackages).
func MeshPackagesOf(goSum []byte) []string {
seen := map[string]bool{}
for _, line := range strings.Split(string(goSum), "\n") {
f := strings.Fields(line)
if len(f) < 2 || !strings.HasPrefix(f[0], ForgeModulePrefix) {
continue
}
if _, _, ok := PackageRepository(f[0]); !ok {
continue
}
seen[f[0]] = true
}
if len(seen) == 0 {
return nil
}
return slices.Sorted(maps.Keys(seen))
}
// OtherPackages are the packages not published from repository, the module's own, in their order; nil for none.
func OtherPackages(packages []string, repository string) []string {
var out []string
for _, p := range packages {
if repo, _, ok := PackageRepository(p); ok && !SameRepository(repo, repository) {
out = append(out, p)
}
}
return out
}
// PackageRepository is the repository (<owner>/<name>) a mesh package is published from and the package's directory in
// it ("" for its root); false for a path that is not a forge module's.
func PackageRepository(pkg string) (repository, dir string, ok bool) {
rest, found := strings.CutPrefix(pkg, ForgeModulePrefix)
if !found || rest == "" {
return "", "", false
}
name, dir, _ := strings.Cut(rest, "/")
if name == "" {
return "", "", false
}
return "novox/" + name, dir, true
}
// PackageVersionsOf is the latest published version of each package from the modules the indexer holds (by key): the
// trunk merge commit of the module of the package's repository whose directory holds the package's directory (the
// deepest, a module at the repository's root holding every directory). A package no held module holds, or whose
// module names no commit or no directory, is in missing, sorted; versions is nil when packages is empty.
func PackageVersionsOf(packages []string, modules map[string]Module) (versions PackageVersions, missing []string) {
for _, pkg := range packages {
repository, dir, ok := PackageRepository(pkg)
commit, depth := "", -1
if ok {
for _, key := range slices.Sorted(maps.Keys(modules)) {
m := modules[key]
if !SameRepository(m.Repository, repository) || m.Commit == "" {
continue
}
at := strings.Trim(m.Path, "/")
switch {
case at == "":
continue // a value that names no directory says nothing of where the package lies
case at == RootPath:
at = ""
case dir != at && !strings.HasPrefix(dir, at+"/"):
continue
}
if d := len(at); d > depth {
commit, depth = m.Commit, d
}
}
}
if commit == "" {
missing = append(missing, pkg)
continue
}
if versions == nil {
versions = PackageVersions{}
}
versions[pkg] = commit
}
slices.Sort(missing)
return versions, missing
}