54 lines
2.1 KiB
Go
54 lines
2.1 KiB
Go
package main
|
|
|
|
// The recipe of the Node build module (novox/hq ADR 0405 §7, ADR 0469 §4, ADR 0477 §1-§3; issues 1488, 1572): the
|
|
// recipe's own version, and the recipe version, a hash of that and the bytes of the lock file that names the
|
|
// TypeScript toolchain. The module names no Node version: it builds with the installed nodejs and npm, reports the
|
|
// version installed to the toolchains state, and refuses only when node cannot be run (module.go).
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
)
|
|
|
|
// RecipeSteps is the recipe's own version: changed whenever the steps of fetch, test or build change in a way that
|
|
// can change what they produce (ADR 0405 §7). It is one of the two values the recipe version hashes.
|
|
const RecipeSteps = "1"
|
|
|
|
// Recipe is every value that makes up the recipe version.
|
|
type Recipe struct {
|
|
// Steps is RecipeSteps.
|
|
Steps string
|
|
// Lock is the hex sha256 of the bytes of toolchain/package-lock.json (LockHash).
|
|
Lock string
|
|
}
|
|
|
|
// LockHash is the text a lock file's bytes stand for in the recipe version.
|
|
func LockHash(lock []byte) string {
|
|
sum := sha256.Sum256(lock)
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
// ReadRecipe is the recipe of this build of the module with the lock file read from path. A missing lock is an
|
|
// error: with no lock there is no toolchain to name.
|
|
func ReadRecipe(lockPath string) (Recipe, error) {
|
|
raw, err := os.ReadFile(lockPath)
|
|
if err != nil {
|
|
return Recipe{}, fmt.Errorf("the toolchain's lock file cannot be read: %w", err)
|
|
}
|
|
return Recipe{Steps: RecipeSteps, Lock: LockHash(raw)}, nil
|
|
}
|
|
|
|
// Version is the recipe version: a hash of the recipe's own version and the lock file's hash, never a Node version
|
|
// (ADR 0477 §3): the toolchain enters each fingerprint once, as the version the build nodes agree on. A change of
|
|
// either moves it and with it every fingerprint that holds it (ADR 0405 §7, ADR 0465 §2). The
|
|
// fields are named in the hashed text, so no two recipes share a text.
|
|
func (r Recipe) Version() string {
|
|
h := sha256.New()
|
|
for _, f := range [][2]string{{"steps", r.Steps}, {"lock", r.Lock}} {
|
|
h.Write([]byte(f[0] + "=" + f[1] + "\n"))
|
|
}
|
|
return hex.EncodeToString(h.Sum(nil))[:16]
|
|
}
|