chore(deps): update all non-major dependencies (patch) #7891
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| 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 | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| with: | |
| filters: | | |
| rust: | |
| - 'crates/**' | |
| - 'migrations/**' | |
| - 'schema/**' | |
| - 'Cargo.toml' | |
| - 'Cargo.lock' | |
| - 'rust-toolchain.toml' | |
| - 'deny.toml' | |
| - '.github/workflows/ci.yml' | |
| - 'scripts/run-tests.sh' | |
| - 'justfile' | |
| desktop: | |
| - 'desktop/**' | |
| - '!desktop/src-tauri/**' | |
| - 'pnpm-lock.yaml' | |
| desktop-rust: | |
| - 'desktop/src-tauri/**' | |
| web: | |
| - 'web/**' | |
| - 'pnpm-lock.yaml' | |
| mobile: | |
| - 'mobile/**' | |
| rust-lint: | |
| name: Rust Lint | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| 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 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| 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 | |
| desktop-core: | |
| name: Desktop Core | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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 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: 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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: 20 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4] | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} | |
| key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }} | |
| - name: Desktop build | |
| run: just desktop-build | |
| - name: Desktop smoke e2e | |
| run: cd desktop && pnpm exec playwright test --project=smoke --shard=${{ matrix.shard }}/4 | |
| - name: Upload desktop smoke e2e artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: desktop-smoke-e2e-artifacts-${{ matrix.shard }} | |
| path: | | |
| desktop/playwright-report | |
| desktop/test-results | |
| if-no-files-found: ignore | |
| desktop: | |
| name: Desktop | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes, desktop-core, desktop-smoke-e2e] | |
| if: always() && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true') | |
| 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 | |
| echo "Desktop jobs passed" | |
| desktop-e2e-relay: | |
| name: Desktop E2E Relay | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| workspaces: | | |
| . | |
| desktop/src-tauri | |
| save-if: ${{ github.event_name != 'pull_request' }} | |
| - name: Build relay | |
| run: cargo build --profile ci -p buzz-relay | |
| - name: Upload relay binary | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: desktop-e2e-relay | |
| path: target/ci/buzz-relay | |
| if-no-files-found: error | |
| retention-days: 1 | |
| desktop-e2e-integration-shard: | |
| name: Desktop E2E Integration (${{ matrix.shard }}/2) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: [changes, desktop-e2e-relay] | |
| if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2] | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 | |
| with: | |
| path: ${{ env.PLAYWRIGHT_BROWSERS_PATH }} | |
| key: playwright-${{ runner.os }}-${{ steps.pw-version.outputs.version }} | |
| - name: Desktop build | |
| run: just desktop-build | |
| - 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/attach-schema-partitions.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_dev@localhost:5432/buzz \ | |
| REDIS_URL=redis://localhost:6379 \ | |
| RELAY_URL=ws://localhost:3000 \ | |
| BUZZ_BIND_ADDR=0.0.0.0:3000 \ | |
| 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: 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: Upload desktop integration artifacts | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: desktop-e2e-integration-artifacts-${{ matrix.shard }} | |
| path: | | |
| desktop/playwright-report | |
| desktop/test-results | |
| /tmp/buzz-relay.log | |
| if-no-files-found: ignore | |
| - name: Save pnpm store cache | |
| if: github.event_name == 'push' | |
| uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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: [changes, desktop-e2e-integration-shard] | |
| if: always() && (github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true') | |
| 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 | |
| needs: [changes, desktop-e2e-relay] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| save-if: ${{ github.event_name != 'pull_request' }} | |
| - 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 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 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/attach-schema-partitions.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_dev@localhost:5432/buzz \ | |
| REDIS_URL=redis://localhost:6379 \ | |
| RELAY_URL=ws://localhost:3000 \ | |
| BUZZ_BIND_ADDR=0.0.0.0:3000 \ | |
| 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 test --profile ci -p buzz-test-client --test e2e_event_reminder -- --ignored | |
| 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 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| with: | |
| save-if: ${{ github.event_name != 'pull_request' }} | |
| - name: Start relay | |
| run: ./scripts/start-relay-for-tests.sh | |
| - name: Relay E2E tests | |
| run: cargo test -p buzz-test-client --test e2e_persona --test e2e_nostr_interop -- --ignored --nocapture | |
| env: | |
| RELAY_URL: ws://localhost:3000 | |
| GIT_CREDENTIAL_NOSTR_BIN: ${{ github.workspace }}/target/ci/git-credential-nostr | |
| - 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 | |
| web: | |
| name: Web | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.web == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.mobile == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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 | |
| security: | |
| name: Security | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - name: Dependency policy | |
| run: cargo-deny 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 | |
| - 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." | |
| server-cross-compile: | |
| name: Server Cross-Compile | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - x86_64-unknown-linux-musl | |
| - aarch64-unknown-linux-musl | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| 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 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.rust == 'true' || needs.changes.outputs.desktop-rust == 'true' | |
| permissions: | |
| contents: read | |
| env: | |
| TARGET: x86_64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| # 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 # v2 | |
| 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. | |
| run: cargo test -p buzz-dev-mcp --target $env:TARGET | |
| # The bundled-bash staging (PortableGit download + SFX extract of the WHOLE | |
| # tree, including mingw64/) runs only in release.yml on tag — so without this | |
| # step the agent's only Windows transport would ship UNEXERCISED until a | |
| # tagged release hits users. Stage the tree and spawn the LAUNCHER bash | |
| # (bin/bash.exe, the entry the resolver now uses) to prove all of: the SFX | |
| # `-o` POSIX-path extraction works; the launcher spawns and sets up | |
| # MSYSTEM/PATH; the lazily-loaded MSYS DLL closure survives; and — the whole | |
| # point of bundling the full toolchain — git/jq/curl resolve from the bundled | |
| # mingw64/bin and git is functional (a real commit round-trips). The launcher | |
| # prepending mingw64/bin to PATH under `-c` is the load-bearing behavior that | |
| # only a real Windows host can confirm; this gate is where it gets proven. | |
| # (End-to-end nostr-SIGNED commits compose build_git_env's GIT_CONFIG_* with | |
| # the git-sign-nostr helper — covered by buzz-dev-mcp unit tests + a bare-host | |
| # check, not here, since this job stages only empty sidecar placeholders.) | |
| - name: Smoke-test bundled bash + git toolchain staging | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| stage_dir="$RUNNER_TEMP/git-bash" | |
| scripts/stage-windows-bash.sh "$stage_dir" | |
| launcher="$stage_dir/bin/bash.exe" | |
| [[ -f "$launcher" ]] || { echo "launcher bash missing: $launcher" >&2; exit 1; } | |
| # Faithfulness to the agent: shell.rs spawns the launcher with PATH set | |
| # wholesale to the shim PATH (shim tempdir + the MCP process's inherited | |
| # PATH). We strip PATH down to just the Windows system dir before | |
| # spawning — stricter than a bare host (ambient PATH stripped to force | |
| # resolution through the launcher). This stops the runner's ambient | |
| # git/jq from masking the launcher's own mingw64/bin prepend, so anything | |
| # that resolves had to come from the launcher itself. The fidelity gap is | |
| # benign: the real shim tempdir only ever holds rg/tree/buzz and the two | |
| # nostr helpers, never git/jq/curl, so a richer real PATH cannot shadow | |
| # mingw64/bin for these three tools. | |
| win_root="${SYSTEMROOT:-${SystemRoot:-C:\\Windows}}" | |
| bare_path="$win_root\\System32;$win_root" | |
| # Coreutils pipeline through the launcher (the resolver's entry point). | |
| out=$(PATH="$bare_path" "$launcher" -c 'echo hello | tr a-z A-Z') | |
| [[ "$out" == "HELLO" ]] || { echo "staged bash pipeline failed: got '$out'" >&2; exit 1; } | |
| # The full-toolchain payoff: git/jq/curl must resolve from the bundle's | |
| # mingw64/bin or usr/bin. Use NON-login `-c` to match the agent's actual | |
| # invocation (shell.rs spawns `bash -c`): if the launcher only fixes PATH | |
| # under a login shell, `-lc` would pass here while the real agent stays | |
| # broken — the exact green-but-broken trap this gate guards. | |
| # | |
| # Anchor to the ACTUAL staged tree, not a mount-shape: command -v reports | |
| # the MSYS mount path (/mingw64/bin/git), so we cygpath -m it back to the | |
| # real Windows location and assert it sits under $stage_dir. A bare | |
| # `/mingw64/*` shape check would pass for any /mingw64-rooted mount; the | |
| # cygpath round-trip proves the tool is literally inside the bundle we | |
| # just staged. cygpath ships in the bundle's usr/bin (MSYS2 core, same | |
| # tier as the `tr` proven above), so it resolves under the launcher. | |
| # | |
| # curl is the one tool of the three with a System32 twin | |
| # (C:\Windows\System32\curl.exe, on $bare_path) — if a future PortableGit | |
| # ever drops curl from mingw64/bin, the launcher would resolve the | |
| # System32 twin and this REDs on a non-bug; git/jq have no such twin. | |
| stage_m=$("$launcher" -c "cygpath -m '$stage_dir'") | |
| for tool in git jq curl; do | |
| located=$(PATH="$bare_path" "$launcher" -c "command -v $tool >/dev/null 2>&1 && cygpath -m \"\$(command -v $tool)\"" || true) | |
| [[ -n "$located" ]] || { echo "$tool did not resolve in bundled bash" >&2; exit 1; } | |
| case "$located" in | |
| "$stage_m"/*) ;; | |
| *) echo "$tool resolved outside the staged bundle: $located (stage: $stage_m)" >&2; exit 1 ;; | |
| esac | |
| echo "$tool -> $located" | |
| done | |
| # git is not just present but functional: a real commit round-trips, | |
| # again through non-login `-c` with the bare PATH. Single-quoted on | |
| # purpose — this script body runs in the launcher's shell, not ours. | |
| # shellcheck disable=SC2016 | |
| PATH="$bare_path" "$launcher" -c ' | |
| set -euo pipefail | |
| 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 "bundled git failed a commit round-trip" >&2; exit 1; } | |
| echo "staged launcher bash spawned; git/jq/curl resolve from the bundle; git commit works" | |
| - name: Check (Tauri crate) | |
| run: cargo check --manifest-path desktop/src-tauri/Cargo.toml --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" | |
| desktop-build-macos: | |
| name: Desktop Build (macOS) | |
| runs-on: macos-latest | |
| timeout-minutes: 45 | |
| needs: [changes] | |
| if: github.event_name == 'push' || needs.changes.outputs.desktop == 'true' || needs.changes.outputs.desktop-rust == 'true' || needs.changes.outputs.rust == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 | |
| - uses: cashapp/activate-hermit@e49f5cb4dd64ff0b0b659d1d8df499595451155a # v1 | |
| - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 | |
| 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-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=$(grep -oE 'mesh-llm\.git\?rev=[0-9a-f]{40}' Cargo.lock | head -1 | grep -oE '[0-9a-f]{40}') | |
| [[ -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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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@27d5ce7f107fe9357f9df03efb73ab90386fccae # 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" |