diff --git a/internal/link/builds.go b/internal/link/builds.go index 0542f61..8963bc2 100644 --- a/internal/link/builds.go +++ b/internal/link/builds.go @@ -27,6 +27,14 @@ const TheBuildMachine = "mesh-build-machine" func BuildWork() string { return "mesh.seat." + TheBuildMachine + ".accept.build" } func BuildOutcome() string { return "mesh.seat." + TheBuildMachine + ".event.built" } +// KeyRoleBuilt is the build outcome under the role's name, on the bus the mesh runs on today. +// +// The same event as KeyModuleBuilt and published beside it, because a catalogue installed before this +// change listens for the module's name and one installed after listens for the role's. Both, until +// this bus retires: a rename needs publisher and subscriber to change together, and a deployment +// cannot promise which arrives first. +const KeyRoleBuilt = "built" + // Builders is how work reaches a build machine and how the outcome comes back. type Builders interface { // Submit asks for one build and waits for its outcome. diff --git a/internal/link/builds_current.go b/internal/link/builds_current.go index 1f60d41..57165d2 100644 --- a/internal/link/builds_current.go +++ b/internal/link/builds_current.go @@ -161,8 +161,26 @@ func (b *currentBuild) Announce(ctx context.Context, result BuildResult) error { if result.Failed != "" || result.Commit == "" { return nil } - return EmitEvent(ctx, OverCurrent{Channel: b.channel}, KeyModuleBuilt, "builder", b.on, - announcementOf(result)) + + // **Announced under both names on this bus, for exactly as long as this bus lives.** + // + // A build's outcome belongs to the role now (novox/hq ADR 0121), so a catalogue built from the + // current manifests listens for the role's name. A catalogue that is *already running* listens for + // the module's, because that is what it was told when it was installed. A rename on a live bus + // needs the publisher and the subscriber to change together, and a merge cannot promise that: one + // of them is deployed first, and in that window the graph silently stops being updated — which is + // the failure this whole change was cleaning up after. + // + // So both, and the order stops mattering. The module's own name goes with the bus, in step 5's + // retirement list; nothing has ever run on the bus being built, so there is no legacy name there + // and this doubling has no counterpart. + announced := announcementOf(result) + if err := EmitEvent(ctx, OverCurrent{Channel: b.channel}, KeyModuleBuilt, "builder", b.on, + announced); err != nil { + return err + } + return EmitEvent(ctx, OverCurrent{Channel: b.channel}, KeyRoleBuilt, TheBuildMachine, b.on, + announced) } func (b *currentBuild) Done() error { return b.delivery.Ack(false) } diff --git a/internal/link/builds_current_test.go b/internal/link/builds_current_test.go new file mode 100644 index 0000000..7f43f2c --- /dev/null +++ b/internal/link/builds_current_test.go @@ -0,0 +1,36 @@ +package link + +import ( + "strings" + "testing" +) + +// **The old bus announces a build under both names, and that is not belt-and-braces.** +// +// A build's outcome belongs to the role now, so a catalogue built from the current manifests listens +// for the role's name — and a catalogue already running listens for the module's, because that is what +// it was told when it was installed. A rename on a live bus needs publisher and subscriber to change +// together, which a deployment cannot promise: one arrives first, and in that window the module graph +// silently stops being updated. +// +// Caught by asking what merging this would do to the mesh that is actually running, which is the only +// place the question could have been asked — the tests were green and both buses were self-consistent. +func TestTheOldBusAnnouncesABuildUnderBothNames(t *testing.T) { + // The routing key a catalogue installed before the change is bound to. + if KeyModuleBuilt != "module.builder.built" { + t.Fatalf("the module's own name is %q; a catalogue already running is bound to the old one", + KeyModuleBuilt) + } + // And the local name a catalogue built from the current manifests declares, which the old bus's + // client turns into `module..built`. + if KeyRoleBuilt != "built" { + t.Fatalf("the role's event is %q, and a holder emits its verbs bare", KeyRoleBuilt) + } + if TheBuildMachine != "mesh-build-machine" { + t.Fatalf("the role is %q", TheBuildMachine) + } + // The two must differ, or one publish would serve both and this doubling would be pointless. + if strings.HasSuffix(KeyModuleBuilt, "."+TheBuildMachine+"."+KeyRoleBuilt) { + t.Fatal("the two names are the same, so nothing was renamed and this is dead weight") + } +}