summaryrefslogtreecommitdiff
path: root/scripts/release-check.sh
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/release-check.sh')
-rwxr-xr-xscripts/release-check.sh258
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
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com