mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-11 01:06:53 +00:00
b03ef4abd3
## Summary Resolves the **5 layout bugs** documented in `specs/packets-layout-audit.md` from the issue investigation. All fixes shipped in one PR per the audit's recommended fix order. Fixes #1128 ### Bug 4 (P0, 1 line) — `--surface` undefined `var(--surface)` was referenced in **8** rules across `style.css` (`.fux-saved-menu`, `.fux-popover`, `.path-popover`, `.fux-ac-dropdown`, `.fux-ctx-menu`, `.path-overflow-pill:hover`, `.fux-saved-trigger:hover`, `.fux-popover-header sticky`) but the variable was **never defined** — every caller resolved to `transparent` and row content bled through. Aliased `--surface: var(--surface-1);` in the `:root`, `@media (prefers-color-scheme: dark)`, and `[data-theme="dark"]` blocks. ### Z-index scale (foundational) Added documented custom properties at the top of `style.css`: ```css --z-base: 0; --z-dropdown: 100; --z-popover: 300; --z-modal-backdrop: 9000; --z-modal: 9100; --z-tooltip: 9200; ``` New code uses these tokens. Existing working values left in place to avoid behavioural risk. ### Bug 1 — path chip re-measure `_finalizePathOverflow` runs **before** `hop-resolver` mutates chip text from hex prefix → longer node name. Chips that fit on first measurement overflow once names resolve, but the `+N` pill never gets appended. Cleared the per-host `overflowChecked` guard and re-ran finalize on a 120 ms debounced timer, so post-resolution overflow is detected. ### Bug 2 — `+N` popover position + z-index `.path-popover` was `z-index: 10500` (above the modal stack) and only ever positioned **below** the pill — when near the bottom of the viewport it hung over adjacent rows. Lowered to `var(--z-popover)` (300), capped `max-height` from `60vh` → `240px`, and added flip-above logic when there isn't room below. ### Bug 3 — filter-bar gap + multi-select truncation `.filter-bar { row-gap: 6px }` was too tight for the 34px controls; bumped to `12px`. `.multi-select-trigger` had no `max-width`, so a selection like `"TRACE,MULTIPART,GRP_TXT"` ballooned the row and overlapped toolbar buttons. Capped `max-width: 180px` with `text-overflow: ellipsis` and surfaced the full selection in the trigger's `title` attribute (so the value remains discoverable). ### Bug 5 — already addressed in #1124 Verified `.filter-group` structure prevents mid-cluster wrap; no further change needed here. ## TDD Branch shows the required **red → green** sequence: | commit | result | |---|---| | `8ad6394` test(packets): red E2E for issue #1128 layout chaos | ✗ Bug 4 (alpha=0), ✗ Bug 2 (z=10500), ✗ Bug 3 (gap=6) | | `eacadc1` fix(packets): resolve --surface undefined + z-index scale + ... | ✓ 5/5 | Test file: `test-issue-1128-packets-layout-e2e.js` — asserts opaque dropdown background, every overflowing `.path-hops` has a `+N` pill, popover z-index ≤ 9000 + anchored to pill, filter-bar gap ≥ 10px, trigger `max-width` bounded. ## E2E Local run against the e2e fixture: ``` === #1128 packets layout E2E === ✓ navigate to /packets and wait for table + rows ✓ Bug 4: Saved-filter dropdown background is OPAQUE (alpha ≥ 0.99) ✓ Bug 1: every overflowing .path-hops has a .path-overflow-pill ✓ Bug 2: +N popover anchored to pill + z-index ≤ 9000 ✓ Bug 3: .filter-bar row-gap ≥ 10px AND .multi-select-trigger has bounded max-width === Results: passed 5 failed 0 === ``` CI hookup: please add `node test-issue-1128-packets-layout-e2e.js` alongside the other `test-issue-XXXX-*-e2e.js` invocations in `.github/workflows/deploy.yml` (line ~226). ## Files - `public/style.css` — `--surface` definition × 3 blocks, z-index scale tokens, `.path-popover`, `.filter-bar`, `.multi-select-trigger` - `public/packets.js` — flip-above popover logic, debounced re-finalize, trigger `title` - `test-issue-1128-packets-layout-e2e.js` — new E2E (red → green) --------- Co-authored-by: openclaw-bot <bot@openclaw.local> Co-authored-by: Kpa-clawbot <kpa-clawbot@users.noreply.github.com>
535 lines
22 KiB
YAML
535 lines
22 KiB
YAML
name: CI/CD Pipeline
|
|
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
tags: ['v*']
|
|
pull_request:
|
|
branches: [master]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
|
|
concurrency:
|
|
group: ci-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
STAGING_COMPOSE_FILE: docker-compose.staging.yml
|
|
STAGING_SERVICE: staging-go
|
|
STAGING_CONTAINER: corescope-staging-go
|
|
|
|
# Pipeline (sequential, fail-fast):
|
|
# go-test → e2e-test → build-and-publish → deploy → publish-badges
|
|
# PRs stop after build-and-publish (no GHCR push). Master continues to deploy + badges.
|
|
|
|
jobs:
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 1. Go Build & Test
|
|
# ───────────────────────────────────────────────────────────────
|
|
go-test:
|
|
name: "✅ Go Build & Test"
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Clean Go module cache
|
|
run: rm -rf ~/go/pkg/mod 2>/dev/null || true
|
|
|
|
- name: Set up Go 1.22
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version: '1.22'
|
|
cache-dependency-path: |
|
|
cmd/server/go.sum
|
|
cmd/ingestor/go.sum
|
|
|
|
- name: Build and test Go server (with coverage)
|
|
run: |
|
|
set -e -o pipefail
|
|
cd cmd/server
|
|
go build .
|
|
go test -coverprofile=server-coverage.out ./... 2>&1 | tee server-test.log
|
|
echo "--- Go Server Coverage ---"
|
|
go tool cover -func=server-coverage.out | tail -1
|
|
|
|
- name: Build and test Go ingestor (with coverage)
|
|
run: |
|
|
set -e -o pipefail
|
|
cd cmd/ingestor
|
|
go build .
|
|
go test -coverprofile=ingestor-coverage.out ./... 2>&1 | tee ingestor-test.log
|
|
echo "--- Go Ingestor Coverage ---"
|
|
go tool cover -func=ingestor-coverage.out | tail -1
|
|
|
|
- name: Build and test channel library + decrypt CLI
|
|
run: |
|
|
set -e -o pipefail
|
|
cd internal/channel
|
|
go test ./...
|
|
echo "--- Channel library tests passed ---"
|
|
cd ../../cmd/decrypt
|
|
CGO_ENABLED=0 go build -ldflags="-s -w" -o corescope-decrypt .
|
|
go test ./...
|
|
echo "--- Decrypt CLI tests passed ---"
|
|
|
|
- name: Run JS unit tests (packet-filter)
|
|
run: |
|
|
set -e
|
|
node test-packet-filter.js
|
|
node test-packet-filter-time.js
|
|
node test-channel-decrypt-insecure-context.js
|
|
node test-live-region-filter.js
|
|
node test-channel-qr.js
|
|
node test-channel-qr-wiring.js
|
|
node test-channel-modal-ux.js
|
|
node test-channel-issue-1087.js
|
|
node test-channel-issue-1101.js
|
|
node test-pull-to-reconnect-1091.js
|
|
node test-channel-fluid-layout.js
|
|
|
|
- name: Verify proto syntax
|
|
run: |
|
|
set -e
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y protobuf-compiler
|
|
for proto in proto/*.proto; do
|
|
echo " ✓ $(basename "$proto")"
|
|
protoc --proto_path=proto --descriptor_set_out=/dev/null "$proto"
|
|
done
|
|
echo "✅ All .proto files are syntactically valid"
|
|
|
|
- name: Generate Go coverage badges
|
|
if: success()
|
|
run: |
|
|
mkdir -p .badges
|
|
|
|
SERVER_COV="0"
|
|
if [ -f cmd/server/server-coverage.out ]; then
|
|
SERVER_COV=$(cd cmd/server && go tool cover -func=server-coverage.out | tail -1 | grep -oP '[\d.]+(?=%)')
|
|
fi
|
|
SERVER_COLOR="red"
|
|
if [ "$(echo "$SERVER_COV >= 80" | bc -l 2>/dev/null)" = "1" ]; then SERVER_COLOR="green"
|
|
elif [ "$(echo "$SERVER_COV >= 60" | bc -l 2>/dev/null)" = "1" ]; then SERVER_COLOR="yellow"; fi
|
|
echo "{\"schemaVersion\":1,\"label\":\"go server coverage\",\"message\":\"${SERVER_COV}%\",\"color\":\"${SERVER_COLOR}\"}" > .badges/go-server-coverage.json
|
|
|
|
INGESTOR_COV="0"
|
|
if [ -f cmd/ingestor/ingestor-coverage.out ]; then
|
|
INGESTOR_COV=$(cd cmd/ingestor && go tool cover -func=ingestor-coverage.out | tail -1 | grep -oP '[\d.]+(?=%)')
|
|
fi
|
|
INGESTOR_COLOR="red"
|
|
if [ "$(echo "$INGESTOR_COV >= 80" | bc -l 2>/dev/null)" = "1" ]; then INGESTOR_COLOR="green"
|
|
elif [ "$(echo "$INGESTOR_COV >= 60" | bc -l 2>/dev/null)" = "1" ]; then INGESTOR_COLOR="yellow"; fi
|
|
echo "{\"schemaVersion\":1,\"label\":\"go ingestor coverage\",\"message\":\"${INGESTOR_COV}%\",\"color\":\"${INGESTOR_COLOR}\"}" > .badges/go-ingestor-coverage.json
|
|
|
|
echo "## Go Coverage" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Module | Coverage |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|--------|----------|" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Server | ${SERVER_COV}% |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Ingestor | ${INGESTOR_COV}% |" >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Upload Go coverage badges
|
|
if: success()
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: go-badges
|
|
path: .badges/go-*.json
|
|
retention-days: 1
|
|
if-no-files-found: ignore
|
|
include-hidden-files: true
|
|
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 2. Playwright E2E Tests (against Go server with fixture DB)
|
|
# ───────────────────────────────────────────────────────────────
|
|
e2e-test:
|
|
name: "🎭 Playwright E2E Tests"
|
|
needs: [go-test]
|
|
runs-on: ubuntu-latest
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- name: Set up Node.js 22
|
|
uses: actions/setup-node@v5
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Clean Go module cache
|
|
run: rm -rf ~/go/pkg/mod 2>/dev/null || true
|
|
|
|
- name: Set up Go 1.22
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version: '1.22'
|
|
cache-dependency-path: cmd/server/go.sum
|
|
|
|
- name: Build Go server
|
|
run: |
|
|
cd cmd/server
|
|
go build -o ../../corescope-server .
|
|
echo "Go server built successfully"
|
|
|
|
- name: Install npm dependencies
|
|
run: npm ci --production=false
|
|
|
|
- name: Install Playwright browser
|
|
run: |
|
|
npx playwright install chromium 2>/dev/null || true
|
|
npx playwright install-deps chromium 2>/dev/null || true
|
|
|
|
- name: Instrument frontend JS for coverage
|
|
run: sh scripts/instrument-frontend.sh
|
|
|
|
- name: Freshen fixture timestamps
|
|
run: bash tools/freshen-fixture.sh test-fixtures/e2e-fixture.db
|
|
|
|
- name: Start Go server with fixture DB
|
|
run: |
|
|
fuser -k 13581/tcp 2>/dev/null || true
|
|
sleep 1
|
|
./corescope-server -port 13581 -db test-fixtures/e2e-fixture.db -public public-instrumented &
|
|
echo $! > .server.pid
|
|
for i in $(seq 1 30); do
|
|
if curl -sf http://localhost:13581/api/healthz > /dev/null 2>&1; then
|
|
echo "Server ready after ${i}s"
|
|
break
|
|
fi
|
|
if [ "$i" -eq 30 ]; then
|
|
echo "Server failed to start within 30s"
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
- name: Run Playwright E2E tests (fail-fast)
|
|
run: |
|
|
BASE_URL=http://localhost:13581 node test-e2e-playwright.js 2>&1 | tee e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-filter-ux-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-channel-issue-1087-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-channel-issue-1111-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-map-modal-fluid-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-nav-fluid-1055-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-nav-priority-1102-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-channel-fluid-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-table-fluid-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-issue-1122-packets-filter-ux-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
BASE_URL=http://localhost:13581 node test-issue-1128-packets-layout-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
|
|
- name: Collect frontend coverage (parallel)
|
|
if: success() && github.event_name == 'push'
|
|
run: |
|
|
BASE_URL=http://localhost:13581 node scripts/collect-frontend-coverage.js 2>&1 | tee fe-coverage-output.txt || true
|
|
|
|
- name: Generate frontend coverage badges
|
|
if: success()
|
|
run: |
|
|
E2E_PASS=$(grep -oP '[0-9]+(?=/)' e2e-output.txt | tail -1 || echo "0")
|
|
|
|
mkdir -p .badges
|
|
if [ -f .nyc_output/frontend-coverage.json ] || [ -f .nyc_output/e2e-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\":\"e2e tests\",\"message\":\"${E2E_PASS:-0} passed\",\"color\":\"brightgreen\"}" > .badges/e2e-tests.json
|
|
|
|
- name: Stop test server
|
|
if: always()
|
|
run: |
|
|
if [ -f .server.pid ]; then
|
|
kill $(cat .server.pid) 2>/dev/null || true
|
|
rm -f .server.pid
|
|
fi
|
|
|
|
- name: Upload E2E badges
|
|
if: success()
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: e2e-badges
|
|
path: .badges/
|
|
retention-days: 1
|
|
if-no-files-found: ignore
|
|
include-hidden-files: true
|
|
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 3. Build & Publish Docker Image
|
|
# ───────────────────────────────────────────────────────────────
|
|
build-and-publish:
|
|
name: "🏗️ Build & Publish Docker Image"
|
|
needs: [e2e-test]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Compute build metadata
|
|
id: meta
|
|
run: |
|
|
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
|
|
GIT_COMMIT="${GITHUB_SHA::7}"
|
|
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
|
|
APP_VERSION="${GITHUB_REF#refs/tags/}"
|
|
else
|
|
APP_VERSION="edge"
|
|
fi
|
|
echo "build_time=$BUILD_TIME" >> "$GITHUB_OUTPUT"
|
|
echo "git_commit=$GIT_COMMIT" >> "$GITHUB_OUTPUT"
|
|
echo "app_version=$APP_VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "Build: version=$APP_VERSION commit=$GIT_COMMIT time=$BUILD_TIME"
|
|
|
|
- name: Build Go Docker image (local staging)
|
|
run: |
|
|
GIT_COMMIT="${{ steps.meta.outputs.git_commit }}" \
|
|
APP_VERSION="${{ steps.meta.outputs.app_version }}" \
|
|
BUILD_TIME="${{ steps.meta.outputs.build_time }}" \
|
|
docker compose -f "$STAGING_COMPOSE_FILE" -p corescope-staging build "$STAGING_SERVICE"
|
|
echo "Built Go staging image ✅"
|
|
|
|
- name: Set up Docker Buildx
|
|
if: github.event_name == 'push'
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Set up QEMU (arm64 runtime stage)
|
|
if: github.event_name == 'push'
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Log in to GHCR
|
|
if: github.event_name == 'push'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Extract Docker metadata
|
|
if: github.event_name == 'push'
|
|
id: docker-meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ghcr.io/kpa-clawbot/corescope
|
|
tags: |
|
|
type=semver,pattern=v{{version}}
|
|
type=semver,pattern=v{{major}}.{{minor}}
|
|
type=semver,pattern=v{{major}}
|
|
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
|
|
type=edge,branch=master
|
|
|
|
- name: Build and push to GHCR
|
|
if: github.event_name == 'push'
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
context: .
|
|
push: true
|
|
platforms: linux/amd64,linux/arm64
|
|
tags: ${{ steps.docker-meta.outputs.tags }}
|
|
labels: ${{ steps.docker-meta.outputs.labels }}
|
|
build-args: |
|
|
APP_VERSION=${{ steps.meta.outputs.app_version }}
|
|
GIT_COMMIT=${{ steps.meta.outputs.git_commit }}
|
|
BUILD_TIME=${{ steps.meta.outputs.build_time }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 4. Release Artifacts (tags only)
|
|
# ───────────────────────────────────────────────────────────────
|
|
release-artifacts:
|
|
name: "📦 Release Artifacts"
|
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
needs: [go-test]
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Set up Go 1.22
|
|
uses: actions/setup-go@v6
|
|
with:
|
|
go-version: '1.22'
|
|
|
|
- name: Build corescope-decrypt (static, linux/amd64)
|
|
run: |
|
|
cd cmd/decrypt
|
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w -X main.version=${{ github.ref_name }}" -o ../../corescope-decrypt-linux-amd64 .
|
|
|
|
- name: Build corescope-decrypt (static, linux/arm64)
|
|
run: |
|
|
cd cmd/decrypt
|
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w -X main.version=${{ github.ref_name }}" -o ../../corescope-decrypt-linux-arm64 .
|
|
|
|
- name: Upload release assets
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
files: |
|
|
corescope-decrypt-linux-amd64
|
|
corescope-decrypt-linux-arm64
|
|
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 4b. Deploy Staging (master only)
|
|
# ───────────────────────────────────────────────────────────────
|
|
deploy:
|
|
name: "🚀 Deploy Staging"
|
|
if: github.event_name == 'push'
|
|
needs: [build-and-publish]
|
|
runs-on: [self-hosted, meshcore-runner-2]
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Pull latest image from GHCR
|
|
run: |
|
|
# Try to pull the edge image from GHCR and tag for docker-compose compatibility
|
|
if docker pull ghcr.io/kpa-clawbot/corescope:edge; then
|
|
docker tag ghcr.io/kpa-clawbot/corescope:edge corescope-go:latest
|
|
echo "Pulled and tagged GHCR edge image ✅"
|
|
else
|
|
echo "⚠️ GHCR pull failed — falling back to locally built image"
|
|
fi
|
|
|
|
- name: Deploy staging
|
|
run: |
|
|
# Force-remove the staging container regardless of how it was created
|
|
# (compose-managed OR manually created via docker run)
|
|
docker stop corescope-staging-go 2>/dev/null || true
|
|
docker rm -f corescope-staging-go 2>/dev/null || true
|
|
docker compose -f "$STAGING_COMPOSE_FILE" -p corescope-staging down --timeout 30 2>/dev/null || true
|
|
|
|
# Wait for container to be fully gone and OS to reclaim memory (3GB limit)
|
|
for i in $(seq 1 15); do
|
|
if ! docker ps -a --format '{{.Names}}' | grep -q 'corescope-staging-go'; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
sleep 5 # extra pause for OS memory reclaim
|
|
|
|
# Ensure staging data dir exists (config.json lives here, no separate file mount)
|
|
STAGING_DATA="${STAGING_DATA_DIR:-$HOME/meshcore-staging-data}"
|
|
mkdir -p "$STAGING_DATA"
|
|
|
|
# If no config exists, copy the example (CI doesn't have a real prod config)
|
|
if [ ! -f "$STAGING_DATA/config.json" ]; then
|
|
echo "Staging config missing — copying config.example.json"
|
|
cp config.example.json "$STAGING_DATA/config.json" 2>/dev/null || true
|
|
fi
|
|
|
|
docker compose -f "$STAGING_COMPOSE_FILE" -p corescope-staging up -d staging-go
|
|
|
|
- name: Healthcheck staging container
|
|
run: |
|
|
for i in $(seq 1 120); do
|
|
HEALTH=$(docker inspect corescope-staging-go --format '{{.State.Health.Status}}' 2>/dev/null || echo "starting")
|
|
if [ "$HEALTH" = "healthy" ]; then
|
|
echo "Staging healthy after ${i}s"
|
|
break
|
|
fi
|
|
if [ "$i" -eq 120 ]; then
|
|
echo "Staging failed health check after 120s"
|
|
docker logs corescope-staging-go --tail 50
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
- name: Smoke test staging API
|
|
run: |
|
|
PORT="${STAGING_GO_HTTP_PORT:-80}"
|
|
if curl -sf "http://localhost:${PORT}/api/stats" | grep -q engine; then
|
|
echo "Staging verified — engine field present ✅"
|
|
else
|
|
echo "Staging /api/stats did not return engine field (port ${PORT})"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Clean up old Docker images
|
|
if: always()
|
|
run: |
|
|
# Remove dangling images and images older than 24h (keeps current build)
|
|
echo "--- Docker disk usage before cleanup ---"
|
|
docker system df
|
|
docker image prune -af --filter "until=24h" 2>/dev/null || true
|
|
docker builder prune -f --keep-storage=1GB 2>/dev/null || true
|
|
echo "--- Docker disk usage after cleanup ---"
|
|
docker system df
|
|
|
|
# ───────────────────────────────────────────────────────────────
|
|
# 5. Publish Badges & Summary (master only)
|
|
# ───────────────────────────────────────────────────────────────
|
|
publish:
|
|
name: "📝 Publish Badges & Summary"
|
|
if: github.event_name == 'push'
|
|
needs: [deploy]
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Download Go coverage badges
|
|
continue-on-error: true
|
|
uses: actions/download-artifact@v6
|
|
with:
|
|
name: go-badges
|
|
path: .badges/
|
|
|
|
- name: Download E2E badges
|
|
continue-on-error: true
|
|
uses: actions/download-artifact@v6
|
|
with:
|
|
name: e2e-badges
|
|
path: .badges/
|
|
|
|
- name: Publish coverage badges to repo
|
|
continue-on-error: true
|
|
env:
|
|
GH_TOKEN: ${{ secrets.BADGE_PUSH_TOKEN }}
|
|
run: |
|
|
# GITHUB_TOKEN cannot push to protected branches (required status checks).
|
|
# Use admin PAT (BADGE_PUSH_TOKEN) via GitHub Contents API instead.
|
|
for badge in .badges/*.json; do
|
|
FILENAME=$(basename "$badge")
|
|
FILEPATH=".badges/$FILENAME"
|
|
CONTENT=$(base64 -w0 "$badge")
|
|
CURRENT_SHA=$(gh api "repos/${{ github.repository }}/contents/$FILEPATH" --jq '.sha' 2>/dev/null || echo "")
|
|
if [ -n "$CURRENT_SHA" ]; then
|
|
gh api "repos/${{ github.repository }}/contents/$FILEPATH" \
|
|
-X PUT \
|
|
-f message="ci: update $FILENAME [skip ci]" \
|
|
-f content="$CONTENT" \
|
|
-f sha="$CURRENT_SHA" \
|
|
-f branch="master" \
|
|
--silent 2>&1 || echo "Failed to update $FILENAME"
|
|
else
|
|
gh api "repos/${{ github.repository }}/contents/$FILEPATH" \
|
|
-X PUT \
|
|
-f message="ci: update $FILENAME [skip ci]" \
|
|
-f content="$CONTENT" \
|
|
-f branch="master" \
|
|
--silent 2>&1 || echo "Failed to create $FILENAME"
|
|
fi
|
|
done
|
|
echo "Badge publish complete"
|
|
|
|
- name: Post deployment summary
|
|
run: |
|
|
echo "## Staging Deployed ✓" >> $GITHUB_STEP_SUMMARY
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
echo "**Commit:** \`$(git rev-parse --short HEAD)\` — $(git log -1 --format=%s)" >> $GITHUB_STEP_SUMMARY
|