earl 0.5.2

AI-safe CLI for AI agents
name: CI

on:
  pull_request:
  push:
    branches:
      - main

concurrency:
  group: ci-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

env:
  CARGO_TERM_COLOR: always

jobs:
  fmt:
    name: Rustfmt
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - name: Check formatting
        run: cargo fmt --all -- --check

  clippy:
    name: Clippy
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install system dependencies
        run: sudo apt-get install -y libdbus-1-dev
      - name: Install toolchain
        uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - name: Setup pnpm
        uses: pnpm/action-setup@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"
          cache-dependency-path: pnpm-lock.yaml
      - name: Cache cargo artifacts
        uses: Swatinem/rust-cache@v2
      - name: Lint
        run: cargo clippy --all-targets --all-features -- -D warnings

  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install system dependencies
        run: sudo apt-get install -y libdbus-1-dev bubblewrap
      - name: Allow bwrap unprivileged user namespaces
        run: |
          echo 'abi <abi/4.0>,' | sudo tee /etc/apparmor.d/bwrap
          echo 'include <tunables/global>' | sudo tee -a /etc/apparmor.d/bwrap
          echo 'profile bwrap /usr/bin/bwrap flags=(unconfined) {' | sudo tee -a /etc/apparmor.d/bwrap
          echo '  userns,' | sudo tee -a /etc/apparmor.d/bwrap
          echo '}' | sudo tee -a /etc/apparmor.d/bwrap
          sudo apparmor_parser -r /etc/apparmor.d/bwrap
      - name: Install toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Setup pnpm
        uses: pnpm/action-setup@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"
          cache-dependency-path: pnpm-lock.yaml
      - name: Cache cargo artifacts
        uses: Swatinem/rust-cache@v2
      - name: Run tests
        run: cargo test --all-targets --all-features

  publish-dry-run:
    name: Publish Dry Run
    if: ${{ !startsWith(github.head_ref, 'release-please--') }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Install system dependencies
        run: sudo apt-get install -y libdbus-1-dev
      - name: Install toolchain
        uses: dtolnay/rust-toolchain@stable
      - name: Setup pnpm
        uses: pnpm/action-setup@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"
          cache-dependency-path: pnpm-lock.yaml
      - name: Cache cargo artifacts
        uses: Swatinem/rust-cache@v2
      - name: Validate crates.io package
        shell: bash
        run: |
          set -euo pipefail

          # Publish in dependency order matching release.yml.
          # - Skip crates that already exist on crates.io (idempotent).
          # - Skip crates whose workspace dependencies haven't been
          #   published yet (valid for newly-added protocol crates before
          #   their first release).
          CRATES=(
            earl-core
            earl-protocol-http
            earl-protocol-grpc
            earl-protocol-bash
            earl-protocol-sql
            earl-protocol-browser
            earl
          )

          for crate in "${CRATES[@]}"; do
            echo "Dry-run for $crate..."
            output=$(cargo publish --dry-run --locked --no-verify -p "$crate" 2>&1) && echo "$output" || {
              if echo "$output" | grep -q "already exists on crates.io index"; then
                echo "$crate already on crates.io, skipping."
              elif echo "$output" | grep -q "no matching package named"; then
                echo "$crate has unpublished workspace dependencies, skipping."
              else
                echo "$output" >&2
                exit 1
              fi
            }
          done