eleven / messenger Public

Clone
git clone https://amadan.net/eleven/messenger

Plain git — no account needed to clone.

Download

Download this file

1#!/usr/bin/env sh
2# πŸ€– ops validate β€” run every check this repo can run locally, in one command.
3#
4# ./deploy/ops validate # everything (what to run before opening a PR)
5# ./deploy/ops validate go # Go build + vet + tests
6# ./deploy/ops validate web # node --check on the ES modules + npm test
7# ./deploy/ops validate shell # sh -n (+ shellcheck if present) on deploy/
8# ./deploy/ops validate infra # tofu fmt + validate on the dev-mesh stack
9#
10# Why this is a verb: the checks are spread across four toolchains, and every
11# one of them is easy to forget β€” the shell scripts and the tofu stack in
12# particular have no test suite and are only ever caught by running these by
13# hand. A contributor (human or agent) should not have to reconstruct the list
14# from CLAUDE.md; ops is where operational knowledge lives, and "what do I run
15# before pushing" is exactly that.
16#
17# Non-goals: this never touches a box, a credential, or the network beyond what
18# `go test` and `npm test` already do (both are hermetic β€” httptest servers and
19# Node's test runner). It is safe to run at any time, on any branch.
20#
21# Exit status is the number of FAILED sections (0 = all good), and every section
22# runs even when an earlier one fails β€” one pass tells you everything that is
23# broken, rather than making you re-run after each fix.
24set -eu
25
26here="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
27root="$(CDPATH= cd -- "$here/.." && pwd)"
28cd "$root"
29
30bold=''; red=''; green=''; yellow=''; off=''
31if [ -t 1 ]; then
32 bold=$(printf '\033[1m'); red=$(printf '\033[31m'); green=$(printf '\033[32m')
33 yellow=$(printf '\033[33m'); off=$(printf '\033[0m')
34fi
35
36failed=0
37skipped=0
38
39section() { printf '\n%s%s%s\n' "$bold" "$1" "$off"; }
40ok() { printf ' %sok%s %s\n' "$green" "$off" "$1"; }
41skip() { printf ' %sskip%s %s\n' "$yellow" "$off" "$1"; skipped=$((skipped + 1)); }
42bad() { printf ' %sFAIL%s %s\n' "$red" "$off" "$1"; failed=$((failed + 1)); }
43
44# run <label> <command...>: run it, show output only when it fails. Keeping a
45# passing run silent is what makes the summary readable; a failure prints
46# everything, because that is the moment you need it.
47run() {
48 label="$1"; shift
49 out="$(mktemp -t lchat-validate.XXXXXX)"
50 if "$@" >"$out" 2>&1; then
51 ok "$label"
52 else
53 bad "$label"
54 sed 's/^/ /' "$out"
55 fi
56 rm -f "$out"
57}
58
59validate_go() {
60 section "Go"
61 if ! command -v go >/dev/null 2>&1; then skip "go not installed"; return; fi
62 run "build (all packages)" go build ./...
63 # vet is advisory here: the tree carries a pre-existing finding in
64 # checkpoint_test.go, so a failure must not mask a real regression elsewhere.
65 # Shown, not counted.
66 if go vet ./... >/dev/null 2>&1; then
67 ok "vet"
68 else
69 printf ' %swarn%s vet reported findings:\n' "$yellow" "$off"
70 go vet ./... 2>&1 | sed 's/^/ /' || true
71 fi
72 run "tests" go test ./...
73}
74
75validate_web() {
76 section "Web"
77 if command -v node >/dev/null 2>&1; then
78 # The ES modules are never executed by the test suite, so a syntax error
79 # ships silently β€” `node --check` is the only thing standing in the way.
80 bad_files=''
81 for f in web/static/*.js web/static/apps/*.js; do
82 [ -f "$f" ] || continue
83 node --check "$f" >/dev/null 2>&1 || bad_files="$bad_files $f"
84 done
85 if [ -n "$bad_files" ]; then
86 bad "node --check β€”$bad_files"
87 for f in $bad_files; do node --check "$f" 2>&1 | sed 's/^/ /' || true; done
88 else
89 ok "node --check (every web/static ES module parses)"
90 fi
91 else
92 skip "node not installed"
93 fi
94 if [ -f package.json ] && command -v npm >/dev/null 2>&1; then
95 run "npm test (crypto, ui, format, event vectors)" npm test --silent
96 else
97 skip "npm test (no npm or package.json)"
98 fi
99}
100
101validate_shell() {
102 section "Shell"
103 # These scripts have no test suite, so a parse check is the floor. deploy/
104 # deliberately mixes sh and bash (ops and its verbs are POSIX sh; the
105 # collectors are bash and use arrays and `local`), so each file is checked
106 # with the interpreter its OWN shebang names β€” forcing `sh -n` on a bash
107 # script reports syntax errors that are not there.
108 bad_files=''
109 for f in "$here"/ops "$here"/*.sh; do
110 [ -f "$f" ] || continue
111 case "$(head -1 "$f")" in
112 *bash) shell='bash' ;;
113 *) shell='sh' ;;
114 esac
115 "$shell" -n "$f" >/dev/null 2>&1 || bad_files="$bad_files $(basename "$f")"
116 done
117 if [ -n "$bad_files" ]; then
118 bad "parse β€”$bad_files"
119 for f in $bad_files; do
120 case "$(head -1 "$here/$f")" in *bash) shell='bash' ;; *) shell='sh' ;; esac
121 "$shell" -n "$here/$f" 2>&1 | sed 's/^/ /' || true
122 done
123 else
124 ok "parse (every deploy/ script, per its own shebang)"
125 fi
126
127 if command -v shellcheck >/dev/null 2>&1; then
128 # Advisory, and shebang-driven (no -s override, same reason as above). The
129 # tree carries known pre-existing findings (SC1007/SC1091/SC2016), so
130 # counting them would make this verb useless on day one: it reports and
131 # never fails. Read it when you touch a script.
132 if shellcheck "$here"/ops "$here"/*.sh >/dev/null 2>&1; then
133 ok "shellcheck"
134 else
135 printf ' %swarn%s shellcheck findings (advisory β€” pre-existing ones exist):\n' "$yellow" "$off"
136 shellcheck "$here"/ops "$here"/*.sh 2>&1 | grep -E '^In ' | sed "s|$root/||" | head -30 | sed 's/^/ /' || true
137 fi
138 else
139 skip "shellcheck not installed (brew install shellcheck)"
140 fi
141}
142
143validate_infra() {
144 section "Infra (dev-mesh tofu)"
145 stack="$root/infra/dev-mesh"
146 if [ ! -d "$stack" ]; then skip "no infra/dev-mesh"; return; fi
147 if ! command -v tofu >/dev/null 2>&1; then skip "tofu not installed"; return; fi
148 # fmt + validate only: both are offline and read-only. `plan` needs AWS
149 # credentials and talks to the provider, so it stays in `ops dev plan` where
150 # the credential handling lives β€” validate must never need a secret.
151 run "tofu fmt" tofu -chdir="$stack" fmt -check -diff
152 if [ -d "$stack/.terraform" ]; then
153 run "tofu validate" tofu -chdir="$stack" validate
154 else
155 skip "tofu validate (not initialised β€” run: ops dev plan)"
156 fi
157}
158
159what="${1:-all}"
160case "$what" in
161 all) validate_go; validate_web; validate_shell; validate_infra ;;
162 go) validate_go ;;
163 web) validate_web ;;
164 shell) validate_shell ;;
165 infra) validate_infra ;;
166 -h|--help|help)
167 sed -n '2,8p' "$0" | sed 's/^# \{0,1\}//'
168 exit 0 ;;
169 *)
170 echo "ops validate: unknown section '$what' (all|go|web|shell|infra)" >&2
171 exit 2 ;;
172esac
173
174printf '\n'
175if [ "$failed" -eq 0 ]; then
176 printf '%s%s%s' "$green" "all checks passed" "$off"
177 [ "$skipped" -gt 0 ] && printf ' (%d skipped)' "$skipped"
178 printf '\n'
179 exit 0
180fi
181printf '%s%d section(s) FAILED%s\n' "$red" "$failed" "$off"
182exit "$failed"
183