name: Deploy on: push: branches: [master] paths-ignore: - '**.md' - 'LICENSE' - '.gitignore' - 'docs/**' concurrency: group: deploy cancel-in-progress: true jobs: test: runs-on: self-hosted steps: - uses: actions/checkout@v4 with: fetch-depth: 2 - uses: actions/setup-node@v4 with: node-version: '22' - name: Install dependencies run: npm ci --production=false - name: Detect changes id: changes run: | BACKEND=$(git diff --name-only HEAD~1 | grep -cE '^(server|db|decoder|packet-store|server-helpers|iata-coords)\.js$' || true) FRONTEND=$(git diff --name-only HEAD~1 | grep -cE '^public/' || true) TESTS=$(git diff --name-only HEAD~1 | grep -cE '^test-|^tools/' || true) CI=$(git diff --name-only HEAD~1 | grep -cE '\.github/|package\.json|test-all\.sh|scripts/' || true) # If CI/test infra changed, run everything if [ "$CI" -gt 0 ]; then BACKEND=1; FRONTEND=1; fi # If test files changed, run everything if [ "$TESTS" -gt 0 ]; then BACKEND=1; FRONTEND=1; fi echo "backend=$([[ $BACKEND -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT echo "frontend=$([[ $FRONTEND -gt 0 ]] && echo true || echo false)" >> $GITHUB_OUTPUT echo "Changes: backend=$BACKEND frontend=$FRONTEND tests=$TESTS ci=$CI" - name: Backend tests + coverage if: steps.changes.outputs.backend == 'true' run: | npx c8 --reporter=text-summary --reporter=text sh test-all.sh 2>&1 | tee test-output.txt TOTAL_PASS=$(grep -oP '\d+(?= passed)' test-output.txt | awk '{s+=$1} END {print s}') TOTAL_FAIL=$(grep -oP '\d+(?= failed)' test-output.txt | awk '{s+=$1} END {print s}') BE_COVERAGE=$(grep 'Statements' test-output.txt | tail -1 | grep -oP '[\d.]+(?=%)') mkdir -p .badges BE_COLOR="red" [ "$(echo "$BE_COVERAGE > 60" | bc -l 2>/dev/null)" = "1" ] && BE_COLOR="yellow" [ "$(echo "$BE_COVERAGE > 80" | bc -l 2>/dev/null)" = "1" ] && BE_COLOR="brightgreen" echo "{\"schemaVersion\":1,\"label\":\"backend tests\",\"message\":\"${TOTAL_PASS} passed\",\"color\":\"brightgreen\"}" > .badges/backend-tests.json echo "{\"schemaVersion\":1,\"label\":\"backend coverage\",\"message\":\"${BE_COVERAGE}%\",\"color\":\"${BE_COLOR}\"}" > .badges/backend-coverage.json echo "## Backend: ${TOTAL_PASS} tests, ${BE_COVERAGE}% coverage" >> $GITHUB_STEP_SUMMARY - name: Backend tests (quick — no coverage) if: steps.changes.outputs.backend == 'false' run: npm run test:unit - name: Install Playwright browser if: steps.changes.outputs.frontend == 'true' run: npx playwright install chromium --with-deps 2>/dev/null || true - name: Frontend coverage (instrumented Playwright) if: steps.changes.outputs.frontend == 'true' run: | sh scripts/instrument-frontend.sh COVERAGE=1 PORT=13581 node server.js & SERVER_PID=$! sleep 5 BASE_URL=http://localhost:13581 node test-e2e-playwright.js 2>&1 | tee e2e-output.txt E2E_PASS=$(grep -oP '[0-9]+(?=/)' e2e-output.txt | tail -1) BASE_URL=http://localhost:13581 node scripts/collect-frontend-coverage.js 2>&1 | tee fe-coverage-output.txt kill $SERVER_PID 2>/dev/null || true mkdir -p .badges if [ -f .nyc_output/frontend-coverage.json ]; then npx nyc report --reporter=text-summary --reporter=text 2>&1 | tee fe-report.txt FE_COVERAGE=$(grep 'Statements' fe-report.txt | head -1 | grep -oP '[\d.]+(?=%)' || echo "0") FE_COVERAGE=${FE_COVERAGE:-0} FE_COLOR="red" [ "$(echo "$FE_COVERAGE > 50" | bc -l 2>/dev/null)" = "1" ] && FE_COLOR="yellow" [ "$(echo "$FE_COVERAGE > 80" | bc -l 2>/dev/null)" = "1" ] && FE_COLOR="brightgreen" echo "{\"schemaVersion\":1,\"label\":\"frontend coverage\",\"message\":\"${FE_COVERAGE}%\",\"color\":\"${FE_COLOR}\"}" > .badges/frontend-coverage.json echo "## Frontend: ${FE_COVERAGE}% coverage" >> $GITHUB_STEP_SUMMARY fi echo "{\"schemaVersion\":1,\"label\":\"frontend tests\",\"message\":\"${E2E_PASS:-0} E2E passed\",\"color\":\"brightgreen\"}" > .badges/frontend-tests.json - name: Frontend E2E only (no coverage) if: steps.changes.outputs.frontend == 'false' run: | PORT=13581 node server.js & SERVER_PID=$! sleep 5 BASE_URL=http://localhost:13581 node test-e2e-playwright.js || true kill $SERVER_PID 2>/dev/null || true - name: Publish badges if: always() continue-on-error: true run: | git config user.name "github-actions" git config user.email "actions@github.com" git remote set-url origin https://x-access-token:${{ github.token }}@github.com/${{ github.repository }}.git git add .badges/ -f git diff --cached --quiet || (git commit -m "ci: update test badges [skip ci]" && git push) || echo "Badge push failed" deploy: needs: test runs-on: self-hosted steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' - name: Validate JS run: sh scripts/validate.sh - name: Build and deploy run: | set -e docker build -t meshcore-analyzer . docker rm -f meshcore-analyzer 2>/dev/null || true docker run -d \ --name meshcore-analyzer \ --restart unless-stopped \ -p 80:80 -p 443:443 -p 1883:1883 \ -v $HOME/meshcore-data:/app/data \ -v $HOME/meshcore-config.json:/app/config.json:ro \ -v $HOME/caddy-data:/data/caddy \ -v $HOME/meshcore-analyzer/Caddyfile:/etc/caddy/Caddyfile \ meshcore-analyzer echo "Deployed $(git rev-parse --short HEAD)"