Skip to main content

max / makenotwork

4.8 KB · 92 lines History Blame Raw
1 // pom: Linux release recipe for Bento (x86_64 on fw13, aarch64 on astra).
2 //
3 // One recipe serves both arches. build_host() resolves to whichever native host
4 // the topology assigns the target and deploy() resolves to whichever machine
5 // bento.toml says that target runs on, so there is no cross-compilation and no
6 // hard-coded host name anywhere in here. That matters more than usual: the two
7 // legs of this release are different architectures on different machines, and a
8 // recipe that named hosts could install the aarch64 binary on the x86_64 box.
9 //
10 // Replaces deploy/deploy.sh, which cross-compiled with cargo zigbuild (against
11 // the native-per-architecture rule, and with a cargo-zigbuild that is not
12 // installed on fw13 anyway), reached Hetzner on a port that refuses this
13 // machine's key, and copied config and the unit file over the live ones with no
14 // diff and no backup. It did not work as written when 0.4.0 went out; that
15 // deploy was done by hand.
16 //
17 // This recipe no longer installs anything. Under the Sando/Bento boundary Bento
18 // builds and packages; Sando decides whether a thing advances a stage. So the
19 // recipe ends at `collect`, and the daemon's `[handoff.pom]` carries the
20 // artifact to sandod, which verifies it against its record and runs the gates
21 // that are about the artifact IN AN ENVIRONMENT -- node health, burn-in, the
22 // restart, and rollback if it goes wrong.
23 //
24 // The split is not arbitrary. What stays here is evidence about the ARTIFACT:
25 // clippy, the tests, the glibc check, the version assertion. Those are answerable
26 // on a build host. What left is evidence about the artifact in a place, which a
27 // build host cannot honestly produce.
28
29 let h = build_host();
30 let v = version();
31 let r = repo();
32
33 step("checkout");
34 // Pin to the release tag v<version>, not whatever main is at pull time. The
35 // daemon runs the same pin as a cross-host barrier before either target builds,
36 // so both arches come from one commit rather than from two machines' idea of
37 // main.
38 let sha = checkout_sha(h);
39 log("pinned " + h + " to v" + v + " @ " + sha);
40
41 // Gate: nothing is installed on a host that watches production from code that
42 // fails clippy or its tests. It runs on this target's own build host, so a
43 // break confined to one architecture is caught where it would have shipped.
44 step("prebuild");
45 sh_ok(h, "cd " + r + " && cargo clippy --workspace --all-targets " + feature_flags() + " -- -D warnings");
46 sh_ok(h, "cd " + r + " && cargo test --workspace " + feature_flags());
47
48 step("build");
49 sh_ok(h, "cd " + r + " && cargo build --release " + feature_flags());
50 let binary = resolve_artifact(h, r + "/target/release/pom");
51
52 step("verify");
53 // Native-per-arch removed the cross-compile hazard the old script was written
54 // against, but not this one: fw13 tracks a newer glibc than the Ubuntu 24.04
55 // box in Hetzner, so a binary built here can reference a symbol version that
56 // box does not have and fail at exec -- after the unit has already restarted
57 // onto it. This compares the two and fails the step if the build host got
58 // ahead. The installer checks the same thing again on the far side, by actually
59 // running the binary.
60 log(glibc_check(binary));
61 // The version that is about to ship is the version in the tag. A binary
62 // reporting something else means the checkout and the release disagree.
63 sh_ok(h, binary + " --version | grep -qw " + v);
64
65 step("collect");
66 // Hand the binary to the daemon, which hashes it, writes the artifact record
67 // beside it, and (per `[handoff.pom]`) stages it into sando and asks for an
68 // intake. Nothing is installed or restarted from here.
69 //
70 // One collect per target, and the two never collide: the daemon files each
71 // under <dist_root>/pom/<version>/<target>/, so the aarch64 and x86_64 binaries
72 // are siblings rather than one overwriting the other. Sando then resolves which
73 // of the two each node takes from the platform on its own build record, so the
74 // aarch64 binary cannot land on the Hetzner box even by mistake.
75 collect(h, binary, "pom", v);
76
77 log("pom " + v + " (" + target() + ") built on " + h + " and handed off");
78
79 // What used to follow, and where it went:
80 //
81 // deploy(binary) -> sando promotes, through the ladder
82 // health poll after restart -> sando's node_health gate on each tier
83 // install_path --version -> sando verifies the bundle digest ON THE NODE
84 // after rsync and before the symlink swap, which is
85 // a stronger check than re-running --version: it
86 // proves the bytes, not just the number they print.
87 //
88 // The bootstrap caveat that lived here still holds, and is now sando's to
89 // honour: pom watches its own deploy, so restarting it takes the watcher down
90 // with it. The instances are promoted one tier at a time and health is read
91 // from a peer over the mesh -- never both at once.
92