Files
mesh-delivery/modules/mesh-delivery-sdk/buildstep/toolchain.go
T
jschoubben 71a35a6376 MV1: move contracts and sdk under modules/, importers follow
The delivery repository's contracts and SDK live under modules/ as
mesh-delivery-contracts and mesh-delivery-sdk, their Go module paths
renamed to match the folders (the TypeScript names already did), and
every importer in mesh-delivery, mesh-catalog, mesh-host, mesh-tools
and mesh-sdk follows in the same group: a design as it should be, one
path per module, checked by the group's check building every importer.

Builds: design 47 G §427, §88

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 05:09:56 +01:00

85 lines
2.9 KiB
Go

package buildstep
import (
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
)
// ToolchainVersion is the layout version of a toolchains value.
const ToolchainVersion = 1
// Toolchain is the value of a build module's state toolchains under the key <node>.<language>: the version of the
// language's toolchain installed on the node, the build module's recipe version, and when the module saw them. It is
// the part build-step's `Toolchain`, the layout every node-build seat gives that state; the three build modules share
// this neutral form (a seat's generated type is one package per seat). A test reads each seat's fixture strictly into it.
type Toolchain struct {
V int `json:"v,omitempty"`
Language string `json:"language"`
Version string `json:"version"`
Recipe string `json:"recipe"`
// Digest is the sha256: digest of the language's toolchain program as the build module runs it, the program of the
// module that provides that language's toolchain, never the build module's own (design 47 G §472).
Digest string `json:"digest,omitempty"`
At time.Time `json:"at,omitzero"`
}
func checkToolchainLanguage(language string) error {
switch language {
case "go", "typescript":
return nil
}
return fmt.Errorf("buildstep: the toolchain language %q is not go or typescript", language)
}
// ToolchainKey is the key of a node's toolchain of one language: <node>.<language>. It refuses an empty node, a node
// with a dot and a language other than go or typescript.
func ToolchainKey(node, language string) (string, error) {
if node == "" || strings.ContainsAny(node, ". ") {
return "", fmt.Errorf("buildstep: %q is not a node name", node)
}
if err := checkToolchainLanguage(language); err != nil {
return "", err
}
return node + "." + language, nil
}
func (t Toolchain) check() error {
if err := checkToolchainLanguage(t.Language); err != nil {
return err
}
if t.Version == "" || t.Recipe == "" {
return fmt.Errorf("buildstep: a %s toolchain needs its version and its recipe", t.Language)
}
return nil
}
// EncodeToolchain is t as JSON; it refuses a language other than go or typescript and an empty version or recipe.
func EncodeToolchain(t Toolchain) ([]byte, error) {
if err := t.check(); err != nil {
return nil, err
}
t.V = ToolchainVersion
return json.Marshal(t)
}
// DecodeToolchain reads a toolchains value, refusing an unknown field, another layout version and what
// EncodeToolchain refuses.
func DecodeToolchain(data []byte) (Toolchain, error) {
var t Toolchain
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
if err := dec.Decode(&t); err != nil {
return Toolchain{}, fmt.Errorf("buildstep: a toolchain does not read: %w", err)
}
if t.V != ToolchainVersion {
return Toolchain{}, fmt.Errorf("buildstep: a toolchain of version %d, this reads %d", t.V, ToolchainVersion)
}
if err := t.check(); err != nil {
return Toolchain{}, err
}
return t, nil
}