package delivery import ( "crypto/sha256" "encoding/hex" "sort" "strings" ) // SourceInputs are what a module's source fingerprint is of (novox/hq ADR 0437 ยง5): the module, the git tree of // its directory, its bases (references, of which only the digest counts), the root tree of each repository its // artifacts read (by artifact), each artifact's toolchain line, and the reason the source does not pin the build // when it does not (a build that resolves packages when built). A context is the value the builder records for it: // a repository's root tree, or for an image that compiles a Go program the `narrow:` hash of the build source it was // handed (mesh-controller internal/builder/builder.go:1146-1156). type SourceInputs struct { Module string Tree string Bases []string Contexts map[string]string Toolchains map[string]string Unpinned string } // SourceFingerprint is the module's source fingerprint, `src1:` and the hex sha256 of its inputs' lines, or none // and the reason when the source does not pin the build: Unpinned when set, "no tree" when Tree is empty. It is the // one function the controller's builder and the indexer both call, so they agree byte for byte. The lines are // "module " and "tree ", then "base " (once each), "context " and "toolchain // " sorted, all joined by a newline. func SourceFingerprint(in SourceInputs) (fp, none string) { if in.Unpinned != "" { return "", in.Unpinned } if in.Tree == "" { return "", "no tree" } lines := []string{"module " + in.Module, "tree " + in.Tree} bases := map[string]bool{} for _, b := range in.Bases { bases[ReferenceDigest(b)] = true } for b := range bases { lines = append(lines, "base "+b) } for a, t := range in.Contexts { lines = append(lines, "context "+a+" "+t) } for a, t := range in.Toolchains { lines = append(lines, "toolchain "+a+" "+t) } sort.Strings(lines[2:]) sum := sha256.Sum256([]byte(strings.Join(lines, "\n"))) return SourcePrefix + hex.EncodeToString(sum[:]), "" } // ReferenceDigest is a reference's digest, `sha256:...`, so the registry address a base was copied into does not // enter the fingerprint; the reference itself when it carries none. func ReferenceDigest(reference string) string { if _, digest, pinned := strings.Cut(reference, "@"); pinned && digest != "" { return digest } return reference }