7. The branch decides
main goes live, staging goes to a twin with its own data, and any other branch goes nowhere.
Push main for live, staging for https://<app>-staging. Each environment has its own RC_DATA, so its own databases; the same code, never the same data.
This is the code serving the zoo right now: read from disk for this request, from commit
77ff74344d (staging). The zoo's own self-check fetches this
page and compares it byte for byte with the file it runs.
exhibits/07-the-branch-decides.php sha256 1efa47dc780a · raw
1<?php 2// Exhibit 7. main goes live, staging goes to a twin with its own data, any other branch goes 3// nowhere. RC_FACTS/pushes.json lists recent pushes with where each one went. The staging zoo 4// has its own RC_DATA, so its own zoo.db and guestbook; the check compares the two over HTTPS. 5return [ 6 "n" => 7, "wing" => "Deploy", 7 "title" => "The branch decides", 8 "promise" => "main goes live, staging goes to a twin with its own data, and any other branch goes nowhere.", 9 "block" => "Push main for live, staging for https://<app>-staging. Each environment has its own RC_DATA, so its own databases; the same code, never the same data.", 10 "show" => function (?array $me): string { 11 // The latest push to each place: live, staging and nowhere. 12 $latest = []; 13 foreach (fact("pushes.json") ?? [] as $p) $latest[$p["env"]] ??= $p; 14 $out = '<ul class="list">'; 15 foreach (array_filter([$latest["live"] ?? null, $latest["staging"] ?? null, $latest["preview"] ?? null, $latest["nowhere"] ?? null]) as $p) { 16 $out .= '<li><span class="tag ' . h($p["env"]) . '">' . h($p["env"]) . "</span><span><code>" . h(preg_replace('#^refs/heads/#', "", $p["ref"])) 17 . "</code> " . h(substr($p["commit"], 0, 7)) . " · " . h($p["result"]) . " · " . ago(strtotime($p["at"])) . "</span></li>"; 18 } 19 $s = stored(7)["data"] ?? null; 20 $other = $s ? "<p>This guestbook has " . (int)$s["mine"] . " signature" . ((int)$s["mine"] === 1 ? "" : "s") . "; the <a href=\"https://" . h(other_env_host()) . "/#e4\">" . h($s["other_env"]) . " zoo's</a> has " . (int)$s["theirs"] . ", and they are different books.</p>" : ""; 21 return $out . "</ul>" . $other; 22 }, 23 "check" => function (): array { 24 $pushes = fact("pushes.json") ?? []; 25 foreach ($pushes as $p) { 26 // preview/* goes to <app>-preview (the builder's loop, DECISIONS.md); anything else nowhere. 27 $want = match (true) { $p["ref"] === "refs/heads/main" => "live", $p["ref"] === "refs/heads/staging" => "staging", 28 str_starts_with($p["ref"], "refs/heads/preview/") => "preview", default => "nowhere" }; 29 if ($p["env"] !== $want) return [false, "{$p['ref']} was sent to {$p['env']}"]; 30 } 31 $live = fact("deploy.json")["live"]["commit"] ?? ""; 32 foreach ($pushes as $p) if ($p["env"] === "nowhere" && $p["commit"] === $live && !in_array($live, array_column(array_filter($pushes, fn($q) => $q["env"] === "live"), "commit"), true)) 33 return [false, "a work-in-progress commit ({$p['ref']}) is what live runs"]; 34 // Ours against the other environment's, each read through its own web address. 35 [, , $mine] = (new Browser())->get("https://" . env("RC_HOST") . "/api/4/list"); 36 [$code, , $theirs] = (new Browser())->get("https://" . other_env_host() . "/api/4/list"); 37 if ($code !== 200 || !isset($theirs["instance"])) return [false, "the other environment's zoo did not answer ($code)"]; 38 if ($theirs["instance"] === $mine["instance"]) return [false, "live and staging are the same database"]; 39 $sig = fn($g) => json_encode(array_map(fn($x) => [$x["user"]["username"], $x["note"], $x["at"]], $g)); 40 if ($sig($mine["guestbook"]) === $sig($theirs["guestbook"])) return [false, "the live and staging guestbooks match"]; 41 $nowhere = count(array_filter($pushes, fn($p) => $p["env"] === "nowhere")); 42 return [true, count($pushes) . " pushes each went where its branch says ($nowhere nowhere); " . env("RC_ENV") . " and {$theirs['env']} keep different guestbooks", 43 ["mine" => count($mine["guestbook"]), "theirs" => count($theirs["guestbook"]), "other_env" => $theirs["env"]]]; 44 }, 45];