clauth 0.9.0

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  # gh release upload attaches assets to the release

# 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"

# The release itself is created by release.sh right after the tag push, with the
# full changelog already in the body so the notes ride along in GitHub's release
# email. This workflow never creates the release or touches its notes -- it only
# builds the per-OS binaries and uploads them (plus sums + signature) to the
# release the script already published. One upload point (the publish job), so no
# concurrent softprops find-or-create can duplicate the release and orphan the notes.

jobs:
  build:
    name: Build ${{ matrix.asset_name }}
    runs-on: ${{ matrix.os }}
    defaults:
      run:
        shell: bash  # windows-latest ships Git Bash; keeps cp + forward-slash paths uniform across OSes
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            asset_name: clauth-linux-x86_64
            ext: ""
          - target: aarch64-apple-darwin
            os: macos-latest
            asset_name: clauth-macos-aarch64
            ext: ""
          - target: x86_64-apple-darwin
            os: macos-latest
            asset_name: clauth-macos-x86_64
            ext: ""
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            asset_name: clauth-windows-x86_64
            ext: .exe
    steps:
      - uses: actions/checkout@v7
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
      - name: Build
        run: cargo build --release --target ${{ matrix.target }}
      - name: Prepare asset
        run: cp "target/${{ matrix.target }}/release/clauth${{ matrix.ext }}" "${{ matrix.asset_name }}${{ matrix.ext }}"
      - name: Upload artifact for the publish job
        uses: actions/upload-artifact@v7
        with:
          name: ${{ matrix.asset_name }}${{ matrix.ext }}
          path: ${{ matrix.asset_name }}${{ matrix.ext }}
          if-no-files-found: error

  publish:
    name: Publish assets
    needs: [build]
    # Run even when a build leg fails so the binaries that DID ship still get
    # attached with a matching sums file covering exactly what was published.
    if: always()
    runs-on: ubuntu-latest
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: artifacts
          merge-multiple: true
      - name: Generate + sign sha256sums.txt
        env:
          MINISIGN_SECRET_KEY: ${{ secrets.MINISIGN_SECRET_KEY }}
        # Glob whatever was actually built so a partial release still produces a sums
        # file matching exactly what shipped. Signing the sums file transitively
        # authenticates every asset hash it lists; gated on the secret so a fork /
        # pre-key release still ships a valid sha256-only release. update.rs verifies
        # this signature when (and only when) MINISIGN_PUBLIC_KEY is pinned.
        run: |
          cd artifacts
          sha256sum clauth-* > sha256sums.txt
          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 assets to the release
        env:
          GH_TOKEN: ${{ github.token }}
        # release.sh normally creates the release (with the full notes) moments after
        # the tag push; fall back to a bare release so a hand-pushed tag still gets
        # its binaries. Never generate notes here -- the script owns them. --clobber
        # keeps re-runs idempotent.
        run: |
          cd artifacts
          gh release view "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1 \
            || gh release create "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" --title "${GITHUB_REF_NAME}" --notes "" --verify-tag
          files=(clauth-* sha256sums.txt)
          [ -f sha256sums.txt.minisig ] && files+=(sha256sums.txt.minisig)
          gh release upload "${GITHUB_REF_NAME}" "${files[@]}" --clobber --repo "${GITHUB_REPOSITORY}"