106 lines
4.1 KiB
Go
106 lines
4.1 KiB
Go
package delivery
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// SourcePrefix2 opens every source fingerprint of the new form.
|
|
const SourcePrefix2 = "src2:"
|
|
|
|
// Toolchain2 is what an artifact's toolchain enters the new fingerprint as (novox/hq ADR 0405 clause 7): the
|
|
// artifact's language, the installed toolchain's version and the build module's recipe version, in place of the
|
|
// toolchain image's digest. A toolchain update so changes the fingerprints of that language's modules only.
|
|
type Toolchain2 struct {
|
|
Language string
|
|
Version string
|
|
Recipe string
|
|
}
|
|
|
|
// SourceInputs2 are what a module's source fingerprint of the new form is of (novox/hq ADR 0429 clause 5 as ADR 0437
|
|
// clause 5 changed it; ADR 0453 clause 6). It differs from SourceInputs in exactly four things: a base enters by its
|
|
// module's fingerprint (src2:...), not its image digest; the builder enters by its own module's fingerprint, not its
|
|
// executable's digest; an artifact that resolves packages when it is built enters by the hash of its lock file; and
|
|
// an artifact's toolchain enters as Toolchain2. Module, Tree, Contexts and Unpinned are as in SourceInputs.
|
|
//
|
|
// Locks names the artifacts that resolve packages when built, each with the hash of its lock file; an entry with an
|
|
// empty hash is such an artifact with no lock, which has no fingerprint. An artifact that resolves nothing is not in
|
|
// Locks.
|
|
type SourceInputs2 struct {
|
|
Module string
|
|
Tree string
|
|
Bases []string
|
|
Builder string
|
|
Contexts map[string]string
|
|
Locks map[string]string
|
|
Toolchains map[string]Toolchain2
|
|
Unpinned string
|
|
}
|
|
|
|
// SourceFingerprint2 is the module's source fingerprint of the new form, `src2:` 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, "no lock" when an artifact resolves packages and has no lock. It is a pure function of its inputs: the order
|
|
// of maps and a base given twice change nothing. The lines are "module <m>", "tree <t>" and "builder <fp>", then
|
|
// "base <fp>" (once each), "context <artifact> <tree>", "lock <artifact> <hash>" and "toolchain <artifact> <language>
|
|
// <version> <recipe>", sorted, all joined by a newline.
|
|
func SourceFingerprint2(in SourceInputs2) (fp, none string) {
|
|
if in.Unpinned != "" {
|
|
return "", in.Unpinned
|
|
}
|
|
if in.Tree == "" {
|
|
return "", "no tree"
|
|
}
|
|
for _, h := range in.Locks {
|
|
if h == "" {
|
|
return "", "no lock"
|
|
}
|
|
}
|
|
lines := []string{"module " + in.Module, "tree " + in.Tree, "builder " + in.Builder}
|
|
bases := map[string]bool{}
|
|
for _, b := range in.Bases {
|
|
bases[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, h := range in.Locks {
|
|
lines = append(lines, "lock "+a+" "+h)
|
|
}
|
|
for a, t := range in.Toolchains {
|
|
lines = append(lines, "toolchain "+a+" "+t.Language+" "+t.Version+" "+t.Recipe)
|
|
}
|
|
sort.Strings(lines[3:])
|
|
sum := sha256.Sum256([]byte(strings.Join(lines, "\n")))
|
|
return SourcePrefix2 + hex.EncodeToString(sum[:]), ""
|
|
}
|
|
|
|
// FingerprintKeyPart is a new-form fingerprint as a key part: its 64 hex digits without the prefix, since a bus key
|
|
// may not hold ':'. It refuses a value that is not `src2:` and 64 lowercase hex digits, a src1 fingerprint included,
|
|
// naming it.
|
|
func FingerprintKeyPart(fp string) (string, error) {
|
|
part, ok := strings.CutPrefix(fp, SourcePrefix2)
|
|
if !ok || !isHex64(part) {
|
|
return "", fmt.Errorf("delivery: %q is not a fingerprint of the new form, %s and 64 lowercase hex digits", fp, SourcePrefix2)
|
|
}
|
|
return part, nil
|
|
}
|
|
|
|
// ParseFingerprintKeyPart is the inverse of FingerprintKeyPart. It refuses a part that is not 64 lowercase hex
|
|
// digits, a src1 fingerprint or a prefixed value included, naming it.
|
|
func ParseFingerprintKeyPart(part string) (string, error) {
|
|
if !isHex64(part) {
|
|
return "", fmt.Errorf("delivery: %q is not the key part of a fingerprint, 64 lowercase hex digits", part)
|
|
}
|
|
return SourcePrefix2 + part, nil
|
|
}
|
|
|
|
func isHex64(s string) bool {
|
|
return len(s) == 64 && strings.Trim(s, "0123456789abcdef") == ""
|
|
}
|