- Rename flash-pi.sh → flash-stock-img.sh for clarity - Add 16GB partition sizing option (faster imaging) - Disable Pi OS auto-expand to preserve partition size - Add pip-audit security check to release validation - Add config.json.example, gitignore actual config 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
335 lines
11 KiB
Bash
Executable File
335 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# =============================================================================
|
|
# Stegasoo Release Validation Script
|
|
# =============================================================================
|
|
# Automated pre-release validation to catch issues before tagging a release.
|
|
#
|
|
# Usage:
|
|
# ./scripts/validate-release.sh # Local validation only
|
|
# ./scripts/validate-release.sh --pi # Include Pi smoke test
|
|
# PI_IP=192.168.0.4 ./scripts/validate-release.sh --pi
|
|
#
|
|
# Exit codes:
|
|
# 0 = All tests passed
|
|
# 1 = One or more tests failed
|
|
# =============================================================================
|
|
|
|
# Don't use set -e as we need to handle test failures gracefully
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
# Default Pi IP (can be overridden via environment)
|
|
PI_IP="${PI_IP:-192.168.0.4}"
|
|
PI_USER="${PI_USER:-alee}"
|
|
INCLUDE_PI=false
|
|
INCLUDE_DOCKER=true
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--pi)
|
|
INCLUDE_PI=true
|
|
shift
|
|
;;
|
|
--no-docker)
|
|
INCLUDE_DOCKER=false
|
|
shift
|
|
;;
|
|
--help|-h)
|
|
echo "Usage: $0 [--pi] [--no-docker]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --pi Include Pi smoke test (requires SSH access)"
|
|
echo " --no-docker Skip Docker build/test"
|
|
echo ""
|
|
echo "Environment:"
|
|
echo " PI_IP Pi IP address (default: 192.168.0.4)"
|
|
echo " PI_USER Pi SSH user (default: alee)"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Track results
|
|
TESTS_RUN=0
|
|
TESTS_PASSED=0
|
|
TESTS_FAILED=0
|
|
FAILED_TESTS=()
|
|
|
|
# Helper functions
|
|
pass() {
|
|
echo -e "${GREEN}[PASS]${NC} $1"
|
|
((TESTS_PASSED++))
|
|
((TESTS_RUN++))
|
|
}
|
|
|
|
fail() {
|
|
echo -e "${RED}[FAIL]${NC} $1"
|
|
FAILED_TESTS+=("$1")
|
|
((TESTS_FAILED++))
|
|
((TESTS_RUN++))
|
|
}
|
|
|
|
skip() {
|
|
echo -e "${YELLOW}[SKIP]${NC} $1"
|
|
}
|
|
|
|
section() {
|
|
echo ""
|
|
echo -e "${CYAN}━━━ $1 ━━━${NC}"
|
|
}
|
|
|
|
# =============================================================================
|
|
# Header
|
|
# =============================================================================
|
|
echo -e "${CYAN}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
|
echo -e "${CYAN}║ Stegasoo Release Validation ║${NC}"
|
|
echo -e "${CYAN}╚═══════════════════════════════════════════════════════════════╝${NC}"
|
|
echo ""
|
|
|
|
# Get version from pyproject.toml
|
|
VERSION=$(grep '^version = ' pyproject.toml | head -1 | cut -d'"' -f2)
|
|
echo -e "Version: ${YELLOW}${VERSION}${NC}"
|
|
echo -e "Branch: ${YELLOW}$(git branch --show-current)${NC}"
|
|
echo ""
|
|
|
|
# =============================================================================
|
|
# 1. Code Quality Checks
|
|
# =============================================================================
|
|
section "Code Quality"
|
|
|
|
# Ruff linting
|
|
if command -v ./venv/bin/ruff &> /dev/null; then
|
|
echo -n "Running ruff check... "
|
|
if ./venv/bin/ruff check src/ frontends/ --quiet 2>/dev/null; then
|
|
pass "Ruff linting"
|
|
else
|
|
fail "Ruff linting (run: ./venv/bin/ruff check src/ frontends/)"
|
|
fi
|
|
else
|
|
skip "Ruff not installed"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 2. Security Audit
|
|
# =============================================================================
|
|
section "Security Audit"
|
|
|
|
# pip-audit for known vulnerabilities
|
|
if command -v ./venv/bin/pip-audit &> /dev/null; then
|
|
echo -n "Running pip-audit... "
|
|
if ./venv/bin/pip-audit --quiet 2>/dev/null; then
|
|
pass "No known vulnerabilities"
|
|
else
|
|
fail "pip-audit found vulnerabilities (run: ./venv/bin/pip-audit)"
|
|
fi
|
|
else
|
|
echo -n "Installing pip-audit... "
|
|
if ./venv/bin/pip install pip-audit --quiet 2>/dev/null; then
|
|
echo -n "Running pip-audit... "
|
|
if ./venv/bin/pip-audit --quiet 2>/dev/null; then
|
|
pass "No known vulnerabilities"
|
|
else
|
|
fail "pip-audit found vulnerabilities (run: ./venv/bin/pip-audit)"
|
|
fi
|
|
else
|
|
skip "Could not install pip-audit"
|
|
fi
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 3. Unit Tests (if they exist)
|
|
# =============================================================================
|
|
section "Unit Tests"
|
|
|
|
if ls tests/test_*.py 1> /dev/null 2>&1; then
|
|
echo -n "Running pytest... "
|
|
if ./venv/bin/pytest tests/ -q --tb=no 2>/dev/null; then
|
|
pass "Pytest unit tests"
|
|
else
|
|
fail "Pytest unit tests"
|
|
fi
|
|
else
|
|
skip "No unit tests found (tests/test_*.py)"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 4. Import Tests
|
|
# =============================================================================
|
|
section "Import Tests"
|
|
|
|
# Test core library import
|
|
echo -n "Testing stegasoo import... "
|
|
if ./venv/bin/python -c "from stegasoo import encode, decode; print('OK')" 2>/dev/null | grep -q OK; then
|
|
pass "Core library import"
|
|
else
|
|
fail "Core library import"
|
|
fi
|
|
|
|
# Test DCT support
|
|
echo -n "Testing DCT support... "
|
|
if ./venv/bin/python -c "from stegasoo import has_dct_support; assert has_dct_support(), 'No DCT'; print('OK')" 2>/dev/null | grep -q OK; then
|
|
pass "DCT support available"
|
|
else
|
|
fail "DCT support (scipy/jpegio missing?)"
|
|
fi
|
|
|
|
# Test CLI import
|
|
echo -n "Testing CLI import... "
|
|
if ./venv/bin/python -c "from stegasoo.cli import main; print('OK')" 2>/dev/null | grep -q OK; then
|
|
pass "CLI module import"
|
|
else
|
|
fail "CLI module import"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 5. Encode/Decode Sanity Test
|
|
# =============================================================================
|
|
section "Encode/Decode Test"
|
|
|
|
echo -n "Running encode/decode sanity check... "
|
|
SANITY_RESULT=$(./venv/bin/python << 'EOF' 2>&1
|
|
import sys
|
|
sys.path.insert(0, 'src')
|
|
from stegasoo import encode, decode
|
|
|
|
with open('test_data/carrier.jpg', 'rb') as f:
|
|
carrier = f.read()
|
|
with open('test_data/ref.jpg', 'rb') as f:
|
|
ref = f.read()
|
|
|
|
# LSB test
|
|
result = encode(message="sanity test", reference_photo=ref, carrier_image=carrier,
|
|
passphrase="test", pin="123456", embed_mode="lsb")
|
|
decoded = decode(stego_image=result.stego_image, reference_photo=ref,
|
|
passphrase="test", pin="123456", embed_mode="lsb")
|
|
assert decoded.message == "sanity test", f"LSB mismatch: {decoded.message}"
|
|
|
|
# DCT test
|
|
result = encode(message="dct sanity", reference_photo=ref, carrier_image=carrier,
|
|
passphrase="dct", pin="654321", embed_mode="dct")
|
|
decoded = decode(stego_image=result.stego_image, reference_photo=ref,
|
|
passphrase="dct", pin="654321", embed_mode="dct")
|
|
assert decoded.message == "dct sanity", f"DCT mismatch: {decoded.message}"
|
|
|
|
print("OK")
|
|
EOF
|
|
)
|
|
|
|
if echo "$SANITY_RESULT" | grep -q "OK"; then
|
|
pass "Encode/decode sanity (LSB + DCT)"
|
|
else
|
|
fail "Encode/decode sanity: $SANITY_RESULT"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 6. Docker Build & Test (optional)
|
|
# =============================================================================
|
|
if $INCLUDE_DOCKER; then
|
|
section "Docker"
|
|
|
|
if command -v docker &> /dev/null || command -v sudo &> /dev/null; then
|
|
DOCKER_CMD="docker"
|
|
if ! docker info &>/dev/null 2>&1; then
|
|
DOCKER_CMD="sudo docker"
|
|
fi
|
|
|
|
echo -n "Building Docker image... "
|
|
if $DOCKER_CMD build -t stegasoo:validate -q . >/dev/null 2>&1; then
|
|
pass "Docker build"
|
|
|
|
# Test container starts
|
|
echo -n "Testing container startup... "
|
|
CONTAINER_ID=$($DOCKER_CMD run -d -p 15000:5000 stegasoo:validate 2>/dev/null)
|
|
sleep 3
|
|
|
|
if curl -s -o /dev/null -w "%{http_code}" http://localhost:15000/ 2>/dev/null | grep -qE "200|302"; then
|
|
pass "Container responds to HTTP"
|
|
else
|
|
fail "Container HTTP response"
|
|
fi
|
|
|
|
# Cleanup
|
|
$DOCKER_CMD stop "$CONTAINER_ID" >/dev/null 2>&1 || true
|
|
$DOCKER_CMD rm "$CONTAINER_ID" >/dev/null 2>&1 || true
|
|
else
|
|
fail "Docker build"
|
|
fi
|
|
|
|
# Cleanup test image
|
|
$DOCKER_CMD rmi stegasoo:validate >/dev/null 2>&1 || true
|
|
else
|
|
skip "Docker not available"
|
|
fi
|
|
else
|
|
skip "Docker tests (use --docker to enable)"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# 7. Pi Smoke Test (optional)
|
|
# =============================================================================
|
|
if $INCLUDE_PI; then
|
|
section "Pi Smoke Test"
|
|
|
|
echo -n "Testing SSH connectivity to $PI_USER@$PI_IP... "
|
|
if ssh -o ConnectTimeout=5 -o BatchMode=yes "$PI_USER@$PI_IP" "echo OK" 2>/dev/null | grep -q OK; then
|
|
pass "SSH connectivity"
|
|
|
|
echo -n "Checking stegasoo service status... "
|
|
if ssh "$PI_USER@$PI_IP" "systemctl is-active stegasoo" 2>/dev/null | grep -q active; then
|
|
pass "Stegasoo service running"
|
|
|
|
echo -n "Running smoke test on Pi... "
|
|
SMOKE_RESULT=$(ssh "$PI_USER@$PI_IP" "cd /home/$PI_USER/stegasoo && bash tests/smoke-test.sh --quick 2>&1" || echo "FAILED")
|
|
if echo "$SMOKE_RESULT" | grep -qE "All tests passed|PASS"; then
|
|
pass "Pi smoke test"
|
|
else
|
|
fail "Pi smoke test"
|
|
fi
|
|
else
|
|
fail "Stegasoo service not running"
|
|
fi
|
|
else
|
|
fail "SSH connectivity to Pi"
|
|
fi
|
|
else
|
|
skip "Pi smoke test (use --pi to enable)"
|
|
fi
|
|
|
|
# =============================================================================
|
|
# Summary
|
|
# =============================================================================
|
|
echo ""
|
|
echo -e "${CYAN}━━━ Summary ━━━${NC}"
|
|
echo ""
|
|
echo -e "Tests run: ${TESTS_RUN}"
|
|
echo -e "Passed: ${GREEN}${TESTS_PASSED}${NC}"
|
|
echo -e "Failed: ${RED}${TESTS_FAILED}${NC}"
|
|
|
|
if [ ${#FAILED_TESTS[@]} -gt 0 ]; then
|
|
echo ""
|
|
echo -e "${RED}Failed tests:${NC}"
|
|
for test in "${FAILED_TESTS[@]}"; do
|
|
echo -e " - $test"
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
if [ $TESTS_FAILED -eq 0 ]; then
|
|
echo -e "${GREEN}✓ All validation checks passed!${NC}"
|
|
echo -e " Ready to tag release ${VERSION}"
|
|
exit 0
|
|
else
|
|
echo -e "${RED}✗ Validation failed - fix issues before release${NC}"
|
|
exit 1
|
|
fi
|