From 6c6495f6d93dab05710ce213f282d65049350645 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 30 Sep 2026 12:40:00 +0200 Subject: [PATCH] A delivered host reads its version from where it sits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit novox/hq ADR 0142, which decided this and was not implemented: "It is unpacked into a directory named for its version, so it can read its own version from its path. The stamp goes, and with it the need for a build to know what it will be called." The mesh's toolchain stamps no version, on purpose, so a delivered host called itself "development build" and the mesh could not tell which host any machine ran — which is the whole of what 087 added. A delivered host lives at /versions//, and that directory is the answer. A host placed by hand keeps its stamp, which is the honest answer for one the mesh did not deliver, and is every machine until a delivery reaches it. A binary sitting anywhere else is not read as a version at all. The decision is split from the reading so a test can ask about a path without being that binary. --- cmd/mesh-host/main.go | 45 ++++++++++++++++++++--- cmd/mesh-host/version_from_path_test.go | 48 +++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 cmd/mesh-host/version_from_path_test.go diff --git a/cmd/mesh-host/main.go b/cmd/mesh-host/main.go index 792b520..c410072 100644 --- a/cmd/mesh-host/main.go +++ b/cmd/mesh-host/main.go @@ -51,6 +51,43 @@ var builtFor = "" var version = "development build" +// runningVersion is this host's version: the directory it was delivered into, or the link-time stamp +// for one placed by hand. +// +// **From where it sits, not from its linker** (novox/hq ADR 0142): "It is unpacked into a directory +// named for its version, so it can read its own version from its path. The stamp goes, and with it the +// need for a build to know what it will be called." +// +// The mesh's toolchain does not stamp a version, on purpose — a build does not know what it will be +// called — so a delivered host read as "development build" and the mesh could not tell which host any +// machine ran (novox/hq 04-ISSUES/161, and 087 for why that matters). The path knows: a delivered host +// lives at `/versions//`. +// +// A host placed by hand keeps its stamp, which is the honest answer for one the mesh did not deliver. +func runningVersion() string { + self, err := os.Executable() + if err != nil { + return version + } + return versionAt(self, version) +} + +// versionAt is runningVersion's decision, with the executable's path and the link-time stamp given — +// so a test can ask it about a path without being that binary. +func versionAt(self, stamped string) string { + // .../versions// — the parent is the version, and its parent is the versions + // directory. Checked rather than assumed, so a binary somewhere else does not read a directory + // name as a version. + dir := filepath.Dir(self) + if filepath.Base(filepath.Dir(dir)) != upgrade.VersionsDirName { + return stamped + } + if name := filepath.Base(dir); name != "" && name != "." && name != string(filepath.Separator) { + return name + } + return stamped +} + const usage = `mesh-host — the node host profile what this machine can be asked to do @@ -254,7 +291,7 @@ func run(ctx context.Context, command string, opts options) error { return runLink(ctx, opts) case "version": - fmt.Println(version) + fmt.Println(runningVersion()) return nil case "", "help", "-h", "--help": @@ -594,8 +631,8 @@ func runApply(ctx context.Context, opts options, d *declaration.Declaration, raw // // A failure to record is reported and does not fail the apply. The apply worked; what is // lost is a rollback's ability to come back here, which is worse to hide than to say. - if version != "" { - if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), version); err != nil { + if v := runningVersion(); v != "" { + if err := upgrade.RecordKnownGood(upgrade.KnownGoodPath(opts.state), v); err != nil { fmt.Fprintf(os.Stderr, "mesh-host: applied, but could not record %s as known-good: %v\n"+ " a rollback would have nothing to return to.\n", version, err) @@ -1369,7 +1406,7 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D sched.Sync(declared, held) } - report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: version} + report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: runningVersion()} // Which of this machine's links face outside, for the filter the mesh writes around them // (novox/hq ADR 0140). Reported whatever the node's mode: a converged node's filter needs it, // and an adopted one becomes converged without a further round trip. A machine that cannot read diff --git a/cmd/mesh-host/version_from_path_test.go b/cmd/mesh-host/version_from_path_test.go new file mode 100644 index 0000000..4b00d95 --- /dev/null +++ b/cmd/mesh-host/version_from_path_test.go @@ -0,0 +1,48 @@ +package main + +import ( + "os" + "path/filepath" + "testing" +) + +// A component's version comes from where it sits, not from its linker (novox/hq ADR 0142). The mesh's +// toolchain stamps no version — a build does not know what it will be called — so a delivered host +// read as "development build" and the mesh could not tell which host a machine ran (04-ISSUES/161). + +func TestADeliveredHostReadsItsVersionFromItsPath(t *testing.T) { + // A delivered host lives at /versions//. + dir := t.TempDir() + versioned := filepath.Join(dir, "versions", "637f65559d16") + if err := os.MkdirAll(versioned, 0o755); err != nil { + t.Fatal(err) + } + self := filepath.Join(versioned, "nox-mesh-host") + if err := os.WriteFile(self, []byte("#!/bin/sh\n"), 0o755); err != nil { + t.Fatal(err) + } + if got := versionAt(self, "development build"); got != "637f65559d16" { + t.Fatalf("a delivered host read its version as %q", got) + } +} + +func TestAHostPlacedByHandKeepsItsStamp(t *testing.T) { + // The honest answer for one the mesh did not deliver — and every machine is in that state until + // a delivery reaches it. + if got := versionAt("/usr/bin/nox-mesh-host", "04a27ca"); got != "04a27ca" { + t.Fatalf("a hand-placed host read its version as %q", got) + } +} + +func TestADirectoryThatIsNotAVersionIsNotReadAsOne(t *testing.T) { + // A binary sitting anywhere else must not have its parent directory's name read as a version. + for _, path := range []string{ + "/opt/somewhere/nox-mesh-host", + "/usr/lib/nox-mesh-host/launch", + "/home/someone/build/nox-mesh-host", + } { + if got := versionAt(path, "the stamp"); got != "the stamp" { + t.Fatalf("%s read its version as %q", path, got) + } + } +} -- 2.54.0