Files
claudetools/.claude/skills/gururmm-build/scripts/verify.sh
Mike Swanson f8c33c9019 sync: auto-sync from GURU-5070 at 2026-06-21 17:24:36
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-21 17:24:36
2026-06-21 17:25:26 -07:00

169 lines
7.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# verify.sh — GuruRMM local pre-merge verification.
# Builds/checks a component the SAME way the server-side pipeline does, so you don't
# merge code that fails the post-merge build on .30. Does NOT trigger the production
# build (that's the webhook on push to main) — this is the local gate only.
#
# Usage:
# bash verify.sh server [--check] # SQLX_OFFLINE cargo build (or check) + .sqlx freshness warning
# bash verify.sh agent [--check] # cargo build (or check) + per-OS compile-gate warning
# bash verify.sh dashboard # npm install + npm run build
# bash verify.sh all [--check] # server + agent + dashboard
# bash verify.sh migrations # report highest migration number + any duplicate numbers
#
# See: projects/msp-tools/guru-rmm/docs/BUILD.md
# projects/msp-tools/guru-rmm/deploy/build-pipeline/README.md
set -uo pipefail
# Resolve repo root (4 levels up: .claude/skills/gururmm-build/scripts/ -> repo root).
CLAUDETOOLS_ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)}"
REPO_DIR="$CLAUDETOOLS_ROOT/projects/msp-tools/guru-rmm"
_logerr() { bash "$CLAUDETOOLS_ROOT/.claude/scripts/log-skill-error.sh" "gururmm-build" "$@" >/dev/null 2>&1 || true; }
# ASCII markers (no emojis — fleet rule).
OK="[OK]"; ERR="[ERROR]"; WARN="[WARNING]"; INFO="[INFO]"
die() { echo "$ERR $*" >&2; _logerr "$*"; exit 1; }
[ -d "$REPO_DIR" ] || die "guru-rmm checkout not found at $REPO_DIR (submodule not initialized?)"
# Host OS family.
case "$(uname -s 2>/dev/null)" in
Linux*) OS=linux ;;
Darwin*) OS=mac ;;
MINGW*|MSYS*|CYGWIN*) OS=windows ;;
*) OS=unknown ;;
esac
CMD="${1:-}"; shift || true
MODE="build"
for a in "$@"; do
case "$a" in
--check) MODE="check" ;;
*) die "unknown flag: $a" ;;
esac
done
[ -n "$CMD" ] || die "usage: verify.sh {server|agent|dashboard|all|migrations} [--check]"
have() { command -v "$1" >/dev/null 2>&1; }
# Names of files changed vs origin/main (committed) + uncommitted, restricted to a path prefix.
changed_under() {
local prefix="$1"
{
git -C "$REPO_DIR" diff --name-only origin/main...HEAD -- "$prefix" 2>/dev/null
git -C "$REPO_DIR" status --porcelain -- "$prefix" 2>/dev/null | awk '{print $2}'
} | sort -u
}
verify_server() {
echo "$INFO === server: $MODE (host=$OS) ==="
have cargo || die "cargo not on PATH — install the Rust toolchain to verify server"
# sqlx offline-cache freshness: the server build runs SQLX_OFFLINE=true against
# server/.sqlx. If server/ Rust changed but .sqlx did not, the build can fail on a
# cache mismatch — warn loudly (not fatal; queries may be unchanged).
local srv_rs srv_sqlx
srv_rs=$(changed_under "server" | grep -E '\.rs$' | wc -l | tr -d ' ')
srv_sqlx=$(changed_under "server/.sqlx" | wc -l | tr -d ' ')
if [ "$srv_rs" -gt 0 ] && [ "$srv_sqlx" -eq 0 ]; then
echo "$WARN server/*.rs changed but server/.sqlx was not regenerated."
echo "$WARN If you added/changed SQL queries or migrations, run (in server/, DB reachable):"
echo "$WARN cargo sqlx prepare && git add server/.sqlx && git commit"
echo "$WARN Otherwise the post-merge server build may fail on the offline cache."
fi
local sub="build --release"; [ "$MODE" = check ] && sub="check"
( cd "$REPO_DIR/server" && SQLX_OFFLINE=true cargo $sub )
local rc=$?
[ $rc -eq 0 ] || { _logerr "server cargo $sub failed (rc=$rc)" --context "os=$OS"; echo "$ERR server $MODE FAILED (rc=$rc)"; return 1; }
echo "$OK server $MODE passed"
}
verify_agent() {
echo "$INFO === agent: $MODE (host=$OS) ==="
have cargo || die "cargo not on PATH — install the Rust toolchain to verify agent"
# Compile-gate warning: platform-gated code is only verified on its own OS.
if [ "$OS" = windows ]; then
echo "$WARN On Windows, cargo cannot verify Linux-gated agent code (#[cfg(target_os=\"linux\")];"
echo "$WARN openssl-sys won't cross-build). The Linux agent build on .30 is the real gate —"
echo "$WARN build on Linux too, or watch /var/log/gururmm-build-linux.log after merge."
elif [ "$OS" = linux ]; then
echo "$INFO Linux host: this gates Linux + cross-platform agent code. Windows-only paths"
echo "$INFO (MSI/tray/legacy/x86) are gated only by the Beast/Pluto Windows build."
fi
local sub="build --release"; [ "$MODE" = check ] && sub="check"
( cd "$REPO_DIR/agent" && cargo $sub )
local rc=$?
[ $rc -eq 0 ] || { _logerr "agent cargo $sub failed (rc=$rc)" --context "os=$OS"; echo "$ERR agent $MODE FAILED (rc=$rc)"; return 1; }
echo "$OK agent $MODE passed (host-OS gate only — see compile-gate warning)"
}
verify_dashboard() {
echo "$INFO === dashboard: build (host=$OS) ==="
have npm || die "npm not on PATH — install Node to verify dashboard"
# 'npm install' (not ci): tolerates the package.json version-bump skew the pipeline makes.
( cd "$REPO_DIR/dashboard" && npm install --no-audit --no-fund && npm run build )
local rc=$?
[ $rc -eq 0 ] || { _logerr "dashboard npm build failed (rc=$rc)" --context "os=$OS"; echo "$ERR dashboard build FAILED (rc=$rc)"; return 1; }
[ -f "$REPO_DIR/dashboard/dist/index.html" ] || { _logerr "dashboard build produced no dist/index.html"; echo "$ERR dashboard build produced no dist/index.html"; return 1; }
echo "$OK dashboard build passed (dist/index.html present)"
}
check_migrations() {
echo "$INFO === migrations: numbering check ==="
local mdir=""
for cand in "server/migrations" "migrations"; do
[ -d "$REPO_DIR/$cand" ] && { mdir="$REPO_DIR/$cand"; break; }
done
[ -n "$mdir" ] || die "no migrations directory found (looked for server/migrations, migrations)"
echo "$INFO migrations dir: ${mdir#$REPO_DIR/}"
# Highest NNN prefix and any duplicate numbers.
local nums
nums=$(ls -1 "$mdir" 2>/dev/null | grep -E '^[0-9]+_' | sed -E 's/^([0-9]+)_.*/\1/' | sort)
[ -n "$nums" ] || { echo "$WARN no NNN_ migrations found in $mdir"; return 0; }
local highest next dupes
highest=$(printf '%s\n' "$nums" | tail -1)
next=$(printf '%03d' $((10#$highest + 1)))
echo "$OK highest migration on this checkout: $highest -> next free number: $next"
dupes=$(printf '%s\n' "$nums" | uniq -d)
if [ -n "$dupes" ]; then
echo "$ERR duplicate migration number(s) detected: $dupes"
printf '%s\n' "$dupes" | while read -r d; do
ls -1 "$mdir" | grep -E "^${d}_" | sed "s/^/$ERR /"
done
_logerr "duplicate migration number(s): $dupes"
return 1
fi
echo "$INFO Before naming a new migration, also check the highest number on origin/main"
echo "$INFO (another branch may have claimed $next): git fetch && ls $(printf '%s' "${mdir#$REPO_DIR/}")"
}
rc=0
case "$CMD" in
server) verify_server || rc=1 ;;
agent) verify_agent || rc=1 ;;
dashboard) verify_dashboard || rc=1 ;;
migrations) check_migrations || rc=1 ;;
all)
verify_server || rc=1
verify_agent || rc=1
verify_dashboard || rc=1
;;
*) die "unknown component: $CMD (use server|agent|dashboard|all|migrations)" ;;
esac
if [ $rc -eq 0 ]; then
echo "$OK verify.sh $CMD: PASS"
else
echo "$ERR verify.sh $CMD: FAIL"
fi
exit $rc