| 1 | #!/usr/bin/env sh |
| 2 | # π€ ops pages β push committed status-page files from web/status/ to the |
| 3 | # status box's pages dir, where the 5-minute publisher |
| 4 | # (deploy/status-publish.sh) uploads them to status.elevenmessenger.com. |
| 5 | # |
| 6 | # ./deploy/ops pages push [file...] # default: carbon.json |
| 7 | # |
| 8 | # This is the encoded form of the accrual runbook's last step (docs/status.md |
| 9 | # Β§ Keeping the carbon tally accruing): accrue β commit (tiny PR, merged |
| 10 | # promptly) β push the file here. Files are named by basename and always read |
| 11 | # from web/status/ β they are COMMITTED data, so a file with uncommitted local |
| 12 | # changes is refused (commit and merge first; the box must never carry a state |
| 13 | # main doesn't). |
| 14 | # |
| 15 | # Write path: the pages dir is setgid, group-writable by lchat-operators, but |
| 16 | # the files themselves may be owned by another operator (mode 644) β a direct |
| 17 | # scp onto one fails with permission denied. So each file lands as a temp name |
| 18 | # and an mv replaces it: a rename needs only the directory write bit, and it's |
| 19 | # atomic, so the publisher can never read a half-copied file. |
| 20 | set -eu |
| 21 | |
| 22 | BOX=${LCHAT_STATUS_BOX:-elevenbox} |
| 23 | PAGES_DIR=${LCHAT_PAGES_DIR:-/opt/lchat/status-pages} |
| 24 | here="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" |
| 25 | repo="$(CDPATH= cd -- "$here/.." && pwd)" |
| 26 | |
| 27 | usage() { |
| 28 | echo "usage: ops pages push [file...] # basenames under web/status/; default: carbon.json" >&2 |
| 29 | echo " (box: \$LCHAT_STATUS_BOX, default elevenbox; dir: \$LCHAT_PAGES_DIR)" >&2 |
| 30 | exit 2 |
| 31 | } |
| 32 | |
| 33 | cmd="${1:-}" |
| 34 | [ $# -gt 0 ] && shift |
| 35 | case "$cmd" in |
| 36 | |
| 37 | push) |
| 38 | files="${*:-carbon.json}" |
| 39 | for f in $files; do |
| 40 | case "$f" in */*) echo "ops pages: '$f' β basenames only (files live in web/status/)" >&2; exit 1 ;; esac |
| 41 | src="$repo/web/status/$f" |
| 42 | [ -f "$src" ] || { echo "ops pages: no such file web/status/$f" >&2; exit 1; } |
| 43 | if ! git -C "$repo" diff --quiet HEAD -- "web/status/$f"; then |
| 44 | echo "ops pages: web/status/$f has uncommitted changes β commit (and merge) first" >&2 |
| 45 | exit 1 |
| 46 | fi |
| 47 | done |
| 48 | for f in $files; do |
| 49 | tmp="$PAGES_DIR/.$f.tmp.$$" |
| 50 | echo "ops pages: $f -> $BOX:$PAGES_DIR/" |
| 51 | scp -o ConnectTimeout=10 -q "$repo/web/status/$f" "$BOX:$tmp" |
| 52 | ssh -o ConnectTimeout=10 "$BOX" "mv '$tmp' '$PAGES_DIR/$f'" |
| 53 | done |
| 54 | echo "ops pages: done β the publisher uploads within ~5 minutes" |
| 55 | echo |
| 56 | echo "note: this opened box SSH sessions β if this session hasn't written its" |
| 57 | echo " access-ledger why-entry yet: ./deploy/ops ledger \"...\"" |
| 58 | ;; |
| 59 | |
| 60 | -h|--help|help|"") usage ;; |
| 61 | *) echo "ops pages: unknown subcommand '$cmd'" >&2; usage ;; |
| 62 | esac |
| 63 | |