Skip to content

Release

Release #5

Workflow file for this run

name: Release
on:
workflow_call:
inputs:
tag:
description: "Tag to release"
required: true
type: string
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g., v0.25.0)"
required: true
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
build:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
# macOS
- target: x86_64-apple-darwin
os: macos-latest
archive: tar.gz
- target: aarch64-apple-darwin
os: macos-latest
archive: tar.gz
# Linux
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
archive: tar.gz
musl: true
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
cross: true
# Windows
- target: x86_64-pc-windows-msvc
os: windows-latest
archive: zip
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross-compilation tools
if: matrix.cross
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
- name: Install musl tools
if: matrix.musl
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Build
run: cargo build --release --target ${{ matrix.target }}
- name: Package (Unix)
if: matrix.os != 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
tar -czvf ../../../clov-${{ matrix.target }}.${{ matrix.archive }} clov
cd ../../..
- name: Package (Windows)
if: matrix.os == 'windows-latest'
run: |
cd target/${{ matrix.target }}/release
7z a ../../../clov-${{ matrix.target }}.${{ matrix.archive }} clov.exe
cd ../../..
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: clov-${{ matrix.target }}
path: clov-${{ matrix.target }}.${{ matrix.archive }}
build-deb:
name: Build DEB package
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-deb
uses: taiki-e/install-action@cargo-deb
- name: Build DEB
run: cargo deb
- name: Upload DEB
uses: actions/upload-artifact@v4
with:
name: clov-deb
path: target/debian/*.deb
build-rpm:
name: Build RPM package
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install rpm-build
run: |
sudo apt-get update
sudo apt-get install -y rpm
- name: Install cargo-generate-rpm
uses: taiki-e/install-action@cargo-generate-rpm
- name: Build release
run: cargo build --release
- name: Generate RPM
run: cargo generate-rpm
- name: Upload RPM
uses: actions/upload-artifact@v4
with:
name: clov-rpm
path: target/generate-rpm/*.rpm
release:
name: Create Release
needs: [build, build-deb, build-rpm]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Get version
id: version
run: |
TAG="${{ inputs.tag }}"
if [ -z "$TAG" ]; then
TAG="${{ github.event.release.tag_name }}"
fi
echo "version=$TAG" >> $GITHUB_OUTPUT
- name: Flatten artifacts
run: |
mkdir -p release
find artifacts -type f \( -name "*.tar.gz" -o -name "*.zip" -o -name "*.deb" -o -name "*.rpm" \) -exec cp {} release/ \;
- name: Create version-agnostic package names
run: |
cd release
for f in *.deb; do
[ -f "$f" ] && cp "$f" "clov_amd64.deb"
done
for f in *.rpm; do
[ -f "$f" ] && cp "$f" "clov.x86_64.rpm"
done
- name: Create checksums
run: |
cd release
sha256sum * > checksums.txt
- name: Upload Release Assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
files: release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
homebrew:
name: Update Homebrew formula
needs: [release]
runs-on: ubuntu-latest
steps:
- name: Get version
id: version
run: |
TAG="${{ inputs.tag }}"
if [ -z "$TAG" ]; then
TAG="${{ github.event.release.tag_name }}"
fi
VERSION="${TAG#v}"
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Download checksums
run: |
gh release download "${{ steps.version.outputs.tag }}" \
--repo alexandephilia/clov-ai \
--pattern checksums.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Parse checksums
id: sha
run: |
echo "mac_arm=$(grep aarch64-apple-darwin.tar.gz checksums.txt | head -1 | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "mac_intel=$(grep x86_64-apple-darwin.tar.gz checksums.txt | head -1 | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "linux_arm=$(grep aarch64-unknown-linux-gnu.tar.gz checksums.txt | head -1 | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "linux_intel=$(grep x86_64-unknown-linux-musl.tar.gz checksums.txt | head -1 | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Generate formula
run: |
cat > clov.rb << 'FORMULA'
class Clov < Formula
desc "Clov Token Omitter - High-performance CLI proxy to minimize LLM token consumption"
homepage "https://github.com/alexandephilia/clov-ai"
version "VERSION_PLACEHOLDER"
license "MIT"
if OS.mac? && Hardware::CPU.arm?
url "https://github.com/alexandephilia/clov-ai/releases/download/TAG_PLACEHOLDER/clov-aarch64-apple-darwin.tar.gz"
sha256 "SHA_MAC_ARM_PLACEHOLDER"
elsif OS.mac? && Hardware::CPU.intel?
url "https://github.com/alexandephilia/clov-ai/releases/download/TAG_PLACEHOLDER/clov-x86_64-apple-darwin.tar.gz"
sha256 "SHA_MAC_INTEL_PLACEHOLDER"
elsif OS.linux? && Hardware::CPU.arm?
url "https://github.com/alexandephilia/clov-ai/releases/download/TAG_PLACEHOLDER/clov-aarch64-unknown-linux-gnu.tar.gz"
sha256 "SHA_LINUX_ARM_PLACEHOLDER"
elsif OS.linux? && Hardware::CPU.intel?
url "https://github.com/alexandephilia/clov-ai/releases/download/TAG_PLACEHOLDER/clov-x86_64-unknown-linux-musl.tar.gz"
sha256 "SHA_LINUX_INTEL_PLACEHOLDER"
end
def install
bin.install "clov"
end
def caveats
<<~EOS
clov is installed! Get started:
# Initialize for Claude Code
clov init -g # Global hook-first setup (recommended)
clov init # Add to ./CLAUDE.md (this project only)
# See all commands
clov --help
# Measure your token savings
clov gain
Full documentation: https://github.com/alexandephilia/clov-ai
EOS
end
test do
system "#{bin}/clov", "--version"
end
end
FORMULA
sed -i "s/VERSION_PLACEHOLDER/${{ steps.version.outputs.version }}/g" clov.rb
sed -i "s/TAG_PLACEHOLDER/${{ steps.version.outputs.tag }}/g" clov.rb
sed -i "s/SHA_MAC_ARM_PLACEHOLDER/${{ steps.sha.outputs.mac_arm }}/g" clov.rb
sed -i "s/SHA_MAC_INTEL_PLACEHOLDER/${{ steps.sha.outputs.mac_intel }}/g" clov.rb
sed -i "s/SHA_LINUX_ARM_PLACEHOLDER/${{ steps.sha.outputs.linux_arm }}/g" clov.rb
sed -i "s/SHA_LINUX_INTEL_PLACEHOLDER/${{ steps.sha.outputs.linux_intel }}/g" clov.rb
# Remove leading spaces from heredoc
sed -i 's/^ //' clov.rb
- name: Push to homebrew-clov
run: |
CONTENT=$(base64 -w 0 clov.rb)
SHA=$(gh api repos/alexandephilia/homebrew-clov/contents/Formula/clov.rb --jq '.sha' 2>/dev/null || echo "")
if [ -n "$SHA" ]; then
gh api -X PUT repos/alexandephilia/homebrew-clov/contents/Formula/clov.rb \
-f message="clov ${{ steps.version.outputs.version }}" \
-f content="$CONTENT" \
-f sha="$SHA"
else
gh api -X PUT repos/alexandephilia/homebrew-clov/contents/Formula/clov.rb \
-f message="clov ${{ steps.version.outputs.version }}" \
-f content="$CONTENT"
fi
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}