| 1 |
// mnw-cli: Linux release recipe for Bento (aarch64 on astra). |
| 2 |
// |
| 3 |
// Ends at `deploy`, not `collect`. mnw-cli installs itself rather than handing |
| 4 |
// off to Sando, and that is a judgement about blast radius rather than a house |
| 5 |
// style: a bad deploy here stops git-over-SSH to the mirrors on astra. Nothing |
| 6 |
// user-facing, and the mirrors are the redundancy story rather than the serving |
| 7 |
// one, so it earns a real install path with a rollback and not Sando's tiers. |
| 8 |
// Same rung as wam, one below pom. See wiki `deploy-ceremony-tiers`. |
| 9 |
// |
| 10 |
// One arch, one host, and no host is named anywhere in here: build_host() |
| 11 |
// resolves to whichever native machine the topology assigns aarch64, and |
| 12 |
// deploy() to whichever machine bento.toml says runs it. |
| 13 |
|
| 14 |
let h = build_host(); |
| 15 |
let v = version(); |
| 16 |
let r = repo(); |
| 17 |
|
| 18 |
step("checkout"); |
| 19 |
// Pin to the release tag, not whatever main is at pull time. The binary that |
| 20 |
// ships has to be the binary the tag names, or the version it reports below is |
| 21 |
// a claim about a commit nobody built. |
| 22 |
let sha = checkout_sha(h); |
| 23 |
log("pinned " + h + " to mnw-cli-v" + v + " @ " + sha); |
| 24 |
|
| 25 |
step("prebuild"); |
| 26 |
sh_ok(h, "cd " + r + " && cargo clippy --all-targets " + feature_flags() + " -- -D warnings"); |
| 27 |
sh_ok(h, "cd " + r + " && cargo test " + feature_flags()); |
| 28 |
|
| 29 |
step("build"); |
| 30 |
sh_ok(h, "cd " + r + " && cargo build --release " + feature_flags()); |
| 31 |
let binary = resolve_artifact(h, r + "/target/release/mnw-cli"); |
| 32 |
|
| 33 |
step("verify"); |
| 34 |
// The build host must not produce a binary the service host's glibc is too old |
| 35 |
// to exec. Answerable here because this app keeps its `[[deploy]]` entry: the |
| 36 |
// check needs to know which machine runs the binary, and that entry is where |
| 37 |
// that is written. |
| 38 |
log(glibc_check(binary)); |
| 39 |
|
| 40 |
// The version about to ship is the version in the tag. A binary reporting |
| 41 |
// something else means the checkout and the release disagree. |
| 42 |
// |
| 43 |
// This assertion is the reason `--version` exists on this binary at all. It was |
| 44 |
// added 2026-08-23 with this recipe: before it, the only way to ask a running |
| 45 |
// mnw-cli what it was was to stat the file, which is exactly how astra's copy |
| 46 |
// sat four months stale without anyone noticing. |
| 47 |
sh_ok(h, binary + " --version | grep -qw " + v); |
| 48 |
|
| 49 |
step("deploy"); |
| 50 |
// Stages under /var/tmp/bento-deploy/mnw-cli/ and calls the privileged |
| 51 |
// installer, which keeps the outgoing binary as <dst>.prev, installs |
| 52 |
// atomically, and restarts the unit. That .prev file is the whole rollback |
| 53 |
// story, and it is proportionate: putting it back and restarting is one |
| 54 |
// command. |
| 55 |
log(deploy(binary)); |
| 56 |
|
| 57 |
// Health assertions live inside the deploy step rather than a step of their |
| 58 |
// own. `step()` names one of a fixed set the daemon knows, and a "health" step |
| 59 |
// would abort the run AFTER the install has already happened -- the binary live |
| 60 |
// and the release reading failed, which is the worst of both. Assertions about |
| 61 |
// the running service belong to the step that started it. |
| 62 |
// |
| 63 |
// What "healthy" means is the service's business. mnw-cli speaks SSH rather |
| 64 |
// than HTTP, so there is no status code to read: the proof is that the unit is |
| 65 |
// active and that the port answers with an SSH identification string. A dead |
| 66 |
// binary satisfies neither, and a binary that starts and then panics satisfies |
| 67 |
// the first only, which is why both are here. |
| 68 |
sh_ok(deploy_host(), "test -x " + install_path()); |
| 69 |
sh_ok(deploy_host(), "systemctl is-active --quiet mnw-cli.service"); |
| 70 |
// |
| 71 |
// 2222 is hardcoded here and is config on the host (`SSH_PORT` in |
| 72 |
// /opt/mnw-cli/.env, read 2026-08-23). Not read from that file on purpose: it |
| 73 |
// is owned by the service user and the deploy user would need sudo to see it, |
| 74 |
// which is a wider grant than an assertion is worth. If the port ever moves, |
| 75 |
// this line moves with it -- and it failing loudly is the correct outcome of |
| 76 |
// forgetting, since a release that cannot find the service it just restarted |
| 77 |
// should not read green. |
| 78 |
sh_ok(deploy_host(), "banner=$(timeout 5 bash -c 'exec 3<>/dev/tcp/127.0.0.1/2222; head -c 4 <&3' 2>/dev/null); test \"$banner\" = 'SSH-' || { echo \"mnw-cli did not answer with an SSH banner on 2222: got '$banner'\"; exit 1; }"); |
| 79 |
|
| 80 |
log("mnw-cli " + v + " (" + target() + ") built on " + h + " and live on " + deploy_host()); |
| 81 |
|