The builder builds one module and stops, with no broker and no registry
This is how a mesh is raised: the installer carries this program and runs it once, before anything exists, to produce the control plane from the same repository and path every later rebuild will use. What raises the mesh is then the same thing that maintains it, rather than a second mechanism exercised once per new mesh — which is how often enough to rot. With nowhere to publish, an image stays in the machine's own runtime and is named by the digest of its own configuration: the same identity the installer has always used for the image it carried.
This commit is contained in:
@@ -56,6 +56,14 @@ is dialled except the broker.
|
||||
MESH_REGISTRY host:port to publish artifacts to, when the mesh has not said
|
||||
MESH_BINDING a file the mesh wrote saying where the artifact store is
|
||||
MESH_WORKSPACE where to clone and build (default: a temporary directory)
|
||||
|
||||
It also builds one module and stops, which is how a mesh is raised — before there is a
|
||||
broker to take work from or a registry to publish into:
|
||||
|
||||
mesh-builder build <repository> [--path P] [--ref COMMIT] [--registry HOST:PORT]
|
||||
|
||||
Without --registry the artifacts stay in this machine's container runtime, named by the
|
||||
digest of their own configuration. The result is printed as JSON.
|
||||
`
|
||||
|
||||
func run() error {
|
||||
@@ -64,6 +72,8 @@ func run() error {
|
||||
case "version":
|
||||
fmt.Println(version)
|
||||
return nil
|
||||
case "build":
|
||||
return buildOnce(context.Background(), os.Args[2:])
|
||||
default:
|
||||
fmt.Print(usage)
|
||||
return nil
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/novox/mesh-control/internal/builder"
|
||||
)
|
||||
|
||||
// buildOnce is the builder doing one build and stopping, with no broker and no mesh.
|
||||
//
|
||||
// **This is how a mesh is raised** (novox/hq ADR 0073). The installer carries this program and runs
|
||||
// it once, before anything else exists, to produce the control plane from the same repository and
|
||||
// path that every later rebuild of it will use. What raises the mesh is therefore the same thing
|
||||
// that maintains it — not a second mechanism that has to be kept in step with the first and is
|
||||
// exercised once per new mesh, which is how often enough to rot.
|
||||
//
|
||||
// It takes no work from a queue and answers nobody: there is no broker yet, and the only thing
|
||||
// waiting for the answer is the installer that started it. So the result goes to standard output as
|
||||
// JSON, which is what the installer reads.
|
||||
//
|
||||
// With no --registry it publishes nowhere and the image stays in this machine's container runtime,
|
||||
// named by the digest of its own configuration (see builder.Local). That is the genesis case. With
|
||||
// one, it publishes as it always does — the same command is how an operator builds a module by hand
|
||||
// on a mesh that already exists.
|
||||
func buildOnce(ctx context.Context, args []string) error {
|
||||
set := flag.NewFlagSet("build", flag.ContinueOnError)
|
||||
path := set.String("path", "", "the module's directory inside the repository (default: its root)")
|
||||
ref := set.String("ref", "", "the commit to build; a branch is a moving target somebody else controls")
|
||||
registry := set.String("registry", "",
|
||||
"host:port to publish to. Without it the artifacts stay in this machine's container runtime, which is the genesis case")
|
||||
workspace := set.String("workspace", "", "where to clone and build (default: a temporary directory)")
|
||||
if err := set.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if set.NArg() != 1 {
|
||||
return errors.New("mesh-builder build <repository> [--path P] [--ref COMMIT] [--registry HOST:PORT]")
|
||||
}
|
||||
repository := set.Arg(0)
|
||||
|
||||
where := *workspace
|
||||
if where == "" {
|
||||
where = os.TempDir() + "/mesh-builder-once"
|
||||
}
|
||||
|
||||
// Local unless told otherwise, because the moment this exists for has nowhere to publish. A
|
||||
// default pointing at a registry would mean genesis failing at a push to something that is not
|
||||
// there yet, one step away from the thing that could explain it.
|
||||
var publisher builder.Publisher = builder.Local{Run: builder.Command}
|
||||
if *registry != "" {
|
||||
publisher = builder.Registry{Address: *registry, Run: builder.Command}
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "building %s", repository)
|
||||
if *path != "" {
|
||||
fmt.Fprintf(os.Stderr, " at %s", *path)
|
||||
}
|
||||
if *ref != "" {
|
||||
fmt.Fprintf(os.Stderr, " at %s", *ref)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr)
|
||||
|
||||
built, err := builder.Build(ctx, builder.Command, publisher, repository, *path, *ref, where)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// To standard output, and everything else to standard error, so the caller can read this
|
||||
// without having to separate it from progress.
|
||||
out := onceResult{
|
||||
Module: built.Manifest.Module,
|
||||
Commit: built.Commit,
|
||||
Repository: repository,
|
||||
Path: *path,
|
||||
Ref: *ref,
|
||||
Manifest: built.Manifest,
|
||||
Against: built.Against,
|
||||
}
|
||||
for _, made := range built.Built {
|
||||
out.Made = append(out.Made, madeArtifact{Name: made.Name, Kind: made.Kind, Reference: made.Reference})
|
||||
}
|
||||
body, err := json.MarshalIndent(out, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(body))
|
||||
fmt.Fprintf(os.Stderr, " built %s from %s\n", built.Manifest.Module, short(built.Commit))
|
||||
return nil
|
||||
}
|
||||
|
||||
// onceResult is what one build reports to whoever started it.
|
||||
//
|
||||
// The same fields the mesh records for a build, so a reader comparing a genesis build against an
|
||||
// ordinary one is comparing the same thing said the same way.
|
||||
type onceResult struct {
|
||||
Module string `json:"module"`
|
||||
Commit string `json:"commit"`
|
||||
Repository string `json:"repository"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Manifest any `json:"manifest"`
|
||||
Made []madeArtifact `json:"made"`
|
||||
Against []string `json:"against,omitempty"`
|
||||
}
|
||||
|
||||
type madeArtifact struct {
|
||||
Name string `json:"name"`
|
||||
Kind string `json:"kind"`
|
||||
Reference string `json:"reference"`
|
||||
}
|
||||
Reference in New Issue
Block a user