Files
mesh-sdk/go/delivery/namedtree.go
T
jschoubben 0c8d0f73fa Compute a module's narrowed tree in one function, so every caller gives the merge's value (issue 1758, S2, ADR 0499 §2)
delivery.NamedTree hashes the manifest digest with each named path's git object;
catalogue.Manifest.NamedPaths gives the union of the paths, only when every artifact
names some. The named: prefix keeps the value apart from a git tree id.
2026-10-22 17:27:02 +02:00

48 lines
2.2 KiB
Go

package delivery
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"path"
"sort"
"strings"
)
// NamedTreePrefix opens the tree input of a module whose every artifact names its paths, so such a value is never read
// as a git tree id and a reader of a kept Tree can tell the two apart.
const NamedTreePrefix = "named:"
// NamedTree is the tree input of the new-form fingerprint of a module whose every artifact names the paths it is built
// from (novox/hq ADR 0499 clause 2): `named:` and the hex sha256 of the lines "manifest <digest>" and, sorted,
// "path <path> <object>", joined by a newline. manifest is the module's ManifestDigest (the sha256 of its module.json's
// exact bytes, as Module.Manifest and the merge preview carry it); paths are the named paths, each clean and relative to
// the module's directory as the manifest names them (catalogue.Manifest.NamedPaths), with the git object id each has at
// the commit (a blob for a file, a tree for a directory).
//
// **One function computes it** (clause 2): the indexer, its merge preview (buildstep.ModuleTrees) and the build modules
// call this one, and no second computation exists. It refuses no path, a named path with no object (absent at the
// commit), a path that is not clean, and a manifest that is not a digest, naming them: the indexer then gives the
// module none, with that reason (clause 3).
func NamedTree(manifest string, paths map[string]string) (string, error) {
if _, err := ManifestsKey(manifest); err != nil {
return "", err
}
if len(paths) == 0 {
return "", fmt.Errorf("delivery: the module names no path, so it has no named tree")
}
lines := make([]string, 0, len(paths))
for p, object := range paths {
if p == "" || p != path.Clean(p) || strings.HasPrefix(p, "/") || p == ".." || strings.HasPrefix(p, "../") {
return "", fmt.Errorf("delivery: the named path %q is not a clean path inside the module's directory", p)
}
if object == "" {
return "", fmt.Errorf("delivery: the named path %q has no git object at the commit", p)
}
lines = append(lines, "path "+p+" "+object)
}
sort.Strings(lines)
sum := sha256.Sum256([]byte("manifest " + manifest + "\n" + strings.Join(lines, "\n")))
return NamedTreePrefix + hex.EncodeToString(sum[:]), nil
}