rippy-cli 0.1.2

A shell command safety hook for AI coding tools (Claude Code, Cursor, Gemini CLI) — Rust rewrite of Dippy
Documentation
name: Release

on:
  release:
    types: [published]
  workflow_dispatch:
    inputs:
      tag:
        description: "Release tag to republish (e.g. rippy-cli-v0.1.0)"
        required: true
        type: string

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always
  RELEASE_TAG: ${{ inputs.tag || github.event.release.tag_name }}

jobs:
  publish:
    name: Publish to crates.io
    if: startsWith(inputs.tag || github.event.release.tag_name, 'rippy-cli-v')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RELEASE_TAG }}

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: Publish rippy-cli
        run: |
          LOCAL=$(cargo metadata --format-version 1 --no-deps | jq -r '.packages[] | select(.name == "rippy-cli") | .version')
          PUBLISHED=$(cargo search rippy-cli --limit 1 --color never | sed -n 's/^rippy-cli = "\([^"]*\)".*/\1/p')
          if [ "$LOCAL" = "$PUBLISHED" ]; then
            echo "::notice::rippy-cli@$LOCAL already published, skipping"
          else
            cargo publish
          fi
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  build-binaries:
    name: Build ${{ matrix.target }}
    if: startsWith(inputs.tag || github.event.release.tag_name, 'rippy-cli-v')
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: aarch64-apple-darwin
            runner: macos-14
          - target: x86_64-apple-darwin
            runner: macos-14
          - target: x86_64-unknown-linux-gnu
            runner: ubuntu-latest
          - target: aarch64-unknown-linux-gnu
            runner: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RELEASE_TAG }}

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

      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}

      - name: Install cross-compilation tools
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu

      - name: Build release binary
        run: cargo build --release --target ${{ matrix.target }}
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc

      - name: Package binary
        shell: bash
        run: |
          TAG="${{ env.RELEASE_TAG }}"
          VERSION="${TAG#rippy-cli-v}"
          ARCHIVE="rippy-v${VERSION}-${{ matrix.target }}.tar.gz"
          cp "target/${{ matrix.target }}/release/rippy" rippy
          tar -czf "${ARCHIVE}" rippy
          echo "ARCHIVE=${ARCHIVE}" >> "$GITHUB_ENV"
          openssl dgst -sha256 "${ARCHIVE}" | awk '{print $2}' > "${ARCHIVE}.sha256"

      - name: Upload to release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release upload "${{ env.RELEASE_TAG }}" \
            "${{ env.ARCHIVE }}" \
            "${{ env.ARCHIVE }}.sha256" \
            --clobber

  update-homebrew:
    name: Update Homebrew tap
    needs: build-binaries
    if: startsWith(inputs.tag || github.event.release.tag_name, 'rippy-cli-v')
    runs-on: ubuntu-latest
    steps:
      - name: Compute version
        id: version
        run: |
          TAG="${{ env.RELEASE_TAG }}"
          echo "version=${TAG#rippy-cli-v}" >> "$GITHUB_OUTPUT"

      - name: Download SHA256 checksums
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          VERSION="${{ steps.version.outputs.version }}"
          for TARGET in aarch64-apple-darwin x86_64-apple-darwin x86_64-unknown-linux-gnu; do
            gh release download "${{ env.RELEASE_TAG }}" \
              --repo "${{ github.repository }}" \
              --pattern "rippy-v${VERSION}-${TARGET}.tar.gz.sha256" \
              --output "${TARGET}.sha256"
          done
          echo "SHA256_AARCH64=$(cat aarch64-apple-darwin.sha256)" >> "$GITHUB_ENV"
          echo "SHA256_X86_64_DARWIN=$(cat x86_64-apple-darwin.sha256)" >> "$GITHUB_ENV"
          echo "SHA256_LINUX=$(cat x86_64-unknown-linux-gnu.sha256)" >> "$GITHUB_ENV"

      - name: Generate GitHub App token
        id: app-token
        uses: actions/create-github-app-token@v3
        with:
          app-id: ${{ secrets.REPOSITORY_BUTLER_APP_ID }}
          private-key: ${{ secrets.REPOSITORY_BUTLER_PEM }}
          repositories: homebrew-tools

      - name: Checkout homebrew-tools tap
        uses: actions/checkout@v6
        with:
          repository: mpecan/homebrew-tools
          token: ${{ steps.app-token.outputs.token }}
          path: homebrew-tools

      - name: Update formula
        env:
          VERSION: ${{ steps.version.outputs.version }}
        run: |
          python3 - <<'PYEOF'
          import os, textwrap
          v = os.environ['VERSION']
          sha_arm = os.environ['SHA256_AARCH64']
          sha_intel = os.environ['SHA256_X86_64_DARWIN']
          sha_linux = os.environ['SHA256_LINUX']
          base = "https://github.com/mpecan/rippy/releases/download"
          formula = textwrap.dedent(f"""\
            class Rippy < Formula
              desc "Shell command safety hook for AI coding tools (Claude Code, Cursor, Gemini CLI)"
              homepage "https://github.com/mpecan/rippy"
              version "{v}"
              license "MIT"

              on_macos do
                on_arm do
                  url "{base}/rippy-cli-v{v}/rippy-v{v}-aarch64-apple-darwin.tar.gz"
                  sha256 "{sha_arm}"
                end
                on_intel do
                  url "{base}/rippy-cli-v{v}/rippy-v{v}-x86_64-apple-darwin.tar.gz"
                  sha256 "{sha_intel}"
                end
              end

              on_linux do
                on_intel do
                  url "{base}/rippy-cli-v{v}/rippy-v{v}-x86_64-unknown-linux-gnu.tar.gz"
                  sha256 "{sha_linux}"
                end
              end

              def install
                bin.install "rippy"
              end

              test do
                assert_match "rippy", shell_output("#{{bin}}/rippy --version")
              end
            end
          """)
          os.makedirs("homebrew-tools/Formula", exist_ok=True)
          with open("homebrew-tools/Formula/rippy.rb", "w") as f:
              f.write(formula)
          PYEOF

      - name: Commit and push
        working-directory: homebrew-tools
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add Formula/rippy.rb
          git diff --cached --quiet || git commit -m "rippy ${{ steps.version.outputs.version }}"
          git push