From 5bb9bc146e195e439fa735ac67bbb74fb4630145 Mon Sep 17 00:00:00 2001 From: Kpa-clawbot Date: Sun, 29 Mar 2026 09:25:51 -0700 Subject: [PATCH] docs: remove letsmesh.net reference from README (#233) * docs: remove letsmesh.net reference from README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: remove paths-ignore from pull_request trigger PR #233 only touches .md files, which were excluded by paths-ignore, causing CI to be skipped entirely. Remove paths-ignore from the pull_request trigger so all PRs get validated. Keep paths-ignore on push to avoid unnecessary deploys for docs-only changes to master. * ci: skip heavy CI jobs for docs-only PRs Instead of using paths-ignore (which skips the entire workflow and blocks required status checks), detect docs-only changes at the start of each job and skip heavy steps while still reporting success. This allows doc-only PRs to merge without waiting for Go builds, Node.js tests, or Playwright E2E runs. Reverts the approach from 7546ece (removing paths-ignore entirely) in favor of a proper conditional skip within the jobs themselves. * fix: update engine tests to match engine-badge HTML format Tests expected [go]/[node] text but formatVersionBadge now renders go. Updated 6 assertions to check for engine-badge class and engine name in HTML output. --------- Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Kpa-clawbot Co-authored-by: you --- .github/workflows/deploy.yml | 63 +++++++++++++++++++++++++++--------- README.md | 2 +- test-e2e-playwright.js | 15 +++++++-- test-frontend-helpers.js | 12 +++---- 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 266e5c4a..61d6ab3f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,11 +10,6 @@ on: - 'docs/**' pull_request: branches: [master] - paths-ignore: - - '**.md' - - 'LICENSE' - - '.gitignore' - - 'docs/**' concurrency: group: deploy-${{ github.event.pull_request.number || github.ref }} @@ -42,8 +37,23 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Skip if docs-only change + id: docs-check + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + NON_DOCS=$(echo "$CHANGED" | grep -cvE '\.(md)$|^LICENSE$|^\.gitignore$|^docs/' || true) + if [ "$NON_DOCS" -eq 0 ]; then + echo "docs_only=true" >> $GITHUB_OUTPUT + echo "๐Ÿ“„ Docs-only PR โ€” skipping heavy CI" + fi + fi - name: Set up Go 1.22 + if: steps.docs-check.outputs.docs_only != 'true' uses: actions/setup-go@v6 with: go-version: '1.22' @@ -52,6 +62,7 @@ jobs: cmd/ingestor/go.sum - name: Build and test Go server (with coverage) + if: steps.docs-check.outputs.docs_only != 'true' run: | set -e -o pipefail cd cmd/server @@ -61,6 +72,7 @@ jobs: go tool cover -func=server-coverage.out | tail -1 - name: Build and test Go ingestor (with coverage) + if: steps.docs-check.outputs.docs_only != 'true' run: | set -e -o pipefail cd cmd/ingestor @@ -70,6 +82,7 @@ jobs: go tool cover -func=ingestor-coverage.out | tail -1 - name: Verify proto syntax (all .proto files compile) + if: steps.docs-check.outputs.docs_only != 'true' run: | set -e echo "Installing protoc..." @@ -84,7 +97,7 @@ jobs: echo "โœ… All .proto files are syntactically valid" - name: Generate Go coverage badges - if: always() + if: always() && steps.docs-check.outputs.docs_only != 'true' run: | mkdir -p .badges @@ -144,21 +157,39 @@ jobs: node-test: name: "๐Ÿงช Node.js Tests" runs-on: [self-hosted, Linux] + defaults: + run: + shell: bash steps: - name: Checkout code uses: actions/checkout@v5 with: - fetch-depth: 2 + fetch-depth: 0 + + - name: Skip if docs-only change + id: docs-check + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + NON_DOCS=$(echo "$CHANGED" | grep -cvE '\.(md)$|^LICENSE$|^\.gitignore$|^docs/' || true) + if [ "$NON_DOCS" -eq 0 ]; then + echo "docs_only=true" >> $GITHUB_OUTPUT + echo "๐Ÿ“„ Docs-only PR โ€” skipping heavy CI" + fi + fi - name: Set up Node.js 22 + if: steps.docs-check.outputs.docs_only != 'true' uses: actions/setup-node@v5 with: node-version: '22' - name: Install npm dependencies + if: steps.docs-check.outputs.docs_only != 'true' run: npm ci --production=false - name: Detect changed files + if: steps.docs-check.outputs.docs_only != 'true' id: changes run: | BACKEND=$(git diff --name-only HEAD~1 | grep -cE '^(server|db|decoder|packet-store|server-helpers|iata-coords)\.js$' || true) @@ -174,7 +205,7 @@ jobs: echo "Changes: backend=$BACKEND frontend=$FRONTEND tests=$TESTS ci=$CI" - name: Run backend tests with coverage - if: steps.changes.outputs.backend == 'true' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.backend == 'true' run: | npx c8 --reporter=text-summary --reporter=text sh test-all.sh 2>&1 | tee test-output.txt @@ -192,11 +223,11 @@ jobs: echo "## Backend: ${TOTAL_PASS} tests, ${BE_COVERAGE}% coverage" >> $GITHUB_STEP_SUMMARY - name: Run backend tests (quick, no coverage) - if: steps.changes.outputs.backend == 'false' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.backend == 'false' run: npm run test:unit - name: Install Playwright browser - if: steps.changes.outputs.frontend == 'true' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: | # Install chromium (skips download if already cached on self-hosted runner) npx playwright install chromium 2>/dev/null || true @@ -204,11 +235,11 @@ jobs: npx playwright install-deps chromium 2>/dev/null || true - name: Instrument frontend JS for coverage - if: steps.changes.outputs.frontend == 'true' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: sh scripts/instrument-frontend.sh - name: Start instrumented test server on port 13581 - if: steps.changes.outputs.frontend == 'true' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: | # Kill any stale server on 13581 fuser -k 13581/tcp 2>/dev/null || true @@ -232,7 +263,7 @@ jobs: done - name: Run Playwright E2E + coverage collection concurrently - if: steps.changes.outputs.frontend == 'true' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: | # Run E2E tests and coverage collection in parallel โ€” both use the same server BASE_URL=http://localhost:13581 node test-e2e-playwright.js 2>&1 | tee e2e-output.txt & @@ -250,7 +281,7 @@ jobs: true - name: Generate frontend coverage badges - if: always() && steps.changes.outputs.frontend == 'true' + if: always() && steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: | E2E_PASS=$(grep -oP '[0-9]+(?=/)' e2e-output.txt | tail -1) @@ -269,7 +300,7 @@ jobs: echo "{\"schemaVersion\":1,\"label\":\"frontend tests\",\"message\":\"${E2E_PASS:-0} E2E passed\",\"color\":\"brightgreen\"}" > .badges/frontend-tests.json - name: Stop test server - if: always() && steps.changes.outputs.frontend == 'true' + if: always() && steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'true' run: | if [ -f .server.pid ]; then kill $(cat .server.pid) 2>/dev/null || true @@ -278,7 +309,7 @@ jobs: fi - name: Run frontend E2E (quick, no coverage) - if: steps.changes.outputs.frontend == 'false' + if: steps.docs-check.outputs.docs_only != 'true' && steps.changes.outputs.frontend == 'false' run: | fuser -k 13581/tcp 2>/dev/null || true PORT=13581 node server.js & diff --git a/README.md b/README.md index cbd0d2c9..fe0bbd73 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ > High-performance mesh network analyzer powered by Go. Sub-millisecond packet queries, ~300 MB memory for 56K+ packets, real-time WebSocket broadcast, full channel decryption. -Self-hosted, open-source MeshCore packet analyzer โ€” a community alternative to the closed-source `analyzer.letsmesh.net`. Collects MeshCore packets via MQTT, decodes them in real time, and presents a full web UI with live packet feed, interactive maps, channel chat, packet tracing, and per-node analytics. +Self-hosted, open-source MeshCore packet analyzer. Collects MeshCore packets via MQTT, decodes them in real time, and presents a full web UI with live packet feed, interactive maps, channel chat, packet tracing, and per-node analytics. ## โšก Performance diff --git a/test-e2e-playwright.js b/test-e2e-playwright.js index 42347337..ef9a6ea4 100644 --- a/test-e2e-playwright.js +++ b/test-e2e-playwright.js @@ -354,10 +354,17 @@ async function run() { await test('Packets clicking row shows detail pane', async () => { // Fresh navigation to avoid stale row references from previous test await page.goto(`${BASE}/#/packets`, { waitUntil: 'domcontentloaded' }); + // Wait for table rows AND initial API data to settle await page.waitForSelector('table tbody tr[data-action]', { timeout: 15000 }); + await page.waitForLoadState('networkidle'); const firstRow = await page.$('table tbody tr[data-action]'); assert(firstRow, 'No clickable packet rows found'); - await firstRow.click(); + // Click the row and wait for the /packets/{hash} API response + const [response] = await Promise.all([ + page.waitForResponse(resp => resp.url().includes('/packets/') && resp.status() === 200, { timeout: 15000 }), + firstRow.click(), + ]); + assert(response, 'API response for packet detail not received'); await page.waitForFunction(() => { const panel = document.getElementById('pktRight'); return panel && !panel.classList.contains('empty'); @@ -375,12 +382,16 @@ async function run() { if (!pktRight) { await page.goto(`${BASE}/#/packets`, { waitUntil: 'domcontentloaded' }); await page.waitForSelector('table tbody tr[data-action]', { timeout: 15000 }); + await page.waitForLoadState('networkidle'); } const panelOpen = await page.$eval('#pktRight', el => !el.classList.contains('empty')); if (!panelOpen) { const firstRow = await page.$('table tbody tr[data-action]'); if (!firstRow) { console.log(' โญ๏ธ Skipped (no clickable rows)'); return; } - await firstRow.click(); + await Promise.all([ + page.waitForResponse(resp => resp.url().includes('/packets/') && resp.status() === 200, { timeout: 15000 }), + firstRow.click(), + ]); await page.waitForFunction(() => { const panel = document.getElementById('pktRight'); return panel && !panel.classList.contains('empty'); diff --git a/test-frontend-helpers.js b/test-frontend-helpers.js index 026860e3..e1ec9a31 100644 --- a/test-frontend-helpers.js +++ b/test-frontend-helpers.js @@ -1322,7 +1322,7 @@ console.log('\n=== app.js: formatVersionBadge ==='); assert.ok(result.includes('>v2.6.0'), 'version text has v prefix'); assert.ok(result.includes(`href="${GH}/commit/abc1234def5678"`), 'commit links to full hash'); assert.ok(result.includes('>abc1234'), 'commit display is truncated to 7'); - assert.ok(result.includes('[node]'), 'should show engine'); + assert.ok(result.includes('engine-badge'), 'should show engine badge'); assert.ok(result.includes('>node<'), 'should show engine name'); }); test('prod port 80: shows version', () => { const { formatVersionBadge } = makeBadgeSandbox('80'); @@ -1348,7 +1348,7 @@ console.log('\n=== app.js: formatVersionBadge ==='); assert.ok(!result.includes('v2.6.0'), 'staging should NOT show version'); assert.ok(result.includes('>abc1234'), 'should show commit hash'); assert.ok(result.includes(`href="${GH}/commit/abc1234def5678"`), 'commit is linked'); - assert.ok(result.includes('[go]'), 'should show engine'); + assert.ok(result.includes('engine-badge'), 'should show engine badge'); assert.ok(result.includes('>go<'), 'should show engine name'); }); test('staging port 81: hides version', () => { const { formatVersionBadge } = makeBadgeSandbox('81'); @@ -1369,18 +1369,18 @@ console.log('\n=== app.js: formatVersionBadge ==='); const result = formatVersionBadge('2.6.0', 'unknown', 'node'); assert.ok(result.includes('>v2.6.0'), 'should show version'); assert.ok(!result.includes('unknown'), 'should not show unknown commit'); - assert.ok(result.includes('[node]'), 'should show engine'); + assert.ok(result.includes('engine-badge'), 'should show engine badge'); assert.ok(result.includes('>node<'), 'should show engine name'); }); test('skips commit when missing', () => { const { formatVersionBadge } = makeBadgeSandbox(''); const result = formatVersionBadge('2.6.0', null, 'go'); assert.ok(result.includes('>v2.6.0'), 'should show version'); - assert.ok(result.includes('[go]'), 'should show engine'); + assert.ok(result.includes('engine-badge'), 'should show engine badge'); assert.ok(result.includes('>go<'), 'should show engine name'); }); test('shows only engine when version/commit missing', () => { const { formatVersionBadge } = makeBadgeSandbox('3000'); const result = formatVersionBadge(null, null, 'go'); - assert.ok(result.includes('[go]'), 'should show engine'); + assert.ok(result.includes('engine-badge'), 'should show engine badge'); assert.ok(result.includes('>go<'), 'should show engine name'); assert.ok(result.includes('version-badge'), 'should use version-badge class'); }); test('short commit not truncated in display', () => { @@ -1398,7 +1398,7 @@ console.log('\n=== app.js: formatVersionBadge ==='); const { formatVersionBadge } = makeBadgeSandbox('8080'); const result = formatVersionBadge('2.6.0', null, 'go'); assert.ok(!result.includes('2.6.0'), 'no version on staging'); - assert.ok(result.includes('[go]'), 'engine shown'); + assert.ok(result.includes('engine-badge'), 'engine badge shown'); assert.ok(result.includes('>go<'), 'engine name shown'); }); }