mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-12 15:54:44 +00:00
364c5766fc
## Adds new logo and home hero Replaces the navbar mushroom emoji + "CoreScope" text spans with the new CoreScope SVG mark, and adds a hero SVG (with the MESH ANALYZER tagline) above the home page H1. ### What changed - `public/img/corescope-logo.svg` — navbar mark, no tagline (locked "aggressive low-amp chirp" variant: facing-arcs + low-amp chirp connector between the two nodes). - `public/img/corescope-hero.svg` — home hero version, includes the MESH ANALYZER tagline. - `public/index.html` — replaces `<span class="brand-icon">🍄</span><span class="brand-text">CoreScope</span>` with `<img class="brand-logo" src="img/corescope-logo.svg?__BUST__" …>`. `.nav-brand` link still routes to `#/`. `.live-dot` retained. - `public/style.css` — adds `.brand-logo { height: 36px }` (32px on tablet ≤900px). Existing 52px nav height unchanged. - `public/home.js` / `public/home.css` — adds `<img class="home-hero-logo">` above the hero `<h1>`, sized `max-width: min(720px, 90vw)` and centered. ### TDD Red→green is visible in the branch: - `3159b82` — `test(logo): add failing E2E …` (red commit). Adds `test-logo-rebrand-e2e.js` and wires it into the `e2e-test` job in `deploy.yml` with `CHROMIUM_REQUIRE=1`. On this commit `index.html` still has the emoji + text spans, `home.js` has no hero img, and the SVG asset files do not exist — the test asserts on each so CI fails on assertion. - `19434e1` — `feat(logo): wire new CoreScope SVG logo …` (green commit). Implements the fix. ### E2E asserts 1. `.nav-brand img` exists with `src` ending `corescope-logo.svg` 2. legacy `.brand-icon` / `.brand-text` are gone 3. `.live-dot` is present, visible, and to the right of the logo (no overlap) 4. `.home-hero img.home-hero-logo` exists with `src` ending `corescope-hero.svg`, positioned BEFORE the `<h1>` 5. both `/img/corescope-{logo,hero}.svg` return 200 with svg content-type ### Customizer compatibility - `customize.js` still does `querySelector('.brand-text')` / `.brand-icon` for live branding updates. Both now return `null`; existing `if (el)` guards make those branches silent no-ops. **No JS errors, but the customizer's `branding.siteName` and `branding.logoUrl` fields no longer rewrite the navbar brand** — the brand is now a fixed SVG asset. - **Theme accent does NOT recolor the SVG.** SVGs loaded via `<img src>` are isolated documents and cannot inherit document CSS variables; the SVG falls back to its embedded brand colors. This is appropriate for a brand mark; if recoloring per theme is desired later, swap to inline SVG (separate PR). ### Browser validation Local Chromium not available in this env; the E2E test soft-skips locally and hard-fails in CI (`CHROMIUM_REQUIRE=1`). Server-side checks done locally: - `curl http://localhost:13581/` → confirmed `<img class="brand-logo" src="img/corescope-logo.svg?<bust>" …>` rendered, no `.brand-icon`/`.brand-text` spans. - `curl -I /img/corescope-logo.svg` and `/img/corescope-hero.svg` → both 200. ### Performance No hot-path changes. Two new static SVG assets (~7.6KB each), served directly by the Go static handler. Cache-busted via `?__BUST__` (auto-replaced server-side). --------- Co-authored-by: OpenClaw Bot <bot@openclaw.local> Co-authored-by: Kpa-clawbot <bot@kpa-clawbot.local>
537 lines
22 KiB
YAML
537 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
|
|
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-rebrand-e2e.js 2>&1 | tee -a e2e-output.txt
|
|
CHROMIUM_REQUIRE=1 BASE_URL=http://localhost:13581 node test-logo-theme-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
|