agtop 2.1.9

Terminal UI for monitoring AI coding agents (Claude Code, Codex, Aider, Cursor, Gemini, Goose, ...) — like top, but for agents.
name: Release

on:
  push:
    tags: ['v*.*.*']
  # Allow auto-tag.yml to invoke the release flow against a freshly-
  # pushed tag — GITHUB_TOKEN-pushed tags don't trigger `on: push:tags`.
  workflow_dispatch:

permissions:
  contents: write

jobs:
  build:
    name: ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - { os: ubuntu-latest,  target: x86_64-unknown-linux-gnu,  suffix: linux-x86_64,  ext: ''     }
          - { os: ubuntu-latest,  target: aarch64-unknown-linux-gnu, suffix: linux-aarch64, ext: '',    cross: true }
          - { os: macos-latest,   target: x86_64-apple-darwin,       suffix: macos-x86_64,  ext: ''     }
          - { os: macos-latest,   target: aarch64-apple-darwin,      suffix: macos-aarch64, ext: ''     }
          - { os: windows-latest, target: x86_64-pc-windows-msvc,    suffix: windows-x86_64, ext: '.exe' }
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2

      - name: Install cross-compile toolchain (linux/aarch64)
        if: matrix.cross
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu
          mkdir -p .cargo
          cat >> .cargo/config.toml <<EOF
          [target.aarch64-unknown-linux-gnu]
          linker = "aarch64-linux-gnu-gcc"
          EOF

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

      - name: Package (unix)
        if: runner.os != 'Windows'
        shell: bash
        run: |
          name="agtop-${{ matrix.suffix }}"
          mkdir -p "dist/$name"
          cp "target/${{ matrix.target }}/release/agtop${{ matrix.ext }}" "dist/$name/"
          cp README.md LICENSE "dist/$name/"
          cd dist
          tar -czf "${name}.tar.gz" "$name"

      - name: Package (windows)
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          $name = "agtop-${{ matrix.suffix }}"
          New-Item -ItemType Directory -Force -Path "dist/$name" | Out-Null
          Copy-Item "target/${{ matrix.target }}/release/agtop${{ matrix.ext }}" "dist/$name/"
          Copy-Item "README.md","LICENSE" "dist/$name/"
          Compress-Archive -Path "dist/$name" -DestinationPath "dist/$name.zip" -Force

      - uses: softprops/action-gh-release@v3
        with:
          files: |
            dist/agtop-${{ matrix.suffix }}.tar.gz
            dist/agtop-${{ matrix.suffix }}.zip
          fail_on_unmatched_files: false
          generate_release_notes: true

  publish-crates:
    name: Publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - name: cargo publish
        env:
          CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
        run: |
          if [ -z "$CRATES_IO_TOKEN" ]; then
            echo "::error::CRATES_IO_TOKEN secret not set"
            exit 1
          fi
          # Skip cleanly if the version is already on crates.io (e.g. the
          # tag was re-pushed for a docs-only re-roll).  Hard-fail on any
          # other publish error so silent breakage can't ship.
          version=$(awk -F'"' '/^version[[:space:]]*=/{print $2; exit}' Cargo.toml)
          if cargo search "agtop" --limit 1 | grep -qE "^agtop = \"$version\""; then
            echo "agtop $version already on crates.io — skipping"
            exit 0
          fi
          cargo publish --token "$CRATES_IO_TOKEN"

  publish-npm:
    name: Publish to npm (@mbrassey/agtop)
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"
      - name: Build npm tarball
        run: bash packages/npm/build.sh
      - name: Publish (skip if version already on npm)
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: |
          if [ -z "$NODE_AUTH_TOKEN" ]; then
            echo "::error::NPM_TOKEN secret not set"
            exit 1
          fi
          cd packages/npm/build
          version=$(node -p "require('./package.json').version")
          name=$(node -p "require('./package.json').name")
          published=$(npm view "$name@$version" version 2>/dev/null || true)
          if [ -n "$published" ]; then
            echo "$name@$version already on npm — skipping"
            exit 0
          fi
          npm publish --access public

  publish-aur:
    name: Publish to AUR
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Resolve version
        id: ver
        run: |
          v=$(awk -F'"' '/^version[[:space:]]*=/{print $2; exit}' Cargo.toml)
          echo "version=$v" >> "$GITHUB_OUTPUT"

      # Pull the GH-archive tarball that the AUR PKGBUILD will reference,
      # then sha256 it.  The archive endpoint becomes available the
      # instant the tag is pushed, so retry a few times in case GitHub's
      # CDN is briefly cold for a fresh tag.
      - name: Compute release tarball sha256
        id: sha
        run: |
          url="https://github.com/mbrassey/agtop/archive/v${{ steps.ver.outputs.version }}.tar.gz"
          for i in 1 2 3 4 5 6 7 8 9 10; do
            if curl -sfL -o /tmp/agtop.tar.gz "$url"; then
              break
            fi
            echo "tarball not ready (attempt $i) — sleeping 10s"
            sleep 10
          done
          sha=$(sha256sum /tmp/agtop.tar.gz | awk '{print $1}')
          echo "sha=$sha" >> "$GITHUB_OUTPUT"
          echo "tarball $sha"

      # The in-repo PKGBUILD is wired for local `packages/pacman/build.sh`
      # builds (source is a tarball staged by the build script).  For AUR
      # we rewrite source= to fetch the GH archive and stamp the real
      # sha256.  Output goes to a temp directory the AUR action picks up.
      - name: Prepare AUR PKGBUILD
        run: |
          mkdir -p aur-pkg
          cp packages/pacman/PKGBUILD aur-pkg/PKGBUILD
          sed -i 's|^source=.*|source=("agtop-${pkgver}.tar.gz::https://github.com/mbrassey/agtop/archive/v${pkgver}.tar.gz")|' aur-pkg/PKGBUILD
          sed -i "s|^sha256sums=.*|sha256sums=('${{ steps.sha.outputs.sha }}')|" aur-pkg/PKGBUILD
          echo "── final PKGBUILD ──"
          cat aur-pkg/PKGBUILD

      - name: Push to AUR
        uses: KSXGitHub/github-actions-deploy-aur@v4.1.1
        with:
          pkgname: agtop
          pkgbuild: aur-pkg/PKGBUILD
          commit_username: mbrassey
          commit_email: matt@brassey.io
          commit_message: "agtop ${{ steps.ver.outputs.version }}"
          ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
          force_push: false
          allow_empty_commits: false