tala-cli 0.32.1

Agent-to-agent messaging for AI coding tools
name: Release

on:
  push:
    branches: [main]
  pull_request:
    types: [closed]
    branches: [main]
  workflow_dispatch:

concurrency:
  group: version-release
  cancel-in-progress: false

jobs:
  release:
    if: github.event_name == 'push' || github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    outputs:
      version: ${{ steps.bump.outputs.version }}
      tag: ${{ steps.bump.outputs.tag }}
      did_bump: ${{ steps.bump.outputs.did_bump }}
    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0
        fetch-tags: true

    - name: Setup Rust
      uses: actions-rust-lang/setup-rust-toolchain@v1
      with:
        toolchain: stable
        cache: true

    - name: Install grubble
      run: cargo install grubble

    - name: Clean stale tags
      run: |
        git fetch origin --tags
        git tag -l 'v*' | while read -r tag; do
          if ! git merge-base --is-ancestor "$tag" HEAD 2>/dev/null; then
            git tag -d "$tag"
          fi
        done

    - name: Bump version and create tag
      id: bump
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        BEFORE=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
        grubble \
          --git-user-name "github-actions[bot]" \
          --git-user-email "41898282+github-actions[bot]@users.noreply.github.com" \
          --preset rust \
          --changelog \
          --push \
          --tag \
          --update-major-tag

        AFTER=$(grep '^version' Cargo.toml | head -1 | cut -d'"' -f2)
        if [ "$AFTER" != "$BEFORE" ]; then
          echo "did_bump=true" >> $GITHUB_OUTPUT
          echo "version=$AFTER" >> $GITHUB_OUTPUT
          echo "tag=v$AFTER" >> $GITHUB_OUTPUT
        else
          echo "did_bump=false" >> $GITHUB_OUTPUT
          echo "version=$AFTER" >> $GITHUB_OUTPUT
        fi

  build:
    name: Build binaries
    needs: release
    if: needs.release.outputs.did_bump == 'true'
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            name: tala-linux-x86_64
            cross: true
          - target: aarch64-unknown-linux-musl
            os: ubuntu-latest
            name: tala-linux-aarch64
            cross: true
          - target: aarch64-apple-darwin
            os: macos-latest
            name: tala-macos-aarch64
            cross: false
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            name: tala-windows-x86_64.exe
            cross: false
    runs-on: ${{ matrix.os }}
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.release.outputs.tag }}

      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          target: ${{ matrix.target }}
          cache: true

      - name: Set up ARM64 emulation
        if: matrix.target == 'aarch64-unknown-linux-musl'
        uses: docker/setup-qemu-action@v3
        with:
          platforms: arm64

      - name: Install cross
        if: matrix.cross
        run: cargo install cross --git https://github.com/cross-rs/cross

      - name: Build (native)
        if: ${{ !matrix.cross }}
        run: cargo build --release --locked --target ${{ matrix.target }}

      - name: Build (cross)
        if: matrix.cross
        run: cross build --release --locked --target ${{ matrix.target }}

      - name: Verify binary (Unix)
        if: matrix.os != 'windows-latest'
        run: |
          expected="tala ${{ needs.release.outputs.version }}"
          if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
            actual="$(docker run --rm --platform linux/arm64 --volume "$GITHUB_WORKSPACE:/work:ro" alpine:3.22 /work/target/${{ matrix.target }}/release/tala --version)"
          else
            actual="$(target/${{ matrix.target }}/release/tala --version)"
          fi
          test "$actual" = "$expected"

      - name: Verify binary (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          $expected = "tala ${{ needs.release.outputs.version }}"
          $actual = (& "target/${{ matrix.target }}/release/tala.exe" --version).Trim()
          if ($actual -ne $expected) { throw "Expected '$expected', got '$actual'" }

      - name: Package (Unix)
        if: matrix.os != 'windows-latest'
        run: |
          cd target/${{ matrix.target }}/release
          tar czf ../../../${{ matrix.name }}.tar.gz tala
          cd -
          shasum -a 256 ${{ matrix.name }}.tar.gz > ${{ matrix.name }}.tar.gz.sha256

      - name: Package (Windows)
        if: matrix.os == 'windows-latest'
        run: |
          cd target/${{ matrix.target }}/release
          7z a ../../../${{ matrix.name }}.zip tala.exe
          cd -

      - name: Verify archive (Unix)
        if: matrix.os != 'windows-latest'
        run: |
          archive="${{ matrix.name }}.tar.gz"
          grep -Fxq tala <(tar -tzf "$archive")
          shasum -a 256 -c "${{ matrix.name }}.tar.gz.sha256"
          extracted="$(mktemp -d)"
          tar -xzf "$archive" -C "$extracted"
          test -x "$extracted/tala"
          if [ "${{ matrix.target }}" = "aarch64-unknown-linux-musl" ]; then
            actual="$(docker run --rm --platform linux/arm64 --volume "$extracted:/work:ro" alpine:3.22 /work/tala --version)"
          else
            actual="$("$extracted/tala" --version)"
          fi
          test "$actual" = "tala ${{ needs.release.outputs.version }}"

      - name: Verify archive (Windows)
        if: matrix.os == 'windows-latest'
        shell: pwsh
        run: |
          $archive = "${{ matrix.name }}.zip"
          $listing = 7z l $archive | Out-String
          if ($listing -notmatch "tala\.exe") { throw "Archive does not contain tala.exe" }
          $extracted = Join-Path $env:RUNNER_TEMP "tala-archive"
          7z x $archive "-o$extracted" -y | Out-Null
          $actual = (& (Join-Path $extracted "tala.exe") --version).Trim()
          $expected = "tala ${{ needs.release.outputs.version }}"
          if ($actual -ne $expected) { throw "Expected '$expected', got '$actual'" }

      - name: Upload Release Asset (Unix)
        if: matrix.os != 'windows-latest'
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.release.outputs.tag }}
          files: |
            ${{ matrix.name }}.tar.gz
            ${{ matrix.name }}.tar.gz.sha256
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload Release Asset (Windows)
        if: matrix.os == 'windows-latest'
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.release.outputs.tag }}
          files: |
            ${{ matrix.name }}.zip
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish:
    name: Publish to crates.io
    needs: [release, build]
    if: needs.release.outputs.did_bump == 'true'
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.release.outputs.tag }}

      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          cache: true

      - name: Authenticate with crates.io
        uses: rust-lang/crates-io-auth-action@v1
        id: auth

      - name: Publish
        run: cargo publish --locked
        env:
          CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}

  verify-install:
    name: Verify cargo-binstall installation
    needs: [release, build, publish]
    if: needs.release.outputs.did_bump == 'true'
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - name: Setup Rust
        uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          toolchain: stable
          cache: true

      - name: Install cargo-binstall
        run: cargo install cargo-binstall --locked

      - name: Install released Tala binary
        run: cargo binstall --no-confirm --force --disable-telemetry --strategies crate-meta-data --target x86_64-unknown-linux-musl tala-cli

      - name: Verify installed version
        run: test "$(tala --version)" = "tala ${{ needs.release.outputs.version }}"