mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
## 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>
410 lines
16 KiB
YAML
410 lines
16 KiB
YAML
name: CI
|
|
on:
|
|
push:
|
|
branches: [main, release]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
env:
|
|
CARGO_TERM_COLOR: always
|
|
BUZZ_TEST_POSTGRES_PASSWORD: buzz_dev
|
|
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.cache/ms-playwright
|
|
|
|
jobs:
|
|
changes:
|
|
name: Detect Changed Paths
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 2
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
outputs:
|
|
rust: ${{ steps.filter.outputs.rust }}
|
|
desktop: ${{ steps.filter.outputs.desktop }}
|
|
desktop-rust: ${{ steps.filter.outputs.desktop-rust }}
|
|
web: ${{ steps.filter.outputs.web }}
|
|
mobile: ${{ steps.filter.outputs.mobile }}
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
with:
|
|
fetch-depth: 2
|
|
- uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1
|
|
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
|
|
id: filter
|
|
with:
|
|
token: ''
|
|
filters: |
|
|
rust:
|
|
- 'crates/**'
|
|
- 'migrations/**'
|
|
- 'schema/**'
|
|
- 'Cargo.toml'
|
|
- 'Cargo.lock'
|
|
- 'rust-toolchain.toml'
|
|
- '.config/nextest.toml'
|
|
- 'scripts/postgres-test-*.sh'
|
|
- 'scripts/reconcile-schema-after-pgschema.sql'
|
|
- 'bin/pgschema'
|
|
- 'bin/.pgschema-*.pkg'
|
|
- 'scripts/check-postgres-test-discovery.py'
|
|
- 'scripts/test-postgres-test-discovery.sh'
|
|
- 'scripts/test-postgres-test-wrapper.sh'
|
|
- 'deny.toml'
|
|
- '.github/workflows/ci.yml'
|
|
- '.github/workflows/_ci-*.yml'
|
|
- 'scripts/run-tests.sh'
|
|
- 'scripts/model-capabilities.json'
|
|
- 'scripts/normative-corpus.json'
|
|
- 'justfile'
|
|
desktop:
|
|
- 'scripts/model-capabilities.json'
|
|
- 'scripts/normative-corpus.json'
|
|
- 'desktop/**'
|
|
- '!desktop/src-tauri/**'
|
|
- 'pnpm-lock.yaml'
|
|
desktop-rust:
|
|
- 'desktop/src-tauri/**'
|
|
web:
|
|
- 'web/**'
|
|
- 'pnpm-lock.yaml'
|
|
mobile:
|
|
- 'mobile/**'
|
|
- 'scripts/mobile-release.sh'
|
|
- 'scripts/mobile-worktree-overrides.sh'
|
|
- 'scripts/mobile-worktree-clean.sh'
|
|
- 'scripts/publish-mobile-release-candidate.sh'
|
|
- 'scripts/release-rulesets.sh'
|
|
- 'scripts/test-mobile-release-contract.sh'
|
|
- 'scripts/test-mobile-release-candidate-publisher.sh'
|
|
- 'scripts/test-mobile-worktree-overrides.sh'
|
|
- '.github/workflows/mobile-release-candidate.yml'
|
|
- '.github/workflows/ci.yml'
|
|
- '.github/workflows/_ci-*.yml'
|
|
- name: Validate PostgreSQL test discovery
|
|
if: github.event_name == 'push' || steps.filter.outputs.rust == 'true'
|
|
run: |
|
|
scripts/test-postgres-test-discovery.sh
|
|
scripts/test-postgres-test-wrapper.sh
|
|
- name: Release workflow source contract
|
|
run: scripts/test-release-ref-contract.sh
|
|
- name: Relay image eligibility contract
|
|
run: scripts/test-relay-image-eligibility-workflow.sh
|
|
- name: Desktop release candidate contract
|
|
run: scripts/test-desktop-release-candidate.sh
|
|
- name: OSS desktop promotion contract
|
|
run: |
|
|
scripts/test-oss-desktop-promotion.sh
|
|
scripts/test-oss-desktop-promotion-behavior.sh
|
|
- name: Mobile release contract
|
|
run: |
|
|
scripts/test-mobile-release-contract.sh
|
|
scripts/test-mobile-release-candidate-publisher.sh
|
|
- name: Desktop instance environment contract
|
|
run: scripts/test-desktop-instance-detection.sh
|
|
- name: Mobile worktree identity contract
|
|
run: scripts/test-mobile-worktree-overrides.sh
|
|
- name: Codex security review contract
|
|
run: just security-review-check
|
|
- name: Rust cache contract
|
|
run: |
|
|
scripts/test-rust-cache-contract.sh
|
|
scripts/test-rust-cache-contract-regressions.sh
|
|
- name: CI required-context isolation contract
|
|
run: scripts/test-ci-required-context-isolation.sh
|
|
- name: File size policy
|
|
run: just file-size-check
|
|
|
|
dead-token-guard:
|
|
name: Dead Token Reference Guard
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
- name: Check for dead API token references in client code
|
|
run: |
|
|
# Fail if dead API token patterns reappear in desktop, mobile, docs, or config.
|
|
# Relay crates are excluded — they still use token auth internally.
|
|
PATTERNS='TokenScope|MintTokenResponse|hasApiToken|spr_tok_'
|
|
PATHS='desktop/src/ desktop/tests/ mobile/test/ mobile/lib/ .env.example'
|
|
EXCLUDES='--exclude-dir=node_modules --exclude-dir=.dart_tool'
|
|
if grep -rn $EXCLUDES -E "$PATTERNS" $PATHS 2>/dev/null; then
|
|
echo "::error::Dead API token references found in client code. See above."
|
|
exit 1
|
|
fi
|
|
echo "No dead token references found."
|
|
|
|
rust:
|
|
name: Rust
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true'
|
|
uses: ./.github/workflows/_ci-rust.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
lane: required
|
|
|
|
rust-cross-compile-domain:
|
|
name: Rust Cross-Compile
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-rust.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
lane: cross-compile
|
|
|
|
desktop-domain:
|
|
name: Desktop Domain
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-desktop.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop: ${{ needs.changes.outputs.desktop == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
|
|
relay-artifacts-domain:
|
|
name: Relay Artifact Producer
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-relay.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop: ${{ needs.changes.outputs.desktop == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
lane: artifacts
|
|
|
|
postgres-domain:
|
|
name: PostgreSQL Domain
|
|
needs: [changes, relay-artifacts-domain]
|
|
if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-relay.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop: ${{ needs.changes.outputs.desktop == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
lane: postgres
|
|
|
|
desktop-macos-domain:
|
|
name: Desktop macOS Domain
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-desktop-macos.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop: ${{ needs.changes.outputs.desktop == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
|
|
relay-domain:
|
|
name: Relay and PostgreSQL
|
|
needs: [changes, relay-artifacts-domain]
|
|
if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-relay.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
desktop: ${{ needs.changes.outputs.desktop == 'true' }}
|
|
desktop_rust: ${{ needs.changes.outputs.desktop-rust == 'true' }}
|
|
lane: required
|
|
|
|
clients:
|
|
name: Clients
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.web == 'true' || needs.changes.outputs.mobile == 'true'
|
|
uses: ./.github/workflows/_ci-clients.yml
|
|
with:
|
|
web: ${{ needs.changes.outputs.web == 'true' }}
|
|
mobile: ${{ needs.changes.outputs.mobile == 'true' }}
|
|
lane: required
|
|
|
|
mobile-swift-domain:
|
|
name: Mobile Swift Domain
|
|
needs: [changes]
|
|
if: needs.changes.outputs.mobile == 'true'
|
|
uses: ./.github/workflows/_ci-clients.yml
|
|
with:
|
|
web: ${{ needs.changes.outputs.web == 'true' }}
|
|
mobile: ${{ needs.changes.outputs.mobile == 'true' }}
|
|
lane: mobile-swift
|
|
|
|
security-domain:
|
|
name: Security Domain
|
|
needs: [changes]
|
|
if: github.event_name == 'push' || needs.changes.outputs.rust == 'true'
|
|
uses: ./.github/workflows/_ci-security.yml
|
|
with:
|
|
rust: ${{ needs.changes.outputs.rust == 'true' }}
|
|
|
|
rust-lint:
|
|
name: Rust Lint
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true')
|
|
needs: [changes, rust]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Rust Lint result
|
|
env:
|
|
RESULT: ${{ needs.rust.outputs.rust_lint_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
unit-tests:
|
|
name: Unit Tests
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, rust]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Unit Tests result
|
|
env:
|
|
RESULT: ${{ needs.rust.outputs.unit_tests_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
windows-rust:
|
|
name: Windows Rust (x86_64-pc-windows-msvc)
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true')
|
|
needs: [changes, rust]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Windows Rust result
|
|
env:
|
|
RESULT: ${{ needs.rust.outputs.windows_rust_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
desktop:
|
|
name: Desktop
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, desktop-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Desktop result
|
|
env:
|
|
RESULT: ${{ needs.desktop-domain.outputs.desktop_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
desktop-build-macos:
|
|
name: Desktop Build (macOS)
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, desktop-macos-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Desktop Build (macOS) result
|
|
env:
|
|
RESULT: ${{ needs.desktop-macos-domain.outputs.desktop_macos_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
desktop-e2e-relay:
|
|
name: Desktop E2E Relay
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, relay-artifacts-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Desktop E2E Relay result
|
|
env:
|
|
RESULT: ${{ needs.relay-artifacts-domain.outputs.desktop_e2e_relay_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
desktop-e2e-integration:
|
|
name: Desktop E2E Integration
|
|
if: always() && needs.changes.result == 'success' && needs.relay-artifacts-domain.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, relay-artifacts-domain, relay-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Desktop E2E Integration result
|
|
env:
|
|
RESULT: ${{ needs.relay-domain.outputs.desktop_e2e_integration_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
backend-integration:
|
|
name: Backend Integration (relay e2e)
|
|
if: always() && needs.changes.result == 'success' && needs.relay-artifacts-domain.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, relay-artifacts-domain, relay-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Backend Integration result
|
|
env:
|
|
RESULT: ${{ needs.relay-domain.outputs.backend_integration_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
postgres-tests:
|
|
name: PostgreSQL Tests
|
|
if: always() && needs.changes.result == 'success' && needs.relay-artifacts-domain.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, relay-artifacts-domain, postgres-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check PostgreSQL Tests result
|
|
env:
|
|
RESULT: ${{ needs.postgres-domain.outputs.postgres_tests_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
relay-e2e:
|
|
name: Relay E2E
|
|
if: always() && needs.changes.result == 'success' && needs.relay-artifacts-domain.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, relay-artifacts-domain, relay-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Relay E2E result
|
|
env:
|
|
RESULT: ${{ needs.relay-domain.outputs.relay_e2e_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
web:
|
|
name: Web
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.web == 'true')
|
|
needs: [changes, clients]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Web result
|
|
env:
|
|
RESULT: ${{ needs.clients.outputs.web_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
mobile:
|
|
name: Mobile
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.mobile == 'true')
|
|
needs: [changes, clients]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Mobile result
|
|
env:
|
|
RESULT: ${{ needs.clients.outputs.mobile_result }}
|
|
run: test "$RESULT" = success
|
|
|
|
security:
|
|
name: Security
|
|
if: always() && needs.changes.result == 'success' && (github.event_name == 'push' || needs.changes.outputs.rust == 'true')
|
|
needs: [changes, security-domain]
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
permissions: {}
|
|
steps:
|
|
- name: Check Security result
|
|
env:
|
|
RESULT: ${{ needs.security-domain.outputs.security_result }}
|
|
run: test "$RESULT" = success
|