#!/usr/bin/env sh # # Усі перевірки NetPulse однією командою. # # Навіщо окремий скрипт, коли є .forgejo/workflows/ci.yml: раннера # Forgejo досі немає, тобто CI не запускався жодного разу. Перевірка, яку # нікому виконати, не ловить нічого — а та сама послідовність, запущена # руками перед комітом, ловить уже сьогодні. Коли раннер зʼявиться, CI # викликатиме ті самі кроки; розходитись їм нема з чого. # # Запуск: # sh scripts/check.sh # усе # sh scripts/check.sh web # лише веб # sh scripts/check.sh go # лише server + agent # sh scripts/check.sh repo # лише перевірки репозиторію # # На Windows — з Git Bash; PowerShell цей файл не виконує. # # Скрипт НЕ зупиняється на першій невдачі навмисно: інакше, щоб # побачити три поламані місця, довелось би прогнати його тричі. # Ненульовий код виходу все одно повертається — рівно для гачка # pre-push і для CI. set -u # CDPATH= — не помилка з пробілом, а гасіння CDPATH: якщо він виставлений # в оточенні, cd мовчки піде не туди, і скрипт перевірятиме чужий каталог. # shellcheck disable=SC1007 ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) cd "$ROOT" || exit 1 FAILED="" PASSED="" # Прогін одного кроку. Назва потрібна для підсумку: список «що саме # впало» у кінці цінніший за чотириста рядків виводу вище. step() { name=$1 shift printf '\n\033[1m== %s\033[0m\n' "$name" if "$@"; then PASSED="$PASSED|$name" else FAILED="$FAILED|$name" fi } # gofmt -l друкує список файлів, а не код виходу: без явної перевірки # порожнечі крива форма проїжджає в main непоміченою. gofmt_check() { bad=$(gofmt -l "$1") if [ -n "$bad" ]; then echo "не відформатовано (виправити: gofmt -w $1):" echo "$bad" # На Windows цей крок червоніє ще й від CRLF у робочій копії: gofmt # вважає \r частиною рядка й пропонує переписати файл цілком. У # репозиторії лежить LF (див. .gitattributes), тож на Linux-раннері # той самий файл зелений. Різницю треба назвати вголос: інакше її # «виправлять» через gofmt -w і закомітять зіпсовані переноси. echo "якщо різниця в цілому файлі — це CRLF у робочій копії:" echo " git ls-files --eol $1 | git add --renormalize ." return 1 fi echo "форма в порядку" } # Зонд їде на чуже залізо: перевіряємо, що збирається під усі платформи, # які обіцяємо, а не лише під ту, де запустили скрипт. agent_cross() { out=${TMPDIR:-/tmp}/netpulse-crossbuild mkdir -p "$out" || return 1 for target in linux/amd64 linux/arm64 linux/arm windows/amd64 darwin/arm64; do os=${target%/*} arch=${target#*/} echo "-- $os/$arch" ( cd agent && CGO_ENABLED=0 GOOS="$os" GOARCH="$arch" go build -trimpath \ -o "$out/netpulse-agent-$os-$arch" ./cmd/netpulse-agent ) || return 1 done rm -rf "$out" } go_in() { ( cd "$1" && shift && "$@" ); } check_go() { step "server: gofmt" gofmt_check server step "server: vet" go_in server go vet ./... # Тести проти бази самі пропускаються без NETPULSE_TEST_DSN — локальний # прогін не має ні піднімати Postgres, ні тим паче чіпати бойову базу. step "server: тести" go_in server go test ./... step "agent: gofmt" gofmt_check agent step "agent: vet" go_in agent go vet ./... step "agent: тести" go_in agent go test ./... step "agent: крос-збірка" agent_cross } # Перевірки самого репозиторію — не коду, а того, як він лежить. # # Обидві ловлять поломки, яких не видно в diff і які проявляються вже на # стенді: розходження каталогу профілів зі згенерованою міграцією і CRLF, # що змінює sha256 тієї ж міграції. Чому саме так — у шапках # scripts/check-eol.sh і db/profiles/build.py. # # Тут вони без --strict: у робочій копії на Windows CRLF цілком буває, і # це попередження, а не привід не дати закомітити. У CI той самий скрипт # викликається з --strict — там свіжий клон на Linux, і CRLF означав би # зламане викачування. check_repo() { step "репозиторій: закінчення рядків" sh scripts/check-eol.sh # python3 на Linux, python у Git Bash на Windows: різні імена одного # й того самого, і вгадувати не треба — беремо той, що є. py=python3 command -v python3 >/dev/null 2>&1 || py=python step "репозиторій: каталог профілів" "$py" db/profiles/build.py --check } check_web() { # npm ci лише коли залежностей немає: на кожному прогоні він викидає # node_modules і ставить наново — хвилини замість секунд. if [ ! -d web/node_modules ]; then step "web: залежності" go_in web npm ci fi step "web: типи" go_in web npx tsc --noEmit step "web: тести" go_in web npx vitest run step "web: збірка" go_in web npx vite build } case "${1:-all}" in web) check_web ;; go) check_go ;; repo) check_repo ;; all) check_repo; check_go; check_web ;; *) echo "невідомий набір: $1 (є: all, repo, go, web)"; exit 2 ;; esac printf '\n\033[1m== підсумок\033[0m\n' echo "$PASSED" | tr '|' '\n' | sed '/^$/d' | sed 's/^/ ok /' if [ -n "$FAILED" ]; then echo "$FAILED" | tr '|' '\n' | sed '/^$/d' | sed 's/^/ ВПАЛО /' exit 1 fi echo "усе зелене"