Files
mesh-host/Makefile
T
jschoubben e1a2fe7323 The installer carries a builder and builds the control plane it raises
It carried the thing it was going to run; it now carries the thing that makes
it. One artifact either way — but a mesh raised this way holds a control plane
it built from a repository and a commit it can name, and can therefore build
again. A mesh handed a finished image could not, and had no way to find that
out until somebody needed it to.

A build step sits between load and bundle, because the bundle must name an
image and that image no longer arrives finished. Everything after it is
unchanged: a locally built image is named by the digest of its own
configuration, which is exactly what the carried one was named by.

Refused in preflight when nothing says what to build, so a run that cannot
finish says so before it has changed anything.
2026-09-13 04:08:58 +02:00

105 lines
5.6 KiB
Makefile

SYSTEM ?= arch
# The gate. Green is the definition of done (novox/hq how-we-build §5).
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo development)
LDFLAGS := -s -w -X main.builtFor=$(SYSTEM) -X main.version=$(VERSION)
# The bundle a host carries is built INTO it (novox/hq ADR 0038, ADR 0041): a host that needed
# a second file to arrive with it is not "copy it and run it".
BUNDLE ?=
.PHONY: check test vet fmt build clean host hosts bootstrap packaging-test
check: fmt vet test packaging-test build
# One binary per operating system (novox/hq ADR 0060). The system is pinned at link time; a
# host built without one refuses to touch a machine rather than guessing.
hosts:
@for s in arch alpine android; do \
CGO_ENABLED=0 go build -ldflags="-s -w -X main.builtFor=$$s -X main.version=$(VERSION)" \
-o mesh-host-$$s ./cmd/mesh-host || exit 1; \
echo "built mesh-host-$$s"; \
done
packaging-test:
@./packaging/rollback_test.sh
@./packaging/launch_test.sh
@./packaging/roused_test.sh
fmt:
@test -z "$$(gofmt -l . )" || { echo "unformatted:"; gofmt -l . ; exit 1; }
vet:
go vet ./...
# Structure and logic, and the same checks against this machine. The boundary is never mocked.
test:
go test ./... -count=1
# A default build carries no bundle and refuses to reconcile, which is the honest state for a
# host nobody has told what a substrate is.
build:
CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host
# A host for a real machine, carrying a real bundle:
# make host SYSTEM=arch BUNDLE=path/to/substrate.lock
#
# The bundle replaces the one for SYSTEM, because its contents are per operating system —
# package names and unit names differ (novox/hq ADR 0005).
host:
@test -n "$(BUNDLE)" || { echo "BUNDLE= is required; a host with no bundle cannot raise a first node"; exit 1; }
@test -f "$(BUNDLE)" || { echo "no such bundle: $(BUNDLE)"; exit 1; }
@test -f internal/bundle/substrate-$(SYSTEM).lock || { echo "no bundle slot for SYSTEM=$(SYSTEM)"; exit 1; }
@cp internal/bundle/substrate-$(SYSTEM).lock internal/bundle/substrate-$(SYSTEM).lock.default
@cp "$(BUNDLE)" internal/bundle/substrate-$(SYSTEM).lock
@CGO_ENABLED=0 go build -ldflags="$(LDFLAGS)" -o mesh-host ./cmd/mesh-host; \
status=$$?; \
mv internal/bundle/substrate-$(SYSTEM).lock.default internal/bundle/substrate-$(SYSTEM).lock; \
exit $$status
@echo "built for $(SYSTEM) carrying $(BUNDLE)"
# The installer, carrying the control plane's image:
# make bootstrap IMAGE=mesh-builder:v1.2.3
#
# **The carried image is the BUILDER** (novox/hq ADR 0073). It used to be the control plane, on the
# argument that the forge holding the source runs on the mesh, so building at genesis would need a
# mesh in order to raise one. That argument was about the *control plane's* source, and it is
# answered by ADR 0071: the source comes from a mesh that already exists, which is not the one being
# raised. What cannot be fetched is the thing that does the fetching, and that is what is carried.
#
# The image is BUILT ELSEWHERE and handed over — mesh-control's own `make builder-image` — and
# embedded here at release time, the same way carrying the bundle breaks the "copy it onto a machine
# and run it" cycle (novox/hq ADR 0005).
#
# The saved image occupies the embed slot for the length of one build and the placeholder goes
# back, exactly as `host:` does with the bundle. Nothing large is ever committed.
#
# IMAGE must be a NAME:TAG and not an id. The installer identifies the carried image by its tag,
# because an image id is the digest of the image's configuration and a runtime REWRITES that
# configuration as it loads — so the id in the archive is not the id the receiving machine will
# hold, and the tag is the only name that survives the transfer. Saving by id produces an archive
# with no tags at all, which the installer refuses; caught here instead, in front of the person who
# can fix it.
#
# BOOTSTRAP_OUT is where the binary is written, and it exists because something other than a person
# now builds this: the lab rebuilds every artifact it runs from source before a raise, into paths it
# chose (mesh-lab's src/rebuild.ts, and novox/hq 04-ISSUES/005 for why it does that at all). A
# caller that could not say where the output goes would have to copy it afterwards, which is one
# more step to forget.
BOOTSTRAP_OUT ?= mesh-bootstrap
bootstrap:
@test -n "$(IMAGE)" || { echo "IMAGE= is required; an installer carrying no builder image cannot raise a mesh"; exit 1; }
@case "$(IMAGE)" in sha256:*) echo "IMAGE=$(IMAGE) is an image id. The installer identifies the carried image by its tag, because an id is the digest of a configuration that a runtime rewrites as it loads. Pass a name:tag"; exit 1;; esac
@docker image inspect "$(IMAGE)" >/dev/null 2>&1 || { echo "this machine does not hold $(IMAGE) — build it in mesh-control with 'make image'"; exit 1; }
@test -n "$$(docker image inspect --format '{{len .RepoTags}}' "$(IMAGE)" | grep -v '^0$$')" || { echo "$(IMAGE) has no repository tag, so the saved archive would carry no name the installer can ask a runtime about. Tag it first: docker tag $(IMAGE) mesh-builder:<version>"; exit 1; }
@cp internal/image/builder.tar internal/image/builder.tar.placeholder
@docker save --output internal/image/builder.tar "$(IMAGE)"
@CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=$(VERSION)" -o "$(BOOTSTRAP_OUT)" ./cmd/mesh-bootstrap; \
status=$$?; \
mv internal/image/builder.tar.placeholder internal/image/builder.tar; \
exit $$status
@echo "built $(BOOTSTRAP_OUT) carrying $(IMAGE)"
clean:
rm -f mesh-host mesh-bootstrap