name: Release on: workflow_call: inputs: tag: description: 'Tag to release' required: true type: string prerelease: description: 'Mark as pre-release' required: false type: boolean default: false workflow_dispatch: inputs: tag: description: 'Tag to release (e.g., v0.1.0)' required: true prerelease: description: 'Mark as pre-release' type: boolean default: false 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 }} env: RTK_TELEMETRY_URL: ${{ vars.RTK_TELEMETRY_URL }} RTK_TELEMETRY_TOKEN: ${{ secrets.RTK_TELEMETRY_TOKEN }} - name: Package (Unix) if: matrix.os != 'windows-latest' run: | cd target/${{ matrix.target }}/release tar -czvf ../../../rtk-${{ matrix.target }}.${{ matrix.archive }} rtk cd ../../.. - name: Package (Windows) if: matrix.os == 'windows-latest' run: | cd target/${{ matrix.target }}/release 7z a ../../../rtk-${{ matrix.target }}.${{ matrix.archive }} rtk.exe cd ../../.. - name: Upload artifact uses: actions/upload-artifact@v4 with: name: rtk-${{ matrix.target }} path: rtk-${{ 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 run: cargo install cargo-deb - name: Build DEB run: cargo deb env: RTK_TELEMETRY_URL: ${{ vars.RTK_TELEMETRY_URL }} RTK_TELEMETRY_TOKEN: ${{ secrets.RTK_TELEMETRY_TOKEN }} - name: Upload DEB uses: actions/upload-artifact@v4 with: name: rtk-deb path: target/debian/*.deb build-rpm: name: Build RPM package runs-on: ubuntu-latest container: fedora:latest steps: - name: Checkout uses: actions/checkout@v4 - name: Install dependencies run: | dnf install -y rust cargo rpm-build - name: Install cargo-generate-rpm run: cargo install cargo-generate-rpm - name: Build release run: cargo build --release env: RTK_TELEMETRY_URL: ${{ vars.RTK_TELEMETRY_URL }} RTK_TELEMETRY_TOKEN: ${{ secrets.RTK_TELEMETRY_TOKEN }} - name: Generate RPM run: cargo generate-rpm - name: Upload RPM uses: actions/upload-artifact@v4 with: name: rtk-rpm path: target/generate-rpm/*.rpm release: name: Create Release needs: [build, build-deb, build-rpm] runs-on: ubuntu-latest steps: - uses: actions/create-github-app-token@v3 id: app-token with: client-id: ${{ secrets.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write - name: Checkout uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Get version id: version env: INPUT_TAG: ${{ inputs.tag }} RELEASE_TAG: ${{ github.event.release.tag_name }} run: | TAG="$INPUT_TAG" if [ -z "$TAG" ]; then TAG="$RELEASE_TAG" 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" "rtk_amd64.deb" done for f in *.rpm; do [ -f "$f" ] && cp "$f" "rtk.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/* prerelease: ${{ inputs.prerelease }} token: ${{ steps.app-token.outputs.token }} notify-discord: name: Notify Discord needs: [release] if: ${{ !inputs.prerelease }} runs-on: ubuntu-latest steps: - name: Get version id: version env: INPUT_TAG: ${{ inputs.tag }} RELEASE_TAG: ${{ github.event.release.tag_name }} run: | TAG="$INPUT_TAG" if [ -z "$TAG" ]; then TAG="$RELEASE_TAG" fi echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Send Discord notification env: DISCORD_WEBHOOK: ${{ secrets.RTK_DISCORD_RELEASE }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="${{ steps.version.outputs.tag }}" RELEASE_URL="https://github.com/rtk-ai/rtk/releases/tag/${TAG}" # Fetch release notes from GitHub API NOTES=$(gh api "repos/rtk-ai/rtk/releases/tags/${TAG}" --jq '.body' 2>/dev/null | head -c 1800 || echo "") DESC=$(echo "${NOTES:-No release notes}" | jq -Rs .) jq -n \ --arg title "RTK ${TAG} released" \ --arg url "$RELEASE_URL" \ --argjson desc "$DESC" \ '{embeds: [{title: $title, url: $url, description: $desc, color: 5814783, footer: {text: "Rust Token Killer"}}]}' \ | curl -sf -H "Content-Type: application/json" -d @- "$DISCORD_WEBHOOK" homebrew: name: Update Homebrew formula needs: [release] if: ${{ !inputs.prerelease }} runs-on: ubuntu-latest steps: - name: Get version id: version env: INPUT_TAG: ${{ inputs.tag }} RELEASE_TAG: ${{ github.event.release.tag_name }} run: | TAG="$INPUT_TAG" if [ -z "$TAG" ]; then TAG="$RELEASE_TAG" 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 rtk-ai/rtk \ --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 > rtk.rb << 'FORMULA' class Rtk < Formula desc "Rust Token Killer - High-performance CLI proxy to minimize LLM token consumption" homepage "https://www.rtk-ai.app" version "VERSION_PLACEHOLDER" license "Apache 2.0" if OS.mac? && Hardware::CPU.arm? url "https://github.com/rtk-ai/rtk/releases/download/TAG_PLACEHOLDER/rtk-aarch64-apple-darwin.tar.gz" sha256 "SHA_MAC_ARM_PLACEHOLDER" elsif OS.mac? && Hardware::CPU.intel? url "https://github.com/rtk-ai/rtk/releases/download/TAG_PLACEHOLDER/rtk-x86_64-apple-darwin.tar.gz" sha256 "SHA_MAC_INTEL_PLACEHOLDER" elsif OS.linux? && Hardware::CPU.arm? url "https://github.com/rtk-ai/rtk/releases/download/TAG_PLACEHOLDER/rtk-aarch64-unknown-linux-gnu.tar.gz" sha256 "SHA_LINUX_ARM_PLACEHOLDER" elsif OS.linux? && Hardware::CPU.intel? url "https://github.com/rtk-ai/rtk/releases/download/TAG_PLACEHOLDER/rtk-x86_64-unknown-linux-musl.tar.gz" sha256 "SHA_LINUX_INTEL_PLACEHOLDER" end def install bin.install "rtk" end def caveats <<~EOS rtk is installed! Get started: # Initialize for Claude Code rtk init -g # Global hook-first setup (recommended) rtk init # Add to ./CLAUDE.md (this project only) # See all commands rtk --help # Measure your token savings rtk gain Full documentation: https://www.rtk-ai.app EOS end test do system "#{bin}/rtk", "--version" end end FORMULA sed -i "s/VERSION_PLACEHOLDER/${{ steps.version.outputs.version }}/g" rtk.rb sed -i "s/TAG_PLACEHOLDER/${{ steps.version.outputs.tag }}/g" rtk.rb sed -i "s/SHA_MAC_ARM_PLACEHOLDER/${{ steps.sha.outputs.mac_arm }}/g" rtk.rb sed -i "s/SHA_MAC_INTEL_PLACEHOLDER/${{ steps.sha.outputs.mac_intel }}/g" rtk.rb sed -i "s/SHA_LINUX_ARM_PLACEHOLDER/${{ steps.sha.outputs.linux_arm }}/g" rtk.rb sed -i "s/SHA_LINUX_INTEL_PLACEHOLDER/${{ steps.sha.outputs.linux_intel }}/g" rtk.rb # Remove leading spaces from heredoc sed -i 's/^ //' rtk.rb - name: Push to homebrew-tap run: | CONTENT=$(base64 -w 0 rtk.rb) SHA=$(gh api repos/rtk-ai/homebrew-tap/contents/Formula/rtk.rb --jq '.sha' 2>/dev/null || echo "") if [ -n "$SHA" ]; then gh api -X PUT repos/rtk-ai/homebrew-tap/contents/Formula/rtk.rb \ -f message="rtk ${{ steps.version.outputs.version }}" \ -f content="$CONTENT" \ -f sha="$SHA" else gh api -X PUT repos/rtk-ai/homebrew-tap/contents/Formula/rtk.rb \ -f message="rtk ${{ steps.version.outputs.version }}" \ -f content="$CONTENT" fi env: GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}