a3s 0.7.3

a3s — A3S coding agent CLI; `a3s code` launches the interactive TUI
name: Release
# One tag → all channels, so they never drift. Requires two repo secrets:
#   CARGO_REGISTRY_TOKEN  — crates.io publish
#   HOMEBREW_TAP_TOKEN    — write access to A3S-Lab/homebrew-tap
# Binary and Homebrew artifacts wait for the crate job so release channels do
# not drift. Missing publish credentials or unpublished dependency crates fail
# the release instead of producing a misleading green job.
on:
  push:
    tags: ["v*"]
permissions:
  contents: write
jobs:
  create-release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Prepare release notes
        run: |
          set -euo pipefail
          version="${GITHUB_REF_NAME#v}"
          awk -v version="$version" '
            $0 ~ "^## \\[" version "\\]" { in_section = 1; print; next }
            in_section && /^## \[/ { exit }
            in_section { print }
          ' CHANGELOG.md > release-notes.md
          if [ ! -s release-notes.md ]; then
            echo "a3s ${GITHUB_REF_NAME}" > release-notes.md
          fi
      - name: Create GitHub release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1 \
            || gh release create "$GITHUB_REF_NAME" --title "$GITHUB_REF_NAME" \
                 --notes-file release-notes.md --verify-tag

  binaries:
    needs: [create-release, crate]
    strategy:
      fail-fast: false
      matrix:
        include:
          - { target: aarch64-apple-darwin, os: macos-latest }
          - { target: x86_64-apple-darwin, os: macos-latest }
          - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest }
          - { target: aarch64-unknown-linux-gnu, os: ubuntu-latest }
          - { target: x86_64-pc-windows-msvc, os: windows-latest }
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - name: Prepare A3S workspace crates
        shell: bash
        run: bash .github/scripts/checkout-a3s-workspace.sh
      - uses: taiki-e/upload-rust-binary-action@v1
        with:
          bin: a3s
          target: ${{ matrix.target }}
          archive: a3s-$tag-$target
          token: ${{ secrets.GITHUB_TOKEN }}

  crate:
    needs: create-release
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Prepare A3S workspace crates
        run: bash .github/scripts/checkout-a3s-workspace.sh
      - uses: dtolnay/rust-toolchain@stable
      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
            echo "::error::CARGO_REGISTRY_TOKEN not set — cannot publish a3s to crates.io"; exit 1
          fi
          crate_exists() {
            curl --retry 5 --retry-all-errors --retry-delay 5 -fsSL \
              -A "a3s-release-check/1.0" \
              "https://crates.io/api/v1/crates/$1/$2" >/dev/null
          }
          if ! crate_exists a3s-code-core 4.3.0; then
            echo "::error::a3s-code-core 4.3.0 is not on crates.io yet — cannot publish a3s"; exit 1
          fi
          if ! crate_exists a3s-memory 0.1.2; then
            echo "::error::a3s-memory 0.1.2 is not on crates.io yet — cannot publish a3s"; exit 1
          fi
          if ! crate_exists a3s-tui 0.1.5; then
            echo "::error::a3s-tui 0.1.5 is not on crates.io yet — cannot publish a3s"; exit 1
          fi
          cargo publish

  homebrew:
    needs: binaries
    runs-on: ubuntu-latest
    steps:
      - name: Update Homebrew tap formula
        env:
          TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
        run: |
          set -euo pipefail
          if [ -z "${TAP_TOKEN:-}" ]; then
            echo "::error::HOMEBREW_TAP_TOKEN not set — cannot update Homebrew tap"; exit 1
          fi
          tag="$GITHUB_REF_NAME"; v="${tag#v}"
          base="https://github.com/A3S-Lab/CLI/releases/download/${tag}"
          sha() { curl -fsSL "$base/a3s-${tag}-$1.tar.gz" | sha256sum | cut -d' ' -f1; }
          mac_arm=$(sha aarch64-apple-darwin)
          mac_x86=$(sha x86_64-apple-darwin)
          lin_arm=$(sha aarch64-unknown-linux-gnu)
          lin_x86=$(sha x86_64-unknown-linux-gnu)
          git clone "https://x-access-token:${TAP_TOKEN}@github.com/A3S-Lab/homebrew-tap.git" tap
          cat > tap/Formula/a3s.rb <<RB
          class A3s < Formula
            desc "A3S coding agent CLI — a3s code launches the interactive TUI"
            homepage "https://github.com/A3S-Lab/CLI"
            version "${v}"
            license "MIT"

            on_macos do
              # RemoteUI popup helper (own repo/formula): A3S-Lab/WebView. Pulled
              # in automatically so the inline OS popup works out of the box.
              depends_on "a3s-lab/tap/a3s-webview"
              on_arm do
                url "${base}/a3s-${tag}-aarch64-apple-darwin.tar.gz"
                sha256 "${mac_arm}"
              end
              on_intel do
                url "${base}/a3s-${tag}-x86_64-apple-darwin.tar.gz"
                sha256 "${mac_x86}"
              end
            end

            on_linux do
              on_arm do
                url "${base}/a3s-${tag}-aarch64-unknown-linux-gnu.tar.gz"
                sha256 "${lin_arm}"
              end
              on_intel do
                url "${base}/a3s-${tag}-x86_64-unknown-linux-gnu.tar.gz"
                sha256 "${lin_x86}"
              end
            end

            def install
              bin.install "a3s"
            end

            test do
              assert_match "a3s", shell_output("#{bin}/a3s --version")
            end
          end
          RB
          cd tap
          git config user.email "ci@a3s.dev"
          git config user.name "A3S CI"
          git add Formula/a3s.rb
          git diff --cached --quiet || git commit -m "a3s ${v} (automated)"
          git push