Skip to main content

max / makenotwork

4.2 KB · 83 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 let h = build_host();
18 let v = version();
19 let r = repo();
20
21 step("checkout");
22 // Pin to the release tag v<version>, not whatever main is at pull time. The
23 // daemon runs the same pin as a cross-host barrier before either target builds,
24 // so both arches come from one commit rather than from two machines' idea of
25 // main.
26 let sha = checkout_sha(h);
27 log("pinned " + h + " to v" + v + " @ " + sha);
28
29 // Gate: nothing is installed on a host that watches production from code that
30 // fails clippy or its tests. It runs on this target's own build host, so a
31 // break confined to one architecture is caught where it would have shipped.
32 step("prebuild");
33 sh_ok(h, "cd " + r + " && cargo clippy --workspace --all-targets " + feature_flags() + " -- -D warnings");
34 sh_ok(h, "cd " + r + " && cargo test --workspace " + feature_flags());
35
36 step("build");
37 sh_ok(h, "cd " + r + " && cargo build --release " + feature_flags());
38 let binary = resolve_artifact(h, r + "/target/release/pom");
39
40 step("verify");
41 // Native-per-arch removed the cross-compile hazard the old script was written
42 // against, but not this one: fw13 tracks a newer glibc than the Ubuntu 24.04
43 // box in Hetzner, so a binary built here can reference a symbol version that
44 // box does not have and fail at exec -- after the unit has already restarted
45 // onto it. This compares the two and fails the step if the build host got
46 // ahead. The installer checks the same thing again on the far side, by actually
47 // running the binary.
48 log(glibc_check(binary));
49 // The version that is about to ship is the version in the tag. A binary
50 // reporting something else means the checkout and the release disagree.
51 sh_ok(h, binary + " --version | grep -qw " + v);
52
53 step("deploy");
54 // Ships the binary and restarts the unit, through the root installer the host
55 // holds a scoped sudo grant for. Config is deliberately untouched:
56 // pom-astra.toml and pom-hetzner.toml differ per instance, and prod's carried a
57 // [targets.mnw.tests] block this repo did not have. A deploy that copies config
58 // over the top is how that block gets silently deleted.
59 log(deploy(binary));
60
61 // pom watches its own deploy, so the restart above takes the watcher down with
62 // it for a moment. The two instances are deployed one at a time (Bento runs one
63 // target per host at a time, and require_all_targets keeps the release from
64 // being called done until both are green), so the other one stays up and keeps
65 // watching while this one comes back.
66 //
67 // Assert the restarted instance is actually serving rather than trusting
68 // systemctl's opinion that the unit started: pom answering /api/health is what
69 // "pom is up" means. Retries because a restart is not instant, and a single
70 // immediate probe would just be a race.
71 sh_ok(deploy_host(),
72 "for i in $(seq 1 30); do " +
73 "curl -fsS -o /dev/null --max-time 5 " + health_url() + " && exit 0; " +
74 "sleep 2; done; " +
75 "echo 'pom did not answer " + health_url() + " within 60s after restart'; " +
76 "systemctl status " + service_name() + " --no-pager || true; exit 1");
77
78 // And that what is serving is what was just installed, rather than an old
79 // process that survived the restart.
80 sh_ok(deploy_host(), install_path() + " --version | grep -qw " + v);
81
82 log("pom " + v + " live on " + deploy_host() + " (" + target() + ")");
83