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
108 lines
4.1 KiB
Go
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
|
|
}
|