148 lines
5.7 KiB
Go
148 lines
5.7 KiB
Go
package index
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// The reasons a module has no fingerprint of the new form while the indexer writes none for any module (novox/hq
|
|
// ADR 0465 clause 2; issue 1444). Toolchains missing is delivery.ToolchainNoReport.
|
|
const (
|
|
// None2NoManifest is for a module whose directory the merged message carries no manifest of (an older bridge).
|
|
None2NoManifest = "the message carries no manifest"
|
|
// None2Builder is for a module with no artifact of a toolchain: only the builder's fingerprint is missing.
|
|
None2Builder = "builder's fingerprint not yet known"
|
|
// None2Context is for a module an artifact of which reads another repository's tree (today's form gives it none
|
|
// either, NoneContext).
|
|
None2Context = "context not pinned"
|
|
)
|
|
|
|
// lockFile is the lock file of a language's artifact that resolves packages when it is built, by name in the module's
|
|
// directory (the bridge reads these, internal/bridge LockFiles; issue 1450).
|
|
var lockFile = map[string]string{"go": "go.sum", "typescript": "package-lock.json"}
|
|
|
|
// moduleInputs is what a module's manifest and lock hashes give the indexer at a merge (issue 1450).
|
|
type moduleInputs struct {
|
|
// Kept is the new-form inputs the merge gave the module; nil when the manifest could not be read or gave none.
|
|
Kept *delivery.KeptInputs2
|
|
// Languages is the language of each artifact that has a toolchain (a bundle or a package of go or typescript), by
|
|
// artifact name. The toolchain versions are joined to them when the build modules report them (issue 1444, the
|
|
// watching half); the SDK's KeptInputs2 keeps no languages, so they are read from the manifest again then.
|
|
Languages map[string]string
|
|
// None2 is why the module has no fingerprint of the new form.
|
|
None2 string
|
|
}
|
|
|
|
// baseFP answers a base module's new-form fingerprint, or why it has none.
|
|
type baseFP func(module string) (fp, none string)
|
|
|
|
// inputsOf turns the manifest of a module (raw, as the forge answered it), the tree of its directory and the lock
|
|
// hashes of that directory (lock file name to hash) into its new-form inputs. The manifest is read by the catalogue's
|
|
// parser; a manifest it refuses, or one that is of another module, leaves Kept nil and says why.
|
|
//
|
|
// The rules are the controller's builder (mesh-controller internal/builder/builder.go): a bundle or a package of a
|
|
// language with a toolchain (go, typescript) is the artifact that has one; a package, and a bundle of those languages,
|
|
// resolves packages when it is built (wantsPackages, :1017) only when its language's lock file exists: it then has
|
|
// an entry in Locks with that file's hash, and an artifact whose lock file the merge did not carry has no entry (a
|
|
// dependency-free or vendored bundle resolves nothing, as mesh-sdk SourceInputs2 says of an artifact not in Locks;
|
|
// novox/hq ADR 0469 clause 5, issue 1459). A bundle that needs a lock and lacks one is refused where it is built, not
|
|
// here, so a fingerprint is never given for a build that could not run. An archive, an image and an upstream resolve
|
|
// nothing and have no toolchain. A base is a build.on entry that names a module.
|
|
func inputsOf(module, raw, tree string, locks map[string]string, base baseFP) moduleInputs {
|
|
m, err := catalogue.ParseManifest([]byte(raw))
|
|
if err != nil {
|
|
return moduleInputs{None2: "the manifest cannot be read: " + err.Error()}
|
|
}
|
|
if m.Module != module {
|
|
return moduleInputs{None2: fmt.Sprintf("the manifest is of module %q, not %q", m.Module, module)}
|
|
}
|
|
if tree == "" {
|
|
return moduleInputs{None2: "the merge names no tree for the module's directory"}
|
|
}
|
|
kept := delivery.KeptInputs2{Tree: tree}
|
|
contexts := false
|
|
var missing []string
|
|
langs := languagesOf(m)
|
|
if m.Build != nil {
|
|
for _, a := range m.Build.Artifacts {
|
|
if a.Context != nil {
|
|
contexts = true
|
|
}
|
|
}
|
|
for _, name := range slices.Sorted(maps.Keys(langs)) {
|
|
// A lock file the merge did not carry leaves no entry: the artifact resolves nothing (ADR 0469 clause 5).
|
|
if h := locks[lockFile[langs[name]]]; h != "" {
|
|
if kept.Locks == nil {
|
|
kept.Locks = map[string]string{}
|
|
}
|
|
kept.Locks[name] = h
|
|
}
|
|
}
|
|
for _, on := range basesOf(m) {
|
|
if fp, none := base(on); fp != "" {
|
|
kept.Bases = append(kept.Bases, fp)
|
|
} else {
|
|
missing = append(missing, fmt.Sprintf("the base %s has no fingerprint (%s)", on, none))
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(kept.Bases)
|
|
sort.Strings(missing)
|
|
out := moduleInputs{Kept: &kept}
|
|
if len(langs) > 0 {
|
|
out.Languages = langs
|
|
}
|
|
switch {
|
|
case contexts:
|
|
out.None2 = None2Context
|
|
case len(missing) > 0:
|
|
out.None2 = strings.Join(missing, "; ")
|
|
case len(langs) > 0:
|
|
out.None2 = delivery.ToolchainNoReport
|
|
default:
|
|
out.None2 = None2Builder
|
|
}
|
|
return out
|
|
}
|
|
|
|
// languagesOf is the language of each artifact of the manifest that has a toolchain (a bundle or a package of go or
|
|
// typescript), by artifact name: the artifacts a build module's toolchain enters the fingerprint of.
|
|
func languagesOf(m catalogue.Manifest) map[string]string {
|
|
langs := map[string]string{}
|
|
if m.Build == nil {
|
|
return langs
|
|
}
|
|
for _, a := range m.Build.Artifacts {
|
|
if a.Kind != catalogue.ArtifactBundle && a.Kind != catalogue.ArtifactPackage {
|
|
continue
|
|
}
|
|
if _, ok := lockFile[a.Language]; ok {
|
|
langs[a.Name] = a.Language
|
|
}
|
|
}
|
|
return langs
|
|
}
|
|
|
|
// basesOf is the modules the manifest names as bases (its build.on entries that name a module), each once, sorted.
|
|
func basesOf(m catalogue.Manifest) []string {
|
|
if m.Build == nil {
|
|
return nil
|
|
}
|
|
seen := map[string]bool{}
|
|
var out []string
|
|
for _, on := range m.Build.On {
|
|
if on.Module != "" && !seen[on.Module] {
|
|
seen[on.Module] = true
|
|
out = append(out, on.Module)
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|