diff options
Diffstat (limited to 'scripts/release-check.sh')
| -rwxr-xr-x | scripts/release-check.sh | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/scripts/release-check.sh b/scripts/release-check.sh new file mode 100755 index 0000000..223c6b6 --- /dev/null +++ b/scripts/release-check.sh @@ -0,0 +1,258 @@ +#!/usr/bin/env bash +# release-check.sh — MAX25-Stack-Lite offline release gates (no root, no live RF TX). +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +PASS=0 +FAIL=0 +WARN=0 + +ok() { echo "OK: $*"; PASS=$((PASS + 1)); } +fail() { echo "FAIL: $*" >&2; FAIL=$((FAIL + 1)); } +warn() { echo "WARN: $*"; WARN=$((WARN + 1)); } + +echo "======== MAX25-Stack-Lite release-check ($(tr -d '[:space:]' < VERSION 2>/dev/null || echo '?')) ========" + +# --- version --- +if [[ -f VERSION ]]; then + VER="$(tr -d '[:space:]' < VERSION)" + if [[ "$VER" == "1.8.0" ]]; then + ok "VERSION=${VER}" + elif [[ -n "$VER" ]]; then + fail "VERSION=${VER} (expected 1.8.0)" + else + fail "VERSION file empty" + fi +else + fail "VERSION file missing" +fi + +# --- required docs (Lite ships a stub docs/, depth lives in the research vault) --- +for doc in README.md CONTRIBUTING.md CHANGELOG.md docs/README.md local/README.md stacks/daemon/README.md stacks/max25-bcpr/README.md include/max25/protocol.md include/max25/packet-radio.md; do + [[ -f "$doc" ]] && ok "doc $doc" || fail "missing $doc" +done + +# --- scripts present + executable (Lite scope only) --- +for script in scripts/max25-lite-setup.sh scripts/test-wizard.sh scripts/run-max25d.sh scripts/run-max25-terminal.sh scripts/max25-ctl scripts/discover-plugins.sh scripts/build.sh scripts/build-all.sh scripts/clean.sh scripts/test.sh scripts/release-check.sh scripts/tx-rx-test.sh scripts/terminology-check.sh scripts/install-max25.sh; do + if [[ -x "$script" ]]; then + ok "executable $script" + else + fail "not executable: $script" + fi +done + +# --- public terminology gate (§0.16 / §0.19) --- +if [[ -x scripts/terminology-check.sh ]]; then + if scripts/terminology-check.sh; then + ok "terminology-check" + else + fail "terminology-check" + fi +else + fail "scripts/terminology-check.sh missing or not executable" +fi + +# --- daemon core files --- +if [[ -x stacks/daemon/max25d ]] && [[ -f stacks/daemon/max25d.py ]]; then + ok "executable stacks/daemon/max25d launcher + max25d.py" +else + fail "stacks/daemon/max25d launcher or max25d.py missing" +fi +for f in kiss_bridge.py ax25_codec.py privilege_drop.py; do + [[ -f "stacks/daemon/${f}" ]] && ok "stacks/daemon/${f}" || fail "missing stacks/daemon/${f}" +done +if python3 stacks/daemon/test_ax25_codec.py >/dev/null 2>&1; then + ok "ax25_codec unit tests" +else + fail "ax25_codec unit tests" +fi + +# --- Lite INI examples (localhost-only model) --- +for ini in share/max25/max25d.ini.example share/max25/max25d.lite.ini.example share/max25/max25d.lite.bcpr.ini.example share/max25/max25d.lite.crdop.ini.example share/max25-bcpr/max25-bcpr.ini.example stacks/max25-bcpr/share/max25-bcpr.ini.example stacks/crdop/share/crdop.ini.example; do + [[ -f "$ini" ]] && ok "ini $ini" || fail "missing $ini" +done + +# --- localhost TCP in shipped max25d examples --- +for ini in share/max25/max25d.ini.example share/max25/max25d.lite.ini.example share/max25/max25d.lite.bcpr.ini.example share/max25/max25d.lite.crdop.ini.example; do + if grep -Eq '^[[:space:]]*tcp_host[[:space:]]*=[[:space:]]*0\.0\.0\.0[[:space:]]*$' "$ini"; then + fail "ini $ini has tcp_host=0.0.0.0 (Lite localhost-only)" + elif grep -Eq '^[[:space:]]*tcp_host[[:space:]]*=[[:space:]]*127\.0\.0\.1[[:space:]]*$' "$ini"; then + ok "ini $ini tcp_host=127.0.0.1" + else + fail "ini $ini missing tcp_host=127.0.0.1" + fi +done + +if [[ -f share/clients/index.yaml ]] && [[ -f share/clients/tnc2c.yaml ]]; then + ok "share/clients registry" +else + warn "share/clients/ (index.yaml / tnc2c.yaml) missing (informational only in Lite)" +fi + +# --- out-of-scope surfaces must stay inert (HyBBX co-host, hardware TNC stacks, plugin tree) --- +if [[ -d plugins ]]; then + fail "plugins/ present — Lite configures devices directly in max25d.ini, no plugin tree expected" +else + ok "no plugins/ tree (Lite: direct INI device config)" +fi +if [[ -d stacks/tncs ]]; then + fail "stacks/tncs/ present — TNC2C/pktnc2 hardware stack is out of scope for Lite" +else + ok "no stacks/tncs/ (hardware TNC stack out of scope for Lite)" +fi +if grep -rq 'hybbx' CMakeLists.txt 2>/dev/null; then + fail "CMakeLists.txt references hybbx — HyBBX co-host build hooks are out of scope for Lite" +else + ok "CMakeLists.txt free of hybbx references" +fi +if [[ -f share/hybbx/README.md ]]; then + ok "share/hybbx/README.md scope note present (kept for upstream sync, not a Lite path)" +else + warn "share/hybbx/ present without a scope note (see design-principles.md sync policy)" +fi + +# --- build artifacts (CMake, Lite target set) --- +BUILD_DIR="build" +if ! cmake -B "${BUILD_DIR}" -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1; then + BUILD_DIR="build-default" + cmake -B "${BUILD_DIR}" -DCMAKE_BUILD_TYPE=Release >/dev/null 2>&1 \ + || fail "cmake configure" +fi +if cmake --build "${BUILD_DIR}" -j"$(nproc 2>/dev/null || echo 2)" >/dev/null 2>&1; then + ok "cmake build (${BUILD_DIR}/bin/)" +else + fail "cmake build" +fi +[[ -x "${BUILD_DIR}/bin/max25-terminal" ]] && ok "max25-terminal built" || fail "max25-terminal build" +if [[ -x "${BUILD_DIR}/bin/max25-bcprd" ]] && [[ -x "${BUILD_DIR}/bin/max25-bcprd-init" ]]; then + ok "max25-bcprd + max25-bcprd-init built (default ON in Lite)" +else + fail "max25-bcprd / max25-bcprd-init not built (expected default ON — see cmake/MAX25Platform.cmake)" +fi +[[ -f stacks/crdop/share/crdop.ini.example ]] && ok "CRDOP scaffold" || fail "CRDOP scaffold missing" +if [[ -x "${BUILD_DIR}/bin/audio-dummyd" ]]; then + ok "audio-dummyd binary" +else + ok "audio-dummyd not in build dir (launcher uses source tree path)" +fi + +# --- install tree hygiene: no vendor/ARDOP artifacts, no out-of-scope leakage --- +INSTALL_DIR="${BUILD_DIR}/install-release-check" +rm -rf "${INSTALL_DIR}" +if cmake --install "${BUILD_DIR}" --prefix "${INSTALL_DIR}" >/dev/null 2>&1; then + ok "cmake --install prefix" + if find "${INSTALL_DIR}" \( -path '*/vendor/ardopcf/*' -o -name 'ardopcf' -o -name 'crdopc' \) 2>/dev/null | grep -q .; then + fail "ARDOP vendor artifacts in install tree (forbidden in release)" + else + ok "install tree free of ARDOP vendor binaries/sources" + fi + if find "${INSTALL_DIR}" -type d -iname 'hybbx' 2>/dev/null | grep -q .; then + fail "hybbx/ present in install tree (out of scope for Lite)" + else + ok "install tree free of hybbx/ directories" + fi +else + warn "cmake --install skipped (non-fatal for dev trees)" +fi + +# --- offline tests --- +if [[ -x "${BUILD_DIR}/bin/max25-bcprd" ]]; then + if "${BUILD_DIR}/bin/max25-bcprd" -c stacks/max25-bcpr/share/max25-bcpr.ini.example --dry-run --once >/dev/null 2>&1; then + ok "max25-bcpr dry-run once" + else + fail "max25-bcpr dry-run once" + fi +else + warn "max25-bcpr dry-run skipped (not built)" +fi + +if python3 stacks/daemon/test_bcpr_backend.py >/dev/null 2>&1; then + ok "bcpr backend unit tests" +else + fail "bcpr backend unit tests" +fi + +if python3 stacks/daemon/test_crdop_backend.py >/dev/null 2>&1; then + ok "crdop backend unit tests" +else + fail "crdop backend unit tests" +fi + +if python3 stacks/daemon/test_paths.py >/dev/null 2>&1; then + ok "daemon path layout unit tests" +else + fail "daemon path layout unit tests" +fi + +if bash scripts/test-wizard.sh >/dev/null 2>&1; then + ok "wizard non-interactive smoke (serial/baycom/crdop)" +else + fail "wizard non-interactive smoke" +fi + +if bash scripts/discover-plugins.sh --type device >/dev/null 2>&1; then + ok "discover-plugins.sh --type device" +else + warn "discover-plugins.sh --type device (no manifest — expected in Lite, see script output)" +fi + +chmod +x stacks/daemon/max25d stacks/daemon/max25d.py 2>/dev/null || true +[[ -x stacks/daemon/max25d ]] && [[ -f stacks/daemon/max25d.py ]] && ok "max25d ready" || fail "max25d" +if [[ -L "${BUILD_DIR}/bin/max25-client" ]] || [[ -x "${BUILD_DIR}/bin/max25-client" ]]; then + ok "max25-client symlink" +else + warn "max25-client symlink missing until cmake --install" +fi +if MAX25_TERMINAL="${PWD}/${BUILD_DIR}/bin/max25-terminal" \ + bash stacks/terminal/test-terminal.sh >/dev/null 2>&1; then + ok "max25-terminal TCP probe" +else + fail "max25-terminal TCP probe" +fi + +if python3 stacks/daemon/test_proto.py >/dev/null 2>&1; then + ok "max25d protocol smoke" +else + fail "max25d protocol smoke" +fi + +if python3 stacks/daemon/test_auth.py >/dev/null 2>&1; then + ok "max25d TCP auth smoke" +else + fail "max25d TCP auth smoke" +fi + +# --- RX before TX gate (§0.20) — offline check only, no live RF TX here --- +if bash scripts/tx-rx-test.sh >/dev/null 2>&1; then + ok "tx-rx-test L0 offline (RX before TX posture)" +else + fail "tx-rx-test L0 offline" +fi + +if python3 stacks/daemon/test_serial_watch.py >/dev/null 2>&1; then + ok "max25d serial watch tests" +else + fail "max25d serial watch tests" +fi + +# --- AmigaOS terminal (optional cross-build) --- +if [[ -x /opt/amiga/bin/m68k-amigaos-gcc ]]; then + if bash scripts/build-amiga-terminal.sh >/dev/null 2>&1; then + ok "max25-terminal AmigaOS cross-build" + else + fail "max25-terminal AmigaOS cross-build" + fi +else + warn "Amiga SDK not at /opt/amiga; skip Amiga terminal build (optional)" +fi + +# --- CI workflow --- +[[ -f .github/workflows/ci.yml ]] && ok "ci workflow" || warn "missing .github/workflows/ci.yml" + +echo "======== release-check: ${PASS} passed, ${FAIL} failed, ${WARN} warnings ========" +if [[ "$FAIL" -gt 0 ]]; then + exit 1 +fi +exit 0 |
