Add Dialogue feature specification + deferred-specs policy (#281) #391
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: | |
| pull_request: | |
| branches: [main] | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| name: Detect changes | |
| runs-on: ubuntu-latest | |
| outputs: | |
| rust: ${{ steps.filter.outputs.rust }} | |
| swift: ${{ steps.filter.outputs.swift }} | |
| proto: ${{ steps.filter.outputs.proto }} | |
| docker: ${{ steps.filter.outputs.docker }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dorny/paths-filter@v4 | |
| id: filter | |
| with: | |
| filters: | | |
| rust: | |
| - 'runner/**' | |
| - 'proto/**' | |
| swift: | |
| - 'MacApp/**' | |
| - 'proto/**' | |
| proto: | |
| - 'proto/**' | |
| docker: | |
| - 'runner/Dockerfile' | |
| - 'runner/Dockerfile.claude' | |
| - 'docker-compose.yml' | |
| rust: | |
| name: Rust — fmt / clippy / test / build | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' | |
| runs-on: ubuntu-latest | |
| env: | |
| SQLX_OFFLINE: 'true' | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: runner | |
| - name: Install protobuf compiler | |
| run: sudo apt-get update && sudo apt-get install -y protobuf-compiler | |
| - name: Format check | |
| run: cargo fmt --check | |
| working-directory: runner | |
| - name: Clippy (deny warnings, pedantic) | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| working-directory: runner | |
| - name: Check for unsafe code | |
| run: | | |
| # Count unsafe blocks — should be minimal and justified | |
| unsafe_count=$(grep -r "unsafe" runner/src/ --include="*.rs" -c 2>/dev/null || echo "0") | |
| echo "Unsafe blocks found: $unsafe_count" | |
| # Don't fail, just report — some unsafe is necessary for PTY/FFI | |
| - name: Tests | |
| run: cargo test | |
| working-directory: runner | |
| - name: Build release | |
| run: cargo build --release | |
| working-directory: runner | |
| security: | |
| name: Rust — cargo audit | |
| needs: changes | |
| if: needs.changes.outputs.rust == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: runner | |
| - name: Install cargo-audit | |
| run: cargo install cargo-audit --locked | |
| - name: Audit dependencies | |
| run: cargo audit | |
| working-directory: runner | |
| swift: | |
| name: Swift — build / test | |
| needs: changes | |
| if: needs.changes.outputs.swift == 'true' | |
| runs-on: macos-26 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Select Xcode | |
| run: sudo xcode-select -s /Applications/Xcode_26.4.app | |
| - name: Install SwiftLint | |
| run: brew install swiftlint | |
| - name: SwiftLint | |
| run: cd MacApp && swiftlint lint --strict --reporter github-actions-logging | |
| # TODO: Add periphery for dead code detection | |
| # brew install peripheryapp/periphery/periphery | |
| # periphery scan --project MacApp/Relay.xcodeproj --schemes Relay --targets Relay | |
| - name: Build | |
| run: | | |
| xcodebuild build \ | |
| -project MacApp/Relay.xcodeproj \ | |
| -scheme Relay \ | |
| -destination 'platform=macOS,arch=arm64' \ | |
| CODE_SIGN_IDENTITY=- \ | |
| | xcpretty || true | |
| - name: Test | |
| run: | | |
| xcodebuild test \ | |
| -project MacApp/Relay.xcodeproj \ | |
| -scheme Relay \ | |
| -destination 'platform=macOS,arch=arm64' \ | |
| CODE_SIGN_IDENTITY=- \ | |
| | xcpretty || true | |
| continue-on-error: true | |
| proto: | |
| name: Proto — buf lint | |
| needs: changes | |
| if: needs.changes.outputs.proto == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: bufbuild/buf-setup-action@v1 | |
| - name: Lint | |
| run: buf lint proto/ | |
| - name: Breaking change detection | |
| # Compare against the last pushed state on main to catch breaking changes in PRs. | |
| # On push to main this step is skipped (no base to compare against in a clean checkout). | |
| if: github.event_name == 'pull_request' | |
| run: buf breaking proto/ --against "https://github.com/${{ github.repository }}.git#branch=main,subdir=proto" | |
| docker: | |
| name: Docker — build (no push) | |
| needs: changes | |
| if: needs.changes.outputs.docker == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: docker/setup-buildx-action@v4 | |
| - name: Build runner image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: runner/Dockerfile | |
| platforms: linux/amd64 | |
| push: false | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Build Claude image (if exists) | |
| if: hashFiles('runner/Dockerfile.claude') != '' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: runner/Dockerfile.claude | |
| platforms: linux/amd64 | |
| push: false | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Summary job: branch protection watches only this single check. | |
| # GitHub treats skipped jobs as "not required to pass", so any job that ran | |
| # and failed will surface here via the needs context result values. | |
| ci: | |
| name: CI | |
| if: always() | |
| needs: [rust, security, swift, proto, docker] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Require all triggered jobs to pass | |
| env: | |
| RUST_RESULT: ${{ needs.rust.result }} | |
| SECURITY_RESULT: ${{ needs.security.result }} | |
| SWIFT_RESULT: ${{ needs.swift.result }} | |
| PROTO_RESULT: ${{ needs.proto.result }} | |
| DOCKER_RESULT: ${{ needs.docker.result }} | |
| run: | | |
| failed=() | |
| for job in rust security swift proto docker; do | |
| result_var="${job^^}_RESULT" | |
| result="${!result_var}" | |
| if [[ "$result" == "failure" || "$result" == "cancelled" ]]; then | |
| failed+=("$job ($result)") | |
| fi | |
| done | |
| if [[ ${#failed[@]} -gt 0 ]]; then | |
| echo "Failed jobs: ${failed[*]}" | |
| exit 1 | |
| fi | |
| echo "All triggered jobs passed or were skipped." |