Files
mesh-catalog/modules/mesh-build-node/cmd/mesh-build-node/main.go
T
jschoubben 075b61901c mesh-build-node: announce the states it keeps from its code, so readers are granted from its word (issue 1790, ADR 0495 §2 §5)
Kept (toolchains, keys, steps, each read by another module today) is said with stdio.Keeps
before the module serves; announcetest.Check holds the announcement equal to the code. The
module is pinned to mesh-sdk feat/r4e-sdk 9cb4d59.
2026-10-22 18:08:06 +02:00

70 lines
2.8 KiB
Go

// mesh-build-node: the Node and TypeScript build module (novox/hq ADR 0405, ADR 0466 §1, ADR 0469, ADR 0470; design
// 47 R4c-3, issue 1488). A Go tools bundle the node's runtime launches as the module's own account (runs-as). It names
// the one Node version it builds with, installs the TypeScript toolchain its own lock names, writes the Node version
// it finds to <node>.typescript of its `toolchains` state at start and before every step, and refuses every step on
// a node that has another: its health is then unhealthy, so the controller raises the node's condition. What it
// says, it says on stderr; stdout is the MCP channel.
package main
import (
"context"
"fmt"
"os"
"os/user"
"path/filepath"
"strings"
stdio "git.novox.be/novox/mesh-sdk/go"
"git.novox.be/novox/mesh-sdk/go/delivery"
"git.novox.be/novox/mesh-sdk/go/forgekey"
)
// CacheDir is the cache directory, outside the account's home (ADR 0466 §1; module.json resource `cache`).
const CacheDir = "/var/lib/mesh-build-node-cache"
// WorkDir is the work directory, outside the home (module.json resource `work`).
const WorkDir = "/var/lib/mesh-build-node-work"
func logf(format string, a ...any) { fmt.Fprintf(os.Stderr, "[mesh-build-node] "+format+"\n", a...) }
func main() {
ctx := context.Background()
home := ""
if u, err := user.Current(); err == nil {
home = u.HomeDir
}
paths := Paths{Home: home, Cache: CacheDir}
registry := func() (Registry, error) {
return ReadRegistry(filepath.Join(home, "package-registry.json"), filepath.Join(home, "package-registry.secret"))
}
m := &Module{
Node: strings.TrimSpace(os.Getenv("MESH_NODE")),
Reload: func() (Recipe, error) { return ReadRecipe(paths.LockPath()) },
Probe: ProbeNode,
Toolchain: &Toolchain{Paths: paths, Run: forgekey.ExecRunnerClean(0), Registry: registry},
Toolchains: stdio.State(delivery.ToolchainsState).Put,
Emit: func(t string, b any) error { return stdio.Emit(t, b) },
Steps: stdio.State(delivery.StepsState).Put,
ReadSteps: readOf(stdio.State(delivery.StepsState)),
ReadToolchainsState: readOf(stdio.State(delivery.ToolchainsState)),
Work: WorkDir,
Run: forgekey.ExecRunnerClean(0),
Ctx: ctx,
Say: logf,
}
// Outside a launch by the runtime — a build, a check — there is no node and no state to write.
if m.Node != "" {
go m.Start(ctx)
}
go startKeeper(ctx)
// Every state it keeps, said on the bus before it serves (ADR 0495 §2; issue 1790).
if err := keepStates(); err != nil {
logf("announcing the states it keeps: %v", err)
os.Exit(1)
}
if err := stdio.Serve("", m.Tools(m.StepTools())); err != nil {
logf("%v", err)
os.Exit(1)
}
}