mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
ci: split CI into reusable workflows (#7168)
## Why The monolithic CI workflow is a frequent merge-conflict hotspot. Splitting cohesive domains into same-repository reusable workflows keeps one centrally filtered entry point while letting Rust, desktop, relay/PostgreSQL, client, and security CI evolve independently. ## What - Keep `ci.yml` as the only push/pull-request orchestrator with unchanged concurrency and path detection. - Move 18 execution jobs into five `workflow_call`-only domain workflows without changing their runners, steps, matrices, caches, artifacts, permissions, or timeouts. - Keep the relay artifact producer with desktop integration, the complete PostgreSQL lane, and relay E2E consumers. - Preserve all 12 existing required GitHub Actions contexts through lightweight top-level compatibility gates, so the repository ruleset does not need to change. - Update the Rust-cache contract to follow Unit Tests into `_ci-rust.yml`. ## Risk Assessment CI-only change with moderate workflow-orchestration risk. The main risks are reusable-workflow output propagation, skip behavior, and visible check naming; the old required names remain explicit top-level jobs, and the draft will stay open until an exact-head GitHub Actions run and independent review are complete. Generated with Codex --------- Signed-off-by: Luke Tornquist <tornquist@squareup.com> Signed-off-by: tornquist <tornquist@squareup.com>
This commit is contained in:
148
.github/workflows/_ci-clients.yml
vendored
Normal file
148
.github/workflows/_ci-clients.yml
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
name: CI / Clients
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
web:
|
||||
required: true
|
||||
type: boolean
|
||||
mobile:
|
||||
required: true
|
||||
type: boolean
|
||||
lane:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
web_result:
|
||||
value: ${{ jobs.results.outputs.web_result }}
|
||||
mobile_result:
|
||||
value: ${{ jobs.results.outputs.mobile_result }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
||||
|
||||
jobs:
|
||||
web:
|
||||
name: Web
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.web)
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- 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 dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Web lint and format
|
||||
run: just web-check
|
||||
- name: Web build
|
||||
run: just web-build
|
||||
- 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') }}
|
||||
|
||||
mobile:
|
||||
name: Mobile
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.mobile)
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- name: Compute Hermit cache key
|
||||
id: hermit-bin-hash
|
||||
run: |
|
||||
hash="$(find ./bin ! -type d | sort | xargs openssl sha256 | openssl sha256 -r | cut -d' ' -f1)"
|
||||
echo "hash=$hash" >> "$GITHUB_OUTPUT"
|
||||
- name: Restore Hermit package cache
|
||||
id: hermit-cache
|
||||
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: ~/.cache/hermit/pkg
|
||||
key: ${{ runner.os }}-hermit-cache-${{ steps.hermit-bin-hash.outputs.hash }}
|
||||
restore-keys: ${{ runner.os }}-hermit-cache-
|
||||
- name: Prime Flutter SDK
|
||||
run: flutter --version
|
||||
- name: Save Hermit package cache
|
||||
if: always() && steps.hermit-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: ~/.cache/hermit/pkg
|
||||
key: ${{ runner.os }}-hermit-cache-${{ steps.hermit-bin-hash.outputs.hash }}
|
||||
- name: Restore pub cache
|
||||
id: pub-cache
|
||||
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: ~/.pub-cache
|
||||
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
|
||||
restore-keys: pub-${{ runner.os }}-
|
||||
- name: Install dependencies
|
||||
run: cd mobile && flutter pub get
|
||||
- name: Save pub cache
|
||||
if: always() && steps.pub-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: ~/.pub-cache
|
||||
key: pub-${{ runner.os }}-${{ hashFiles('mobile/pubspec.lock') }}
|
||||
- name: Format check
|
||||
run: cd mobile && dart format --output=none --set-exit-if-changed .
|
||||
- name: Analyze
|
||||
run: cd mobile && flutter analyze
|
||||
- name: Test
|
||||
run: cd mobile && flutter test
|
||||
- name: Build Android debug APK
|
||||
run: just mobile-build-android
|
||||
|
||||
mobile-swift:
|
||||
name: Mobile Swift
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
if: inputs.lane == 'mobile-swift' && inputs.mobile
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- name: Install Flutter dependencies
|
||||
run: cd mobile && flutter pub get
|
||||
- name: Build
|
||||
run: swift build --package-path mobile/ios/BuzzPushKit
|
||||
- name: Build release
|
||||
run: swift build -c release --package-path mobile/ios/BuzzPushKit
|
||||
- name: Test
|
||||
run: swift test --package-path mobile/ios/BuzzPushKit
|
||||
- name: Build complete unsigned iOS release
|
||||
run: cd mobile && flutter build ios --release --no-codesign --no-pub
|
||||
|
||||
results:
|
||||
name: Results
|
||||
if: ${{ always() }}
|
||||
needs: [web, mobile, mobile-swift]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
outputs:
|
||||
web_result: ${{ needs.web.result }}
|
||||
mobile_result: ${{ needs.mobile.result }}
|
||||
steps:
|
||||
- run: echo "Captured client job results"
|
||||
110
.github/workflows/_ci-desktop-macos.yml
vendored
Normal file
110
.github/workflows/_ci-desktop-macos.yml
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
name: CI / Desktop macOS
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
rust:
|
||||
required: true
|
||||
type: boolean
|
||||
desktop:
|
||||
required: true
|
||||
type: boolean
|
||||
desktop_rust:
|
||||
required: true
|
||||
type: boolean
|
||||
outputs:
|
||||
desktop_macos_result:
|
||||
value: ${{ jobs.results.outputs.desktop_macos_result }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
||||
|
||||
jobs:
|
||||
desktop-build-macos:
|
||||
name: Desktop Build (macOS)
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 45
|
||||
if: github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || 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:
|
||||
workspaces: desktop/src-tauri
|
||||
save-if: ${{ github.event_name != 'pull_request' }}
|
||||
- name: Install desktop dependencies
|
||||
run: just desktop-install-ci
|
||||
- name: Create sidecar placeholders
|
||||
run: |
|
||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||
mkdir -p desktop/src-tauri/binaries
|
||||
touch "desktop/src-tauri/binaries/buzz-acp-$TARGET"
|
||||
touch "desktop/src-tauri/binaries/buzz-agent-$TARGET"
|
||||
touch "desktop/src-tauri/binaries/buzz-backend-kubernetes-$TARGET"
|
||||
touch "desktop/src-tauri/binaries/buzz-dev-mcp-$TARGET"
|
||||
touch "desktop/src-tauri/binaries/git-credential-nostr-$TARGET"
|
||||
touch "desktop/src-tauri/binaries/buzz-$TARGET"
|
||||
# Mesh rev is derived from Cargo.lock so a dependency bump needs no
|
||||
# lockstep edit here; the cache key tracks it automatically.
|
||||
- name: Resolve mesh-llm rev
|
||||
id: mesh_rev
|
||||
run: |
|
||||
set -euo pipefail
|
||||
REV=$(python3 -c 'import tomllib; d=tomllib.load(open("Cargo.lock", "rb")); p=next(p for p in d["package"] if p["name"] == "mesh-llm-sdk"); print(p["source"].rsplit("#", 1)[1])')
|
||||
[[ -n "$REV" ]] || { echo "::error::could not resolve mesh-llm rev from Cargo.lock"; exit 1; }
|
||||
echo "rev=$REV" >> "$GITHUB_OUTPUT"
|
||||
echo "short=${REV:0:7}" >> "$GITHUB_OUTPUT"
|
||||
- name: Restore mesh llama build cache
|
||||
id: llama_cache
|
||||
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cache/mesh-llama
|
||||
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
|
||||
- name: Build mesh llama native libraries
|
||||
if: steps.llama_cache.outputs.cache-hit != 'true'
|
||||
env:
|
||||
MESH_REV_SHORT: ${{ steps.mesh_rev.outputs.short }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cargo fetch --manifest-path desktop/src-tauri/Cargo.toml
|
||||
SHORT="$MESH_REV_SHORT"
|
||||
MESH_ROOT=$(find "${CARGO_HOME:-$HOME/.cargo}/git/checkouts" -path "*/$SHORT" -type d -name "$SHORT" | head -1)
|
||||
if [[ -z "$MESH_ROOT" ]]; then
|
||||
echo "::error::mesh-llm checkout for $SHORT not found after cargo fetch"
|
||||
exit 1
|
||||
fi
|
||||
export LLAMA_STAGE_BACKEND=metal
|
||||
export LLAMA_STAGE_BUILD_DIR="$GITHUB_WORKSPACE/.cache/mesh-llama/build-stage-abi-metal"
|
||||
export CMAKE_OSX_DEPLOYMENT_TARGET=10.15
|
||||
"$MESH_ROOT/scripts/prepare-llama.sh" pinned
|
||||
"$MESH_ROOT/scripts/build-llama.sh" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15
|
||||
- name: Save mesh llama build cache
|
||||
if: steps.llama_cache.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
|
||||
with:
|
||||
path: ${{ github.workspace }}/.cache/mesh-llama
|
||||
key: mesh-llama-${{ runner.os }}-metal-${{ steps.mesh_rev.outputs.rev }}
|
||||
- name: Build Tauri app
|
||||
run: cd desktop && pnpm tauri build
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.15"
|
||||
CMAKE_OSX_DEPLOYMENT_TARGET: "10.15"
|
||||
LLAMA_STAGE_BACKEND: metal
|
||||
LLAMA_STAGE_BUILD_DIR: ${{ github.workspace }}/.cache/mesh-llama/build-stage-abi-metal
|
||||
SKIPPY_LLAMA_AUTO_BUILD: "0"
|
||||
|
||||
results:
|
||||
name: Results
|
||||
if: ${{ always() }}
|
||||
needs: [desktop-build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
outputs:
|
||||
desktop_macos_result: ${{ needs.desktop-build-macos.result }}
|
||||
steps:
|
||||
- run: echo "Captured Desktop Build (macOS) result"
|
||||
244
.github/workflows/_ci-desktop.yml
vendored
Normal file
244
.github/workflows/_ci-desktop.yml
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
name: CI / Desktop
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
rust:
|
||||
required: true
|
||||
type: boolean
|
||||
desktop:
|
||||
required: true
|
||||
type: boolean
|
||||
desktop_rust:
|
||||
required: true
|
||||
type: boolean
|
||||
outputs:
|
||||
desktop_result:
|
||||
value: ${{ jobs.results.outputs.desktop_result }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
||||
|
||||
jobs:
|
||||
desktop-core:
|
||||
name: Desktop Core
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
if: github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- uses: rui314/setup-mold@7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3 # v1
|
||||
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
||||
with:
|
||||
workspaces: desktop/src-tauri
|
||||
save-if: ${{ github.event_name != 'pull_request' }}
|
||||
- name: Install Tauri dependencies (Linux)
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
sudo apt-get update \
|
||||
-o Acquire::Retries=3 \
|
||||
-o Acquire::http::Timeout=30 \
|
||||
-o Acquire::https::Timeout=30
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
-o Acquire::Retries=3 \
|
||||
-o Acquire::http::Timeout=30 \
|
||||
-o Acquire::https::Timeout=30 \
|
||||
-o DPkg::Lock::Timeout=120 \
|
||||
build-essential \
|
||||
curl \
|
||||
file \
|
||||
libasound2-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
libgtk-3-dev \
|
||||
librsvg2-dev \
|
||||
libssl-dev \
|
||||
libwebkit2gtk-4.1-dev \
|
||||
libxdo-dev \
|
||||
patchelf \
|
||||
wget
|
||||
- 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: Desktop lint and format
|
||||
run: just desktop-check
|
||||
- name: Desktop unit tests
|
||||
run: just desktop-test
|
||||
- name: Desktop build
|
||||
run: just desktop-build
|
||||
- name: Desktop Tauri clippy
|
||||
run: just desktop-tauri-clippy
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
- name: Desktop Tauri check
|
||||
run: just desktop-tauri-check
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
- name: Desktop Tauri tests
|
||||
run: just desktop-tauri-test
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
- name: Desktop Tauri compiled-flag verification
|
||||
run: just desktop-tauri-test-compiled-flags
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
- name: Upload desktop e2e artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: desktop-e2e-artifacts
|
||||
path: |
|
||||
desktop/playwright-report
|
||||
desktop/test-results
|
||||
if-no-files-found: ignore
|
||||
- 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-smoke-e2e:
|
||||
name: Desktop Smoke E2E (${{ matrix.shard }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- 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: Desktop smoke e2e
|
||||
run: cd desktop && pnpm exec playwright test --project=smoke --shard=${{ matrix.shard }}/4
|
||||
- name: Summarize flaky tests
|
||||
if: ${{ !cancelled() }}
|
||||
run: node scripts/summarize-flaky-tests.mjs playwright-report.json "Desktop Smoke E2E (${{ matrix.shard }})"
|
||||
working-directory: desktop
|
||||
- name: Upload desktop smoke e2e artifacts
|
||||
if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
||||
with:
|
||||
name: desktop-smoke-e2e-artifacts-${{ matrix.shard }}
|
||||
path: |
|
||||
desktop/playwright-report
|
||||
desktop/playwright-report.json
|
||||
desktop/test-results
|
||||
if-no-files-found: ignore
|
||||
retention-days: 7
|
||||
|
||||
desktop:
|
||||
name: Desktop
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs: [desktop-core, desktop-smoke-e2e, desktop-windows-build]
|
||||
if: always() && (github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust)
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check desktop jobs
|
||||
run: |
|
||||
if [ "${{ needs.desktop-core.result }}" != "success" ]; then
|
||||
echo "Desktop Core finished with: ${{ needs.desktop-core.result }}"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ needs.desktop-smoke-e2e.result }}" != "success" ]; then
|
||||
echo "Desktop Smoke E2E shards finished with: ${{ needs.desktop-smoke-e2e.result }}"
|
||||
exit 1
|
||||
fi
|
||||
if [ "${{ needs.desktop-windows-build.result }}" != "success" ]; then
|
||||
echo "Desktop Windows Build finished with: ${{ needs.desktop-windows-build.result }}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Desktop jobs passed"
|
||||
|
||||
desktop-windows-build:
|
||||
name: Desktop Windows Build
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 20
|
||||
if: github.event_name == 'push' || inputs.desktop || inputs.desktop_rust || inputs.rust
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
||||
with:
|
||||
node-version: 24.14.1
|
||||
package-manager-cache: false
|
||||
- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
|
||||
with:
|
||||
version: 11.4.0
|
||||
- name: Install desktop dependencies
|
||||
shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build both protected-feature selections
|
||||
shell: pwsh
|
||||
run: |
|
||||
Remove-Item Env:VITE_BUZZ_BESTIE -ErrorAction SilentlyContinue
|
||||
pnpm -C desktop build
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
$env:VITE_BUZZ_BESTIE = "1"
|
||||
pnpm -C desktop build
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
results:
|
||||
name: Results
|
||||
if: ${{ always() }}
|
||||
needs: [desktop]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
outputs:
|
||||
desktop_result: ${{ needs.desktop.result }}
|
||||
steps:
|
||||
- run: echo "Captured Desktop job results"
|
||||
611
.github/workflows/_ci-relay.yml
vendored
Normal file
611
.github/workflows/_ci-relay.yml
vendored
Normal file
@@ -0,0 +1,611 @@
|
||||
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: 30
|
||||
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: 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:
|
||||
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:
|
||||
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: 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:
|
||||
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_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
|
||||
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"
|
||||
219
.github/workflows/_ci-rust.yml
vendored
Normal file
219
.github/workflows/_ci-rust.yml
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
name: CI / Rust
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
rust:
|
||||
required: true
|
||||
type: boolean
|
||||
desktop_rust:
|
||||
required: true
|
||||
type: boolean
|
||||
lane:
|
||||
required: true
|
||||
type: string
|
||||
outputs:
|
||||
rust_lint_result:
|
||||
value: ${{ jobs.results.outputs.rust_lint_result }}
|
||||
unit_tests_result:
|
||||
value: ${{ jobs.results.outputs.unit_tests_result }}
|
||||
windows_rust_result:
|
||||
value: ${{ jobs.results.outputs.windows_rust_result }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
||||
|
||||
jobs:
|
||||
rust-lint:
|
||||
name: Rust Lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.rust || inputs.desktop_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' }}
|
||||
- name: Format check
|
||||
run: just fmt-check
|
||||
- name: Desktop Tauri format check
|
||||
run: just desktop-tauri-fmt-check
|
||||
- name: Clippy
|
||||
run: just clippy
|
||||
|
||||
unit-tests:
|
||||
name: Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
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: rui314/setup-mold@7e4f20ad28a2e8ca6fd0892ccf72e2abb706b9c3 # v1
|
||||
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
||||
with:
|
||||
key: sherpa-cache-v1
|
||||
save-if: ${{ github.event_name != 'pull_request' }}
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
||||
with:
|
||||
tool: cargo-nextest@0.9.136
|
||||
- name: Unit tests
|
||||
run: just test-unit
|
||||
|
||||
server-cross-compile:
|
||||
name: Server Cross-Compile
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
if: inputs.lane == 'cross-compile' && (github.event_name == 'push' || inputs.rust)
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-musl
|
||||
- aarch64-unknown-linux-musl
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
||||
with:
|
||||
key: cross-${{ matrix.target }}
|
||||
save-if: ${{ github.event_name != 'pull_request' }}
|
||||
- name: Install cross
|
||||
uses: taiki-e/install-action@0fd46367812ee04360509b4169d9f659d6892bb2 # v2.79.15
|
||||
with:
|
||||
tool: cross@0.2.5
|
||||
- name: Build server binaries
|
||||
env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
# PRs: compile + build-script gate only (no codegen/link). Main: full link gate.
|
||||
CARGO_CMD: ${{ github.event_name == 'pull_request' && 'check' || 'build' }}
|
||||
run: |
|
||||
cross "$CARGO_CMD" --release --target "$TARGET" \
|
||||
-p buzz-relay \
|
||||
-p buzz-acp \
|
||||
-p buzz-agent \
|
||||
-p buzz-dev-mcp \
|
||||
-p git-credential-nostr \
|
||||
-p git-sign-nostr
|
||||
|
||||
windows-rust:
|
||||
name: Windows Rust (x86_64-pc-windows-msvc)
|
||||
runs-on: windows-latest
|
||||
# Windows runners are slow and this compiles the workspace + Tauri crate
|
||||
# cold across four steps; budget generously.
|
||||
timeout-minutes: 45
|
||||
if: inputs.lane == 'required' && (github.event_name == 'push' || inputs.rust || inputs.desktop_rust)
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
TARGET: x86_64-pc-windows-msvc
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
# MSVC needs windows.h (aws-lc-sys et al.), so this runs on a real Windows
|
||||
# runner — hermit, used by the Linux jobs, does not provide MSVC. The
|
||||
# toolchain (1.95.0 + clippy via profile = default) comes from the
|
||||
# repo-root rust-toolchain.toml, which the runner's preinstalled rustup
|
||||
# honors on demand; the host triple already is x86_64-pc-windows-msvc.
|
||||
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32
|
||||
with:
|
||||
workspaces: |
|
||||
.
|
||||
desktop/src-tauri
|
||||
key: windows-msvc
|
||||
save-if: ${{ github.event_name != 'pull_request' }}
|
||||
# Tauri validates externalBin at compile time, so the Tauri-crate steps
|
||||
# below fail without these stubs. Mirrors scripts/bundle-sidecars.sh's
|
||||
# Windows naming (binaries/<bin>-<triple>.exe); empty files suffice for a
|
||||
# type-check since nothing executes them.
|
||||
- name: Create sidecar placeholders
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p desktop/src-tauri/binaries
|
||||
for bin in buzz-acp buzz-agent buzz-dev-mcp git-credential-nostr buzz; do
|
||||
touch "desktop/src-tauri/binaries/${bin}-${TARGET}.exe"
|
||||
done
|
||||
- name: Clippy (workspace)
|
||||
run: cargo clippy --workspace --all-targets --target $env:TARGET -- -D warnings
|
||||
- name: Check (workspace)
|
||||
run: cargo check --workspace --all-targets --target $env:TARGET
|
||||
- name: Test (buzz-dev-mcp)
|
||||
# The Windows-only bash resolver lives in buzz-dev-mcp; its unit tests
|
||||
# only gate if this crate is tested ON Windows.
|
||||
# Serial: windows_resolver_tests mutate process-global env
|
||||
# (BUZZ_SHELL/GIT_BASH/SystemRoot) that SharedState::new reads.
|
||||
run: cargo test -p buzz-dev-mcp --target $env:TARGET -- --test-threads=1
|
||||
- name: Test (buzz-agent auth coordinator)
|
||||
# The auth coordinator single-flights on an OS advisory lock, which is
|
||||
# LockFileEx on Windows; this integration suite drives real second
|
||||
# processes on the same lock file, so it only exercises the Windows
|
||||
# lock runtime if it runs ON Windows. Every other job compiles it but
|
||||
# never executes it. Tests exercised on Windows: lock serialization
|
||||
# (two coordinators race for the same key), cooldown sidecar sharing
|
||||
# across processes, attempt-sidecar adoption (UserInitiated waiter
|
||||
# adopts a predecessor's denial), and the in-process single-flight for
|
||||
# same-key coalescing. Tests that are UNIX-ONLY and NOT executed here:
|
||||
# crash-release (flock drop on SIGKILL, guarded by #[cfg(unix)]) and
|
||||
# cross-process cache success/race (on-disk token handoff, also
|
||||
# #[cfg(unix)]).
|
||||
run: cargo test -p buzz-agent --target $env:TARGET --test databricks_auth_coordinator
|
||||
# Smoke-test the new host-prereq contract: Git for Windows (which provides
|
||||
# bash) is available on the runner, a shell command round-trips, and bash
|
||||
# does NOT resolve from System32 (so WSL's launcher is never picked up).
|
||||
# windows-latest runners have Git for Windows pre-installed; the unit tests
|
||||
# above exercise the MCP resolver itself. This step verifies the host env.
|
||||
- name: Smoke-test host Git Bash prereq (host env check)
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Git for Windows ships bash.exe under its bin/ directory; confirm it
|
||||
# resolves from the standard location the runtime resolver probes first.
|
||||
bash_path=$(command -v bash 2>/dev/null || true)
|
||||
[[ -n "$bash_path" ]] || { echo "ERROR: bash not found on PATH — host Git for Windows missing" >&2; exit 1; }
|
||||
echo "Resolved bash: $bash_path"
|
||||
[[ "$bash_path" != *System32* ]] || { echo "ERROR: resolved bash is WSL's System32 launcher" >&2; exit 1; }
|
||||
|
||||
# Run a basic pipeline through the resolved bash (same invocation the
|
||||
# agent uses: bash -c '...').
|
||||
out=$(bash -c 'echo hello | tr a-z A-Z')
|
||||
[[ "$out" == "HELLO" ]] || { echo "bash pipeline failed: got '$out'" >&2; exit 1; }
|
||||
|
||||
# Confirm git itself works — agents run git commands frequently.
|
||||
git --version
|
||||
repo=$(mktemp -d)
|
||||
cd "$repo"
|
||||
git init -q
|
||||
git -c user.name=ci -c user.email=ci@example.com commit -q --allow-empty -m smoke
|
||||
git log -1 --format=%s | grep -qx smoke
|
||||
echo "Host bash resolved and functional; git commit round-trip passed"
|
||||
- name: Check (Tauri crate)
|
||||
run: cargo check --manifest-path desktop/src-tauri/Cargo.toml --workspace --all-targets --target $env:TARGET
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
- name: Test (Tauri crate)
|
||||
run: cargo test --manifest-path desktop/src-tauri/Cargo.toml --target $env:TARGET
|
||||
env:
|
||||
CMAKE_POLICY_VERSION_MINIMUM: "3.5"
|
||||
|
||||
|
||||
results:
|
||||
name: Results
|
||||
if: ${{ always() }}
|
||||
needs: [rust-lint, unit-tests, server-cross-compile, windows-rust]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
outputs:
|
||||
rust_lint_result: ${{ needs.rust-lint.result }}
|
||||
unit_tests_result: ${{ needs.unit-tests.result }}
|
||||
windows_rust_result: ${{ needs.windows-rust.result }}
|
||||
steps:
|
||||
- run: echo "Captured Rust job results"
|
||||
42
.github/workflows/_ci-security.yml
vendored
Normal file
42
.github/workflows/_ci-security.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: CI / Security
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
rust:
|
||||
required: true
|
||||
type: boolean
|
||||
outputs:
|
||||
security_result:
|
||||
value: ${{ jobs.results.outputs.security_result }}
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
||||
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
||||
|
||||
jobs:
|
||||
security:
|
||||
name: Security
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
if: github.event_name == 'push' || inputs.rust
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
||||
- name: Dependency policy
|
||||
run: cargo-deny check
|
||||
|
||||
|
||||
results:
|
||||
name: Results
|
||||
if: ${{ always() }}
|
||||
needs: [security]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions: {}
|
||||
outputs:
|
||||
security_result: ${{ needs.security.result }}
|
||||
steps:
|
||||
- run: echo "Captured Security job results"
|
||||
1401
.github/workflows/ci.yml
vendored
1401
.github/workflows/ci.yml
vendored
File diff suppressed because it is too large
Load Diff
83
scripts/test-ci-required-context-isolation.sh
Executable file
83
scripts/test-ci-required-context-isolation.sh
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||
orchestrator="$repo_root/.github/workflows/ci.yml"
|
||||
desktop_workflow="$repo_root/.github/workflows/_ci-desktop.yml"
|
||||
macos_workflow="$repo_root/.github/workflows/_ci-desktop-macos.yml"
|
||||
rust_workflow="$repo_root/.github/workflows/_ci-rust.yml"
|
||||
relay_workflow="$repo_root/.github/workflows/_ci-relay.yml"
|
||||
clients_workflow="$repo_root/.github/workflows/_ci-clients.yml"
|
||||
|
||||
fail() {
|
||||
echo "CI required-context isolation contract failed: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$macos_workflow" ]] || fail "missing _ci-desktop-macos.yml"
|
||||
|
||||
extract_job() {
|
||||
local job=$1
|
||||
local workflow=$2
|
||||
awk -v job="$job" '
|
||||
$0 == " " job ":" { found = 1 }
|
||||
found && $0 ~ /^ [a-zA-Z0-9_-]+:$/ && $0 != " " job ":" { exit }
|
||||
found { print }
|
||||
' "$workflow"
|
||||
}
|
||||
|
||||
desktop_call=$(extract_job desktop-domain "$orchestrator")
|
||||
macos_call=$(extract_job desktop-macos-domain "$orchestrator")
|
||||
rust_call=$(extract_job rust "$orchestrator")
|
||||
rust_cross_compile_call=$(extract_job rust-cross-compile-domain "$orchestrator")
|
||||
relay_call=$(extract_job relay-domain "$orchestrator")
|
||||
relay_artifacts_call=$(extract_job relay-artifacts-domain "$orchestrator")
|
||||
postgres_call=$(extract_job postgres-domain "$orchestrator")
|
||||
clients_call=$(extract_job clients "$orchestrator")
|
||||
mobile_swift_call=$(extract_job mobile-swift-domain "$orchestrator")
|
||||
desktop_gate=$(extract_job desktop "$orchestrator")
|
||||
macos_gate=$(extract_job desktop-build-macos "$orchestrator")
|
||||
|
||||
[[ "$desktop_call" == *'uses: ./.github/workflows/_ci-desktop.yml'* ]] ||
|
||||
fail "Desktop Domain must call _ci-desktop.yml"
|
||||
[[ "$macos_call" == *'uses: ./.github/workflows/_ci-desktop-macos.yml'* ]] ||
|
||||
fail "Desktop macOS domain must call _ci-desktop-macos.yml"
|
||||
[[ "$desktop_gate" == *'needs: [changes, desktop-domain]'* ]] ||
|
||||
fail "Desktop required check must depend only on Desktop Domain"
|
||||
[[ "$desktop_gate" == *'needs.desktop-domain.outputs.desktop_result'* ]] ||
|
||||
fail "Desktop required check must read the Desktop Domain result"
|
||||
[[ "$macos_gate" == *'needs: [changes, desktop-macos-domain]'* ]] ||
|
||||
fail "Desktop Build (macOS) required check must depend only on the macOS domain"
|
||||
[[ "$macos_gate" == *'needs.desktop-macos-domain.outputs.desktop_macos_result'* ]] ||
|
||||
fail "Desktop Build (macOS) required check must read the macOS domain result"
|
||||
|
||||
[[ "$rust_call" == *'lane: required'* && "$rust_cross_compile_call" == *'lane: cross-compile'* ]] ||
|
||||
fail "required Rust checks and server cross-compiles must use isolated calls"
|
||||
[[ "$relay_artifacts_call" == *'lane: artifacts'* && "$relay_call" == *'lane: required'* && "$postgres_call" == *'lane: postgres'* ]] ||
|
||||
fail "relay artifacts, required checks, and PostgreSQL tests must use isolated calls"
|
||||
[[ "$relay_call" == *'needs: [changes, relay-artifacts-domain]'* ]] ||
|
||||
fail "required relay checks must wait for the artifact producer"
|
||||
[[ "$postgres_call" == *'needs: [changes, relay-artifacts-domain]'* ]] ||
|
||||
fail "PostgreSQL tests must wait for the artifact producer"
|
||||
[[ "$clients_call" == *'lane: required'* && "$mobile_swift_call" == *'lane: mobile-swift'* ]] ||
|
||||
fail "required client checks and Mobile Swift must use isolated calls"
|
||||
|
||||
grep -Fq "inputs.lane == 'cross-compile'" "$rust_workflow" ||
|
||||
fail "Rust cross-compiles must be selected by their isolated lane"
|
||||
grep -Fq "inputs.lane == 'postgres'" "$relay_workflow" ||
|
||||
fail "PostgreSQL tests must be selected by their isolated lane"
|
||||
grep -Fq "inputs.lane == 'artifacts'" "$relay_workflow" ||
|
||||
fail "relay artifacts must be selected by their isolated lane"
|
||||
grep -Fq "inputs.lane == 'mobile-swift'" "$clients_workflow" ||
|
||||
fail "Mobile Swift must be selected by its isolated lane"
|
||||
|
||||
grep -Fq 'on:' "$macos_workflow" || fail "macOS workflow is missing its trigger"
|
||||
grep -Fq ' workflow_call:' "$macos_workflow" || fail "macOS workflow must use workflow_call"
|
||||
grep -Fq ' desktop-build-macos:' "$macos_workflow" || fail "macOS build job is missing"
|
||||
grep -Fq 'desktop_macos_result:' "$macos_workflow" || fail "macOS result output is missing"
|
||||
|
||||
if grep -Eq '^ desktop-build-macos:|desktop_macos_result:' "$desktop_workflow"; then
|
||||
fail "Desktop Domain must not own the isolated macOS required check"
|
||||
fi
|
||||
|
||||
echo "CI required-context isolation contract passed"
|
||||
@@ -139,7 +139,7 @@ YAML
|
||||
run_contract >/dev/null
|
||||
rm -rf "$tmp/.github/actions" "$tmp/.github/workflows/new-cache-user.yaml"
|
||||
|
||||
python3 - "$tmp/.github/workflows/ci.yml" <<'PY'
|
||||
python3 - "$tmp/.github/workflows/_ci-rust.yml" <<'PY'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
|
||||
@@ -91,9 +91,9 @@ cache_uses.each do |path, location, ref|
|
||||
end
|
||||
end
|
||||
|
||||
ci_path = root / ".github" / "workflows" / "ci.yml"
|
||||
ci = workflows.fetch(ci_path) { load_workflow.call(ci_path) }
|
||||
jobs = ci["jobs"]
|
||||
rust_ci_path = root / ".github" / "workflows" / "_ci-rust.yml"
|
||||
rust_ci = workflows.fetch(rust_ci_path) { load_workflow.call(rust_ci_path) }
|
||||
jobs = rust_ci["jobs"]
|
||||
unit_tests = jobs.is_a?(Hash) ? jobs["unit-tests"] : nil
|
||||
abort "unit-tests job missing" unless unit_tests.is_a?(Hash)
|
||||
steps = unit_tests["steps"]
|
||||
|
||||
Reference in New Issue
Block a user