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