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 " and, sorted, // "path ", 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 }