clauth 0.7.3

Manage multiple Claude Code accounts, monitor 5h/7d usage with configurable auto-switch and delegation with an MCP plugin. CLI + TUI.
name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write

# Force HTTP/1.1 for registry downloads: curl's HTTP/2 multiplexing against the
# crates.io CDN intermittently dies with "[16] Error in the HTTP2 framing layer"
# on the hosted runners. Extra retries cover the remaining transient blips.
env:
  CARGO_NET_RETRY: "10"
  CARGO_HTTP_MULTIPLEXING: "false"

jobs:
  create-release:
    name: Create release
    runs-on: ubuntu-latest
    steps:
      - name: Create release notes
        uses: softprops/action-gh-release@v3
        with:
          generate_release_notes: false

  build-linux-windows:
    name: Build ${{ matrix.asset_name }}
    needs: create-release
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            asset_name: clauth-linux-x86_64
            ext: ""
          - target: x86_64-pc-windows-gnu
            asset_name: clauth-windows-x86_64
            ext: .exe

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Install mingw (Windows target)
        if: matrix.ext == '.exe'
        run: sudo apt-get install -y gcc-mingw-w64-x86-64

      - name: Cache cargo
        uses: actions/cache@v5
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-${{ matrix.target }}-cargo-

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Prepare asset (Unix)
        if: matrix.ext == ''
        run: cp target/${{ matrix.target }}/release/clauth ${{ matrix.asset_name }}

      - name: Prepare asset (Windows)
        if: matrix.ext == '.exe'
        run: cp target/${{ matrix.target }}/release/clauth.exe ${{ matrix.asset_name }}.exe

      - name: Upload to release
        uses: softprops/action-gh-release@v3
        with:
          files: ${{ matrix.asset_name }}${{ matrix.ext }}

      - name: Upload artifact for checksum job
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.asset_name }}${{ matrix.ext }}
          path: ${{ matrix.asset_name }}${{ matrix.ext }}
          if-no-files-found: error

  build-macos:
    name: Build ${{ matrix.asset_name }}
    needs: create-release
    runs-on: macos-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: aarch64-apple-darwin
            asset_name: clauth-macos-aarch64
          - target: x86_64-apple-darwin
            asset_name: clauth-macos-x86_64

    steps:
      - uses: actions/checkout@v6

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Cache cargo
        uses: actions/cache@v5
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-${{ matrix.target }}-cargo-

      - name: Build
        run: cargo build --release --target ${{ matrix.target }}

      - name: Prepare asset
        run: cp target/${{ matrix.target }}/release/clauth ${{ matrix.asset_name }}

      - name: Upload to release
        uses: softprops/action-gh-release@v3
        with:
          files: ${{ matrix.asset_name }}

      - name: Upload artifact for checksum job
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.asset_name }}
          path: ${{ matrix.asset_name }}
          if-no-files-found: error

  checksums:
    name: Generate checksums
    needs: [build-linux-windows, build-macos]
    # Run even when some build legs failed so that binaries already published
    # always have a matching sha256sums.txt in the release.
    if: always()
    runs-on: ubuntu-latest
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          merge-multiple: true

      - name: Generate sha256sums.txt
        # Glob whatever was actually downloaded so a partial build still
        # produces a sums file that matches exactly what shipped.
        run: |
          cd artifacts
          sha256sum clauth-* > ../sha256sums.txt

      - name: Upload sha256sums.txt to release
        uses: softprops/action-gh-release@v3
        with:
          files: sha256sums.txt

      - name: Sign sha256sums.txt with minisign
        env:
          MINISIGN_SECRET_KEY: ${{ secrets.MINISIGN_SECRET_KEY }}
        # Signing the sums file transitively authenticates every asset hash it
        # lists. Gated on the secret so forks / pre-key releases still ship a
        # valid sha256-only release; update.rs verifies this signature when (and
        # only when) MINISIGN_PUBLIC_KEY is pinned. A passwordless key signs
        # non-interactively in the default (prehashed) format.
        run: |
          if [ -z "${MINISIGN_SECRET_KEY:-}" ]; then
            echo "MINISIGN_SECRET_KEY not set — shipping sha256-only (no signature)."
            exit 0
          fi
          sudo apt-get update
          sudo apt-get install -y minisign
          # Remove the key file on ANY exit (incl. a signing failure under `set -e`).
          trap 'rm -f minisign.key' EXIT
          printf '%s\n' "${MINISIGN_SECRET_KEY}" > minisign.key
          minisign -S -s minisign.key -m sha256sums.txt -t "clauth ${GITHUB_REF_NAME} checksums"
          echo "Signed → sha256sums.txt.minisig"

      - name: Upload signature to release
        if: hashFiles('sha256sums.txt.minisig') != ''
        uses: softprops/action-gh-release@v3
        with:
          files: sha256sums.txt.minisig