mirror of
https://github.com/block/buzz.git
synced 2026-10-29 13:31:44 +00:00
## Summary Make Kubernetes readiness depend only on the relay process lifecycle, so a shared Postgres or Redis slowdown cannot withdraw every pod at once. Move dependency checks to one fixed-cadence loop per pod. The loop permits only one evaluation at a time, emits metrics after each sample, and caches a timestamped report. `/_status` reads that cache without calling Postgres, Redis, or the deletion catalog. It reports `not_yet_sampled`, `fresh`, or `stale`. Each completed sample also writes a Unix timestamp gauge, so Datadog can compute sample age at query time. Fail closed when the community lifecycle lookup errors. An inconclusive tenant-state lookup no longer admits a socket to AUTH or REQ handling. ### Related issue None found. This addresses the elevated relay HTTP 500 and reconnect incident investigated on 2026-09-03. ### Tradeoff The readiness gauge now reports the latest private-probe observation, not the shutdown transition itself. After shutdown starts, a scrape can still see `1` until the next readiness probe refreshes the gauge to `0`. Kubernetes routing is unaffected because it uses the readiness response from the authoritative process state. A terminal `0` was never guaranteed: even a transition-owned gauge could update in process and then exit before Prometheus scraped it. If no scrape occurs before exit, both designs miss the terminal sample. The new design adds at most the interval until the next readiness probe when a scrape occurs during shutdown. Dependency status is now sampled every 30 seconds rather than on demand. A slow evaluation delays the next sample instead of stacking more work. The status payload exposes the cached report age. The completion timestamp stays unchanged when sampling stops, so `time() - timestamp` crosses the stale threshold without a server aging loop. Outcome counters and duration histograms remain cumulative and cannot provide no-data detection. ### Testing Gimli exercised exact SHA `edff1ec4a5362e132e367bf7449d2b0a68f1e9a9` on an isolated Blox stack. Repeated `/_status` bursts caused no dependency calls, metrics advanced without status traffic, stale status remained visible, and lifecycle lookup failures admitted no AUTH or REQ handling. Follow-up verification at `fdbb394ed598c94cde85ed01cb11e9de0ee8f00d` confirmed sampler-owned outcome and duration emission without status traffic and bound the real startup seam. The final timestamp-gauge change is covered by targeted red/green tests and the same boot regression at `c0f36e22b527c2fd6a63a548d036d006ffa17171`. ### Complexity Readiness remains one process-local sample per probe. Dependency I/O now has one owner and one execution path; request volume cannot increase dependency-check load. One completion timestamp replaces the prior server-side age update path; Datadog computes age without another loop. Community admission also returns to one fail-closed rule for inactive and unknown tenant state. Generated with [Claude Code](https://claude.ai/code) --------- Signed-off-by: tornquist <tornquist@squareup.com> Signed-off-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Elrond <28d6302a099e5225b02c4155ac4236e4912603df2ab08dbfc2f4fef08ce598c8@buzz.block.builderlab.xyz> Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Amp <amp@ampcode.com>
642 lines
28 KiB
YAML
642 lines
28 KiB
YAML
name: CI / Relay and PostgreSQL
|
|
on:
|
|
workflow_call:
|
|
inputs:
|
|
rust:
|
|
required: true
|
|
type: boolean
|
|
desktop:
|
|
required: true
|
|
type: boolean
|
|
desktop_rust:
|
|
required: true
|
|
type: boolean
|
|
lane:
|
|
required: true
|
|
type: string
|
|
outputs:
|
|
desktop_e2e_relay_result:
|
|
value: ${{ jobs.results.outputs.desktop_e2e_relay_result }}
|
|
desktop_e2e_integration_result:
|
|
value: ${{ jobs.results.outputs.desktop_e2e_integration_result }}
|
|
backend_integration_result:
|
|
value: ${{ jobs.results.outputs.backend_integration_result }}
|
|
relay_e2e_result:
|
|
value: ${{ jobs.results.outputs.relay_e2e_result }}
|
|
postgres_tests_result:
|
|
value: ${{ jobs.results.outputs.postgres_tests_result }}
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
|
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
|
|
|
jobs:
|
|
desktop-e2e-relay:
|
|
name: Desktop E2E Relay
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 45
|
|
if: inputs.lane == 'artifacts' && (github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust)
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
SCCACHE_GHA_ENABLED: "true"
|
|
SCCACHE_GHA_RW_MODE: ${{ (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.number == 5224)) && 'READ_WRITE' || 'READ_ONLY' }}
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
# Reuse the relay binaries and backend test archive when none of their
|
|
# inputs changed (desktop-only PRs hit this every time). The key covers
|
|
# everything they embed, including migrations via sqlx migrate!.
|
|
- name: Restore relay artifacts cache
|
|
id: relay-artifacts-cache
|
|
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: |
|
|
target/ci/buzz-relay
|
|
target/ci/git-credential-nostr
|
|
target/ci/backend-integration-tests.tar.zst
|
|
target/ci/postgres-tests.tar.zst
|
|
key: relay-artifacts-${{ runner.os }}-${{ hashFiles('crates/**', 'migrations/**', 'Dockerfile', 'Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', '.cargo/config.toml', '.config/nextest.toml', 'scripts/postgres-test-*.sh', 'scripts/check-postgres-test-discovery.py', '.github/workflows/ci.yml', '.github/workflows/_ci-*.yml') }}
|
|
- uses: rui314/setup-mold@7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3 # v1
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
|
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
|
|
with:
|
|
workspaces: |
|
|
.
|
|
desktop/src-tauri
|
|
save-if: ${{ github.event_name != 'pull_request' }}
|
|
# Cache rustc outputs for unchanged workspace crates. Trusted pushes write;
|
|
# the bounded PR 5224 trial writes only to its isolated merge-ref scope.
|
|
- name: Set up sccache
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
|
|
uses: Mozilla-Actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11 # zizmor: ignore[cache-poisoning] Bounded trial: only PR 5224 writes to its isolated merge-ref scope; trusted pushes retain production writes.
|
|
with:
|
|
version: v0.16.0
|
|
- name: Install cargo-nextest
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
|
|
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
|
with:
|
|
tool: cargo-nextest@0.9.136
|
|
- name: Build relay artifacts
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true'
|
|
env:
|
|
RUSTC_WRAPPER: sccache
|
|
run: |
|
|
cargo build --profile ci -p buzz-relay -p git-credential-nostr
|
|
cargo nextest archive \
|
|
--cargo-profile ci \
|
|
-p buzz-db \
|
|
-p buzz-relay \
|
|
-p buzz-test-client \
|
|
--lib \
|
|
--test e2e_event_reminder \
|
|
--archive-file target/ci/backend-integration-tests.tar.zst
|
|
postgres_package_args=()
|
|
while IFS= read -r package; do
|
|
postgres_package_args+=(-p "$package")
|
|
done < <(scripts/postgres-test-packages.sh)
|
|
if [[ "${#postgres_package_args[@]}" -eq 0 ]]; then
|
|
echo "no PostgreSQL test packages were discovered" >&2
|
|
exit 1
|
|
fi
|
|
cargo nextest archive \
|
|
--cargo-profile ci \
|
|
"${postgres_package_args[@]}" \
|
|
--lib \
|
|
--tests \
|
|
--archive-file target/ci/postgres-tests.tar.zst
|
|
- name: Save relay artifacts cache
|
|
# PR-scoped exact-source entries cannot warm main or other PRs and churn
|
|
# the shared cache pool. sccache provides read-only PR reuse instead.
|
|
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true' && github.event_name == 'push'
|
|
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: |
|
|
target/ci/buzz-relay
|
|
target/ci/git-credential-nostr
|
|
target/ci/backend-integration-tests.tar.zst
|
|
target/ci/postgres-tests.tar.zst
|
|
key: relay-artifacts-${{ runner.os }}-${{ hashFiles('crates/**', 'migrations/**', 'Dockerfile', 'Cargo.toml', 'Cargo.lock', 'rust-toolchain.toml', '.cargo/config.toml', '.config/nextest.toml', 'scripts/postgres-test-*.sh', 'scripts/check-postgres-test-discovery.py', '.github/workflows/ci.yml', '.github/workflows/_ci-*.yml') }}
|
|
- name: Upload relay artifacts
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: desktop-e2e-relay
|
|
path: |
|
|
target/ci/buzz-relay
|
|
target/ci/git-credential-nostr
|
|
target/ci/backend-integration-tests.tar.zst
|
|
target/ci/postgres-tests.tar.zst
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
postgres-tests:
|
|
name: PostgreSQL Tests
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
if: inputs.lane == 'postgres' && (github.event_name == 'push' || inputs.rust)
|
|
permissions:
|
|
contents: read
|
|
services:
|
|
postgres:
|
|
image: postgres:16
|
|
env:
|
|
POSTGRES_USER: buzz
|
|
POSTGRES_PASSWORD: ${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}
|
|
POSTGRES_DB: postgres
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd "pg_isready -U buzz -d postgres"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
redis:
|
|
image: redis:7
|
|
ports:
|
|
- 6379:6379
|
|
options: >-
|
|
--health-cmd "redis-cli ping"
|
|
--health-interval 5s
|
|
--health-timeout 5s
|
|
--health-retries 12
|
|
env:
|
|
PGHOST: localhost
|
|
PGPORT: "5432"
|
|
PGUSER: buzz
|
|
PG_BIN_DIR: /usr/bin
|
|
REDIS_URL: redis://localhost:6379
|
|
PGSCHEMA_PLAN_HOST: localhost
|
|
PGSCHEMA_PLAN_PORT: "5432"
|
|
PGSCHEMA_PLAN_USER: buzz
|
|
PGSCHEMA_PLAN_PASSWORD: buzz_dev
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
- name: Tune disposable PostgreSQL
|
|
env:
|
|
PGPASSWORD: ${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}
|
|
run: |
|
|
# Database-per-test cloning forces checkpoints. Durability is redundant
|
|
# for this disposable service and makes runtime depend on runner disk I/O.
|
|
psql --dbname postgres --set ON_ERROR_STOP=1 <<'SQL'
|
|
ALTER SYSTEM SET fsync = off;
|
|
ALTER SYSTEM SET full_page_writes = off;
|
|
ALTER SYSTEM SET synchronous_commit = off;
|
|
SELECT pg_reload_conf();
|
|
SQL
|
|
psql --dbname postgres --tuples-only --no-align --command \
|
|
"SELECT bool_and(setting = 'off') FROM pg_settings WHERE name IN ('fsync', 'full_page_writes', 'synchronous_commit')" \
|
|
| grep --fixed-strings --line-regexp t
|
|
- name: Install cargo-nextest
|
|
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
|
with:
|
|
tool: cargo-nextest@0.9.136
|
|
- name: Download backend test archive
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: desktop-e2e-relay
|
|
path: target/ci
|
|
- name: Restore relay executable permission
|
|
run: chmod +x ./target/ci/buzz-relay
|
|
- name: PostgreSQL-backed tests
|
|
env:
|
|
BUZZ_POSTGRES_ADMIN_URL: postgres://buzz:${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}@localhost:5432/postgres
|
|
PGPASSWORD: ${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}
|
|
run: |
|
|
scripts/postgres-test-run.sh \
|
|
--archive-file target/ci/postgres-tests.tar.zst
|
|
|
|
desktop-e2e-integration-shard:
|
|
env:
|
|
COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml
|
|
name: Desktop E2E Integration (${{ matrix.shard }}/2)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust)
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2]
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
- name: Start integration services
|
|
run: |
|
|
for attempt in 1 2 3; do
|
|
if docker compose up -d postgres redis minio minio-init; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "docker compose up failed after 3 attempts" >&2
|
|
exit 1
|
|
fi
|
|
echo "docker compose up failed (attempt $attempt), retrying in $((attempt * 5))s..." >&2
|
|
sleep $((attempt * 5))
|
|
done
|
|
- name: Get pnpm store directory
|
|
id: pnpm-cache
|
|
run: echo "STORE_PATH=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
|
- name: Restore pnpm store cache
|
|
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
restore-keys: pnpm-${{ runner.os }}-
|
|
- name: Install desktop dependencies
|
|
run: just desktop-install-ci
|
|
- name: Get Playwright version
|
|
id: pw-version
|
|
run: echo "version=$(cd desktop && node -e "console.log(require('@playwright/test/package.json').version)")" >> "$GITHUB_OUTPUT"
|
|
- name: Restore Playwright browser cache
|
|
id: playwright-cache
|
|
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
|
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
|
|
- name: Install Playwright Chromium
|
|
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
|
run: cd desktop && pnpm exec playwright install chromium
|
|
- name: Install Playwright system dependencies
|
|
run: cd desktop && pnpm exec playwright install-deps chromium
|
|
- name: Save Playwright browser cache
|
|
if: steps.playwright-cache.outputs.cache-hit != 'true' && github.event_name == 'push' && matrix.shard == 1
|
|
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }}
|
|
key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }}
|
|
- name: Desktop E2E build
|
|
run: pnpm -C desktop build:e2e
|
|
- name: Wait for integration services
|
|
run: |
|
|
wait_healthy() {
|
|
local service="$1"
|
|
local container="$2"
|
|
for attempt in $(seq 1 60); do
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' "${container}" 2>/dev/null || echo "not_found")
|
|
if [ "${status}" = "healthy" ]; then
|
|
echo "${service} is healthy"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
docker logs "${container}" || true
|
|
return 1
|
|
}
|
|
wait_healthy "Postgres" "buzz-postgres"
|
|
wait_healthy "Redis" "buzz-redis"
|
|
wait_healthy "MinIO" "buzz-minio"
|
|
- name: Download relay binary
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: desktop-e2e-relay
|
|
path: target/ci
|
|
- name: Apply schema and seed deployment community
|
|
# MT: the relay resolves each request's tenant from the communities host
|
|
# map and fails closed on an unmapped host. The channel reconciler binds
|
|
# the deployment community ONCE at boot (outside its retry loop) and
|
|
# exits permanently on an unmapped host, so the 'localhost:3000'
|
|
# community MUST exist before the relay starts — the retry loop only
|
|
# handles late-seeded channels, not a late-seeded community. The relay
|
|
# migrates at boot via BUZZ_AUTO_MIGRATE, but that's too late for the
|
|
# pre-boot seed, so apply the schema here first (then drop AUTO_MIGRATE
|
|
# below). lower(host) is the unique index → ON CONFLICT target. psql
|
|
# isn't on PATH in hermit → exec into the buzz-postgres container.
|
|
env:
|
|
PGHOST: localhost
|
|
PGPORT: "5432"
|
|
PGUSER: buzz
|
|
PGPASSWORD: buzz_dev
|
|
PGDATABASE: buzz
|
|
# Use the already-running docker postgres for desired-state planning instead of
|
|
# downloading an embedded Postgres from Maven Central (transient-fetch flake source).
|
|
PGSCHEMA_PLAN_HOST: localhost
|
|
PGSCHEMA_PLAN_PORT: "5432"
|
|
PGSCHEMA_PLAN_DB: buzz
|
|
PGSCHEMA_PLAN_USER: buzz
|
|
PGSCHEMA_PLAN_PASSWORD: buzz_dev
|
|
run: |
|
|
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
|
docker exec -i -e PGPASSWORD=buzz_dev buzz-postgres \
|
|
psql -U buzz -d buzz -v ON_ERROR_STOP=1 < scripts/reconcile-schema-after-pgschema.sql
|
|
docker exec -e PGPASSWORD=buzz_dev buzz-postgres \
|
|
psql -U buzz -d buzz -qtA -c "
|
|
INSERT INTO communities (id, host)
|
|
VALUES ('00000000-0000-4000-8000-00000000c0de', 'localhost:3000')
|
|
ON CONFLICT (lower(host)) DO NOTHING
|
|
;"
|
|
- name: Start relay
|
|
run: |
|
|
chmod +x ./target/ci/buzz-relay
|
|
nohup env \
|
|
DATABASE_URL="postgres://buzz:${BUZZ_TEST_POSTGRES_PASSWORD}@localhost:5432/buzz" \
|
|
REDIS_URL=redis://localhost:6379 \
|
|
RELAY_URL=ws://localhost:3000 \
|
|
BUZZ_BIND_ADDR=0.0.0.0:3000 \
|
|
BUZZ_RELAY_PRIVATE_KEY="$(openssl rand -hex 32)" \
|
|
BUZZ_REQUIRE_AUTH_TOKEN=false \
|
|
BUZZ_RECONCILE_CHANNELS=true \
|
|
BUZZ_RATE_LIMIT_HUMAN_MESSAGES_PER_MIN=100000 \
|
|
BUZZ_RATE_LIMIT_HUMAN_API_CALLS_PER_MIN=100000 \
|
|
BUZZ_RATE_LIMIT_HUMAN_WS_EVENTS_PER_SEC=10000 \
|
|
BUZZ_GIT_PROBE_WRITERS=8 \
|
|
SPROUT_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
|
|
./target/ci/buzz-relay > /tmp/buzz-relay.log 2>&1 &
|
|
echo $! > /tmp/buzz-relay.pid
|
|
for attempt in $(seq 1 60); do
|
|
if ! kill -0 "$(cat /tmp/buzz-relay.pid)" 2>/dev/null; then
|
|
cat /tmp/buzz-relay.log
|
|
exit 1
|
|
fi
|
|
status_code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/_readiness || true)
|
|
if [ "${status_code}" = "200" ]; then
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
cat /tmp/buzz-relay.log
|
|
exit 1
|
|
- name: Seed desktop e2e data
|
|
run: bash scripts/setup-desktop-test-data.sh
|
|
- name: Desktop relay-backed e2e
|
|
run: cd desktop && pnpm exec playwright test --project=integration --shard=${{ matrix.shard }}/2
|
|
- name: Summarize flaky tests
|
|
if: ${{ !cancelled() }}
|
|
run: node scripts/summarize-flaky-tests.mjs playwright-report.json "Desktop E2E Integration (${{ matrix.shard }}/2)"
|
|
working-directory: desktop
|
|
- name: Upload desktop integration artifacts
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: desktop-e2e-integration-artifacts-${{ matrix.shard }}
|
|
path: |
|
|
desktop/playwright-report
|
|
desktop/playwright-report.json
|
|
desktop/test-results
|
|
/tmp/buzz-relay.log
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
- name: Save pnpm store cache
|
|
if: github.event_name == 'push'
|
|
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
|
with:
|
|
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
|
|
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
|
|
desktop-e2e-integration:
|
|
name: Desktop E2E Integration
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
needs: [desktop-e2e-integration-shard]
|
|
if: always() && inputs.lane == 'required' && (github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust)
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- name: Check integration shards
|
|
run: |
|
|
if [ "${{ needs.desktop-e2e-integration-shard.result }}" != "success" ]; then
|
|
echo "Desktop E2E Integration shards finished with: ${{ needs.desktop-e2e-integration-shard.result }}"
|
|
exit 1
|
|
fi
|
|
echo "Desktop E2E Integration shards passed"
|
|
|
|
backend-integration:
|
|
env:
|
|
COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml
|
|
name: Backend Integration (relay e2e)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.rust)
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
- name: Install cargo-nextest
|
|
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
|
with:
|
|
tool: cargo-nextest@0.9.136
|
|
- name: Start integration services
|
|
run: |
|
|
for attempt in 1 2 3; do
|
|
if docker compose up -d postgres redis minio minio-init; then
|
|
break
|
|
fi
|
|
if [ "$attempt" -eq 3 ]; then
|
|
echo "docker compose up failed after 3 attempts" >&2
|
|
exit 1
|
|
fi
|
|
echo "docker compose up failed (attempt $attempt), retrying in $((attempt * 5))s..." >&2
|
|
sleep $((attempt * 5))
|
|
done
|
|
- name: Wait for integration services
|
|
run: |
|
|
wait_healthy() {
|
|
local service="$1"
|
|
local container="$2"
|
|
for attempt in $(seq 1 60); do
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' "${container}" 2>/dev/null || echo "not_found")
|
|
if [ "${status}" = "healthy" ]; then
|
|
echo "${service} is healthy"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
docker logs "${container}" || true
|
|
return 1
|
|
}
|
|
wait_healthy "Postgres" "buzz-postgres"
|
|
wait_healthy "Redis" "buzz-redis"
|
|
wait_healthy "MinIO" "buzz-minio"
|
|
- name: Download relay artifacts
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: desktop-e2e-relay
|
|
path: target/ci
|
|
- name: Apply schema and seed deployment community
|
|
# MT: the relay resolves each request's tenant from the communities host
|
|
# map and fails closed on an unmapped host. The reminder scheduler binds
|
|
# the deployment community ONCE at boot and exits permanently on an
|
|
# unmapped host (no retry, unlike the channel reconciler), so the
|
|
# 'localhost:3000' community MUST exist before the relay starts — seeding
|
|
# after boot leaves the scheduler dead. The relay migrates at boot via
|
|
# BUZZ_AUTO_MIGRATE, but that's too late for the pre-boot seed, so apply
|
|
# the schema here first (then drop AUTO_MIGRATE below). lower(host) is the
|
|
# unique index → ON CONFLICT target. psql isn't on PATH in hermit → exec
|
|
# into the buzz-postgres container.
|
|
env:
|
|
PGHOST: localhost
|
|
PGPORT: "5432"
|
|
PGUSER: buzz
|
|
PGPASSWORD: buzz_dev
|
|
PGDATABASE: buzz
|
|
# Use the already-running docker postgres for desired-state planning instead of
|
|
# downloading an embedded Postgres from Maven Central (transient-fetch flake source).
|
|
PGSCHEMA_PLAN_HOST: localhost
|
|
PGSCHEMA_PLAN_PORT: "5432"
|
|
PGSCHEMA_PLAN_DB: buzz
|
|
PGSCHEMA_PLAN_USER: buzz
|
|
PGSCHEMA_PLAN_PASSWORD: buzz_dev
|
|
run: |
|
|
./bin/pgschema apply --file schema/schema.sql --auto-approve
|
|
docker exec -i -e PGPASSWORD=buzz_dev buzz-postgres \
|
|
psql -U buzz -d buzz -v ON_ERROR_STOP=1 < scripts/reconcile-schema-after-pgschema.sql
|
|
docker exec -e PGPASSWORD=buzz_dev buzz-postgres \
|
|
psql -U buzz -d buzz -qtA -c "
|
|
INSERT INTO communities (id, host)
|
|
VALUES ('00000000-0000-4000-8000-00000000c0de', 'localhost:3000')
|
|
ON CONFLICT (lower(host)) DO NOTHING
|
|
;"
|
|
- name: Git default-branch route and clone regressions
|
|
env:
|
|
# The preceding step already applied and reconciled schema/schema.sql.
|
|
BUZZ_TEST_SCHEMA_MODE: desired
|
|
BUZZ_TEST_DATABASE_URL: postgres://buzz:${{ env.BUZZ_TEST_POSTGRES_PASSWORD }}@localhost:5432/buzz
|
|
BUZZ_TEST_REDIS_URL: redis://localhost:6379
|
|
BUZZ_TEST_S3_ENDPOINT: http://localhost:9000
|
|
BUZZ_TEST_S3_BUCKET: buzz-media
|
|
run: |
|
|
cargo nextest run \
|
|
--archive-file target/ci/backend-integration-tests.tar.zst \
|
|
-E 'package(buzz-relay) and test(/api::git::settings::tests::external_infra::/)' \
|
|
--run-ignored ignored-only
|
|
- name: Relay quota accounting and rejection regressions
|
|
env:
|
|
BUZZ_TEST_REDIS_URL: redis://localhost:6379
|
|
run: |
|
|
cargo nextest run \
|
|
--archive-file target/ci/backend-integration-tests.tar.zst \
|
|
-E 'package(buzz-relay) and (test(/^rejection::/) or test(/^admission::/))' \
|
|
--run-ignored all
|
|
- name: Workflow message provenance unit tests
|
|
# The relay's workflow_sink suite is not selected by the infra-free
|
|
# unit job. Its ignored database cases run in the isolated PostgreSQL
|
|
# lane; keep the pure provenance cases covered here without duplication.
|
|
run: |
|
|
cargo nextest run \
|
|
--archive-file target/ci/backend-integration-tests.tar.zst \
|
|
-E 'package(buzz-relay) and test(/workflow_sink/)'
|
|
- name: Start relay
|
|
run: |
|
|
chmod +x ./target/ci/buzz-relay
|
|
nohup env \
|
|
DATABASE_URL="postgres://buzz:${BUZZ_TEST_POSTGRES_PASSWORD}@localhost:5432/buzz" \
|
|
REDIS_URL=redis://localhost:6379 \
|
|
RELAY_URL=ws://localhost:3000 \
|
|
BUZZ_BIND_ADDR=0.0.0.0:3000 \
|
|
BUZZ_RELAY_PRIVATE_KEY="$(openssl rand -hex 32)" \
|
|
BUZZ_REQUIRE_AUTH_TOKEN=false \
|
|
BUZZ_RECONCILE_CHANNELS=true \
|
|
BUZZ_GIT_PROBE_WRITERS=8 \
|
|
SPROUT_REMINDER_SCHEDULER_INTERVAL_SECS=1 \
|
|
./target/ci/buzz-relay > /tmp/buzz-relay.log 2>&1 &
|
|
echo $! > /tmp/buzz-relay.pid
|
|
for attempt in $(seq 1 60); do
|
|
if ! kill -0 "$(cat /tmp/buzz-relay.pid)" 2>/dev/null; then
|
|
cat /tmp/buzz-relay.log
|
|
exit 1
|
|
fi
|
|
status_code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/_readiness || true)
|
|
if [ "${status_code}" = "200" ]; then
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
cat /tmp/buzz-relay.log
|
|
exit 1
|
|
- name: NIP-ER reminder e2e
|
|
# Feature e2e for NIP-ER (Event Reminders, kind:30300): write-path
|
|
# validation, author-only read filtering, and scheduler delivery against
|
|
# a live relay. The schema-drift / migration-version guarantee is owned
|
|
# by the buzz-db migration.rs unit tests, not this suite.
|
|
run: |
|
|
cargo nextest run \
|
|
--archive-file target/ci/backend-integration-tests.tar.zst \
|
|
-E 'binary(e2e_event_reminder)' \
|
|
--run-ignored ignored-only
|
|
env:
|
|
RELAY_URL: ws://localhost:3000
|
|
- name: Upload relay log
|
|
if: failure()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: backend-integration-relay-log
|
|
path: /tmp/buzz-relay.log
|
|
if-no-files-found: ignore
|
|
|
|
relay-e2e:
|
|
env:
|
|
COMPOSE_FILE: docker-compose.yml:docker-compose.ci.yml
|
|
name: Relay E2E
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.rust)
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
|
with:
|
|
save-if: ${{ github.event_name != 'pull_request' }}
|
|
# Reuse the relay + git-credential-nostr built by Desktop E2E Relay
|
|
# instead of compiling them a second time.
|
|
- name: Download relay binary
|
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: desktop-e2e-relay
|
|
path: target/ci
|
|
- name: Start relay
|
|
run: |
|
|
chmod +x ./target/ci/buzz-relay ./target/ci/git-credential-nostr
|
|
./scripts/start-relay-for-tests.sh --no-build
|
|
- name: Relay E2E tests
|
|
run: |
|
|
cargo test -p buzz-test-client --test e2e_persona --test e2e_team_catalog --test e2e_nostr_interop --test e2e_thread_roots --test e2e_project -- --ignored --nocapture
|
|
cargo test -p buzz-test-client --test e2e_relay invite -- --ignored --nocapture
|
|
cargo test -p buzz-test-client --test e2e_relay nip43_membership_snapshots_are_rejected -- --ignored --nocapture
|
|
cargo test -p buzz-test-client --test e2e_relay nip29_departure_wire -- --ignored --nocapture
|
|
env:
|
|
RELAY_URL: ws://localhost:3000
|
|
GIT_CREDENTIAL_NOSTR_BIN: ${{ github.workspace }}/target/ci/git-credential-nostr
|
|
- name: Media read-auth e2e
|
|
# Reads require kind:24242 `t=get` auth, so these binaries are the only
|
|
# coverage that a real relay rejects bare reads and honours host- and
|
|
# hash-scoped tokens. They were #[ignore]d and selected by no CI job, so
|
|
# the lane never ran; select it here, where MinIO and the seeded
|
|
# 'localhost:3000' community already exist.
|
|
# --no-fail-fast: without it cargo stops after the first failing binary,
|
|
# so one broken case hides every later binary's result.
|
|
run: |
|
|
cargo test -p buzz-test-client --no-fail-fast --test e2e_media --test e2e_media_extended --test e2e_media_video -- --ignored --nocapture
|
|
env:
|
|
RELAY_URL: ws://localhost:3000
|
|
RELAY_HTTP_URL: http://localhost:3000
|
|
- name: Upload relay logs
|
|
if: failure()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
with:
|
|
name: relay-e2e-artifacts
|
|
path: /tmp/buzz-relay.log
|
|
if-no-files-found: ignore
|
|
|
|
|
|
results:
|
|
name: Results
|
|
if: ${{ always() }}
|
|
needs: [desktop-e2e-relay, postgres-tests, desktop-e2e-integration, backend-integration, relay-e2e]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
outputs:
|
|
desktop_e2e_relay_result: ${{ needs.desktop-e2e-relay.result }}
|
|
desktop_e2e_integration_result: ${{ needs.desktop-e2e-integration.result }}
|
|
backend_integration_result: ${{ needs.backend-integration.result }}
|
|
relay_e2e_result: ${{ needs.relay-e2e.result }}
|
|
postgres_tests_result: ${{ needs.postgres-tests.result }}
|
|
steps:
|
|
- run: echo "Captured Relay and PostgreSQL job results"
|