max / makenotwork
| 1 | #!/bin/sh |
| 2 | # Test runner for PoM's scheduled suites on astra. |
| 3 | # |
| 4 | # pom.service calls this once a target's last run goes stale. Everything it |
| 5 | # touches lives under /var/lib/pom, the one path the hardened unit can write: |
| 6 | # the clones, the rust toolchain, and the build artifacts. That is deliberate. |
| 7 | # The daemon runs as `pom` with ProtectHome, so max's checkouts and max's |
| 8 | # toolchain are out of reach by design, and this script must not depend on them. |
| 9 | # |
| 10 | # The one exception is the git mirrors it fetches from, which are read-only. |
| 11 | # |
| 12 | # Usage: run-ci.sh <target> [filter] |
| 13 | # target one of: mnw mt go bb af sk |
| 14 | # filter optional cargo test filter, passed through by PoM |
| 15 | # |
| 16 | # Exit status is the suite's, which is what PoM records as pass/fail. |
| 17 | |
| 18 | |
| 19 | |
| 20 | STAGING="/var/lib/pom/staging" |
| 21 | MIRRORS="/home/max/git-mirrors" |
| 22 | |
| 23 | # The pom user's own toolchain. /usr/bin/cargo is the system rustc, which |
| 24 | # trails what these dependency trees need. |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | # Parallel test databases exhaust the default 1024 descriptors. |
| 31 | || |
| 32 | |
| 33 | # Peer auth on the socket means postgres wants an OS username, and sqlx cannot |
| 34 | # work one out inside the service sandbox: it falls back to the `whoami` crate's |
| 35 | # placeholder and every connection is refused as user "anonymous". libpq tools |
| 36 | # and sqlx both honour PGUSER, so say it once here. |
| 37 | |
| 38 | |
| 39 | # astra has 96 cores, and cargo's default job count is all of them. Ninety-six |
| 40 | # concurrent rustc processes on the MNW server workspace peaked past 16G and 20G |
| 41 | # of swap before the cgroup killed it. This is a memory cap wearing a job-count |
| 42 | # costume; the wall-clock cost is small next to thrashing. |
| 43 | # |
| 44 | # 8 rather than 16 because MemoryMax applies to the whole pom.service cgroup, |
| 45 | # not per invocation, and a sweep that finds several targets stale at once runs |
| 46 | # them concurrently under that one ceiling. Caches are warm in the steady state, |
| 47 | # so few crates actually rebuild and the job count barely shows up in wall clock. |
| 48 | |
| 49 | |
| 50 | # The mirrors are max's, this runs as pom, and git refuses a repo it considers |
| 51 | # foreign. Without this every fetch below fails and the clones silently test |
| 52 | # whatever commit they were created at, forever. Scoped to this process rather |
| 53 | # than written into a global gitconfig. |
| 54 | GIT_CONFIG_COUNT=1 |
| 55 | GIT_CONFIG_KEY_0=safe.directory |
| 56 | GIT_CONFIG_VALUE_0='*' |
| 57 | |
| 58 | |
| 59 | target="" |
| 60 | filter="" |
| 61 | |
| 62 | # The layout under $STAGING mirrors ~/Code on purpose, Apps/ included. Nothing |
| 63 | # in git records that tree, so a plain clone does not reproduce it, and the |
| 64 | # Tauri apps have a symlink that reaches across it: |
| 65 | # Apps/<app>/src-tauri/frontend/js/shared-updater.js |
| 66 | # -> ../../../../../MNW/shared/tauri-updater-ui/updater.js |
| 67 | # Flatten the tree and that dangles, and the app's build.rs panics on a read. |
| 68 | case "" in |
| 69 | mnw) dir="/MNW/server" ;; |
| 70 | mt) dir="/MNW/multithreaded" ;; |
| 71 | go) dir="/Apps/goingson" ;; |
| 72 | bb) dir="/Apps/balanced_breakfast" ;; |
| 73 | af) dir="/Apps/audiofiles" ;; |
| 74 | sk) dir="/synckit/synckit-client" ;; |
| 75 | *) |
| 76 | |
| 77 | |
| 78 | ;; |
| 79 | esac |
| 80 | |
| 81 | if [; then |
| 82 | |
| 83 | |
| 84 | fi |
| 85 | |
| 86 | # Track the mirror so a run tests current main rather than whatever was cloned |
| 87 | # months ago. A failed fetch is not fatal: testing a slightly old tree beats |
| 88 | # reporting nothing, and the staleness clock keeps ticking either way. |
| 89 | repo_root= |
| 90 | if ! ( && && ); then |
| 91 | |
| 92 | fi |
| 93 | |
| 94 | |
| 95 | |
| 96 | |
| 97 | # Postgres on astra listens on a unix socket only, so the URLs carry no host. |
| 98 | # Both harnesses default to a TCP URL, which fails to connect here and fails |
| 99 | # every test in the suite within seconds rather than failing loudly once. |
| 100 | # Do not add ?host=... : the harnesses split these on the last '/'. |
| 101 | # Auth is peer on the socket, so the `pom` role needs LOGIN and CREATEDB. |
| 102 | case "" in |
| 103 | mnw) |
| 104 | # Its own database, not max's `makenotwork`: the sqlx macros are |
| 105 | # checked at compile time against whatever DATABASE_URL points at, so |
| 106 | # CI needs a schema it owns and can migrate. Compiling against the |
| 107 | # committed .sqlx cache instead is not an option worth taking, since |
| 108 | # the cache is hand-maintained by `cargo sqlx prepare` and goes stale |
| 109 | # silently the moment a migration lands without one. |
| 110 | DATABASE_URL='postgres:///pom_ci_makenotwork' |
| 111 | TEST_DATABASE_URL='postgres:///postgres' |
| 112 | |
| 113 | |
| 114 | |
| 115 | "SELECT 1 FROM pg_database WHERE datname = 'pom_ci_makenotwork'" \ |
| 116 | | || |
| 117 | |
| 118 | ;; |
| 119 | mt) |
| 120 | # Two harnesses in one suite. Most tests use the hand-rolled `TestDb`, |
| 121 | # which reads TEST_DATABASE_URL. The `admin` and `bans` modules use |
| 122 | # `#[sqlx::test]`, which reads DATABASE_URL and manages its own throwaway |
| 123 | # databases off it. Set only the first and those 29 fail with |
| 124 | # "DATABASE_URL must be set", which reads like 29 broken tests rather |
| 125 | # than one missing variable. |
| 126 | DATABASE_URL='postgres:///pom_ci_mt' |
| 127 | TEST_DATABASE_URL='postgres:///postgres' |
| 128 | |
| 129 | |
| 130 | |
| 131 | "SELECT 1 FROM pg_database WHERE datname = 'pom_ci_mt'" \ |
| 132 | | || |
| 133 | ;; |
| 134 | esac |
| 135 | |
| 136 | # --test-threads=4 rather than the default: at full parallelism the suites |
| 137 | # intermittently exhaust postgres connection slots on this box. |
| 138 | if [; then |
| 139 | |
| 140 | else |
| 141 | |
| 142 | fi |
| 143 |