Skip to content

Release

Release #20

Workflow file for this run

name: Release
on:
release:
types: [published]
workflow_call:
inputs:
tag:
description: 'Tag to release'
required: true
type: string
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g., v0.0.1)'
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:
- target: x86_64-apple-darwin
os: macos-latest
archive: tar.gz
- target: aarch64-apple-darwin
os: macos-latest
archive: tar.gz
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
- target: aarch64-unknown-linux-gnu
os: ubuntu-latest
archive: tar.gz
- 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.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
- name: Build (cross aarch64)
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: cargo build --release --target ${{ matrix.target }} -p icm-cli --features vendored-openssl
env:
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
- name: Build
if: matrix.target != 'aarch64-unknown-linux-gnu'
run: cargo build --release --target ${{ matrix.target }} -p icm-cli
- name: Package (Unix)
if: matrix.archive == 'tar.gz'
run: |
cd target/${{ matrix.target }}/release
tar -czvf ../../../icm-${{ matrix.target }}.${{ matrix.archive }} icm
cd ../../..
- name: Package (Windows)
if: matrix.archive == 'zip'
shell: pwsh
run: |
Compress-Archive -Path "target/${{ matrix.target }}/release/icm.exe" -DestinationPath "icm-${{ matrix.target }}.zip"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: icm-${{ matrix.target }}
path: icm-${{ matrix.target }}.${{ matrix.archive }}
build-deb:
name: Build .deb
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 -p icm-cli
- name: Prepare artifacts
run: |
mkdir -p deb-out
cp target/debian/*.deb deb-out/
# Create version-agnostic name
cp target/debian/*.deb deb-out/icm_amd64.deb
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: icm-deb
path: deb-out/*.deb
build-rpm:
name: Build .rpm
runs-on: ubuntu-latest
container: fedora:latest
steps:
- name: Install dependencies
run: dnf install -y git rust cargo rpm-build openssl-devel pkg-config perl-FindBin perl-File-Compare gcc-c++
- name: Checkout
uses: actions/checkout@v4
- name: Install cargo-generate-rpm
run: cargo install cargo-generate-rpm
- name: Build release
run: cargo build --release -p icm-cli
- name: Generate .rpm
run: cargo generate-rpm -p crates/icm-cli
- name: Prepare artifacts
run: |
mkdir -p rpm-out
cp target/generate-rpm/*.rpm rpm-out/
# Create version-agnostic name
VERSION=$(grep '^version' crates/icm-cli/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
cp target/generate-rpm/*.rpm "rpm-out/icm-${VERSION}-1.x86_64.rpm"
cp target/generate-rpm/*.rpm rpm-out/icm.x86_64.rpm
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: icm-rpm
path: rpm-out/*.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: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "version=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" = "workflow_call" ]; then
echo "version=${{ inputs.tag }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" = "release" ]; then
echo "version=${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
fi
- 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 checksums
run: |
cd release
sha256sum * > checksums.txt
- name: Upload Release Assets
if: github.event_name == 'release' || github.event_name == 'workflow_call'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
files: release/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release
if: github.event_name == 'workflow_dispatch'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
name: icm ${{ steps.version.outputs.version }}
draft: false
prerelease: false
generate_release_notes: true
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
# Strip tag prefixes: "icm-v0.10.1" → "0.10.1", "v0.10.1" → "0.10.1"
VERSION="${TAG#icm-v}"
VERSION="${VERSION#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/icm \
--pattern checksums.txt
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Parse checksums
id: sha
run: |
echo "arm=$(grep aarch64-apple-darwin checksums.txt | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "intel=$(grep x86_64-apple-darwin checksums.txt | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "linux=$(grep x86_64-unknown-linux-gnu checksums.txt | awk '{print $1}')" >> $GITHUB_OUTPUT
echo "linux_arm=$(grep aarch64-unknown-linux-gnu checksums.txt | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Update formula
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
cat > icm.rb << 'FORMULA'
class Icm < Formula
desc "Permanent memory for AI agents — MCP server with hybrid search"
homepage "https://github.com/rtk-ai/icm"
version "VERSION_PLACEHOLDER"
license "MIT"
on_macos do
on_intel do
url "https://github.com/rtk-ai/icm/releases/download/TAG_PLACEHOLDER/icm-x86_64-apple-darwin.tar.gz"
sha256 "SHA_INTEL_PLACEHOLDER"
end
on_arm do
url "https://github.com/rtk-ai/icm/releases/download/TAG_PLACEHOLDER/icm-aarch64-apple-darwin.tar.gz"
sha256 "SHA_ARM_PLACEHOLDER"
end
end
on_linux do
on_intel do
url "https://github.com/rtk-ai/icm/releases/download/TAG_PLACEHOLDER/icm-x86_64-unknown-linux-gnu.tar.gz"
sha256 "SHA_LINUX_PLACEHOLDER"
end
on_arm do
url "https://github.com/rtk-ai/icm/releases/download/TAG_PLACEHOLDER/icm-aarch64-unknown-linux-gnu.tar.gz"
sha256 "SHA_LINUX_ARM_PLACEHOLDER"
end
end
def install
bin.install "icm"
end
test do
assert_match "icm #{version}", shell_output("#{bin}/icm --version")
end
end
FORMULA
sed -i "s/VERSION_PLACEHOLDER/$VERSION/g" icm.rb
sed -i "s/TAG_PLACEHOLDER/$TAG/g" icm.rb
sed -i "s/SHA_INTEL_PLACEHOLDER/${{ steps.sha.outputs.intel }}/g" icm.rb
sed -i "s/SHA_ARM_PLACEHOLDER/${{ steps.sha.outputs.arm }}/g" icm.rb
sed -i "s/SHA_LINUX_PLACEHOLDER/${{ steps.sha.outputs.linux }}/g" icm.rb
sed -i "s/SHA_LINUX_ARM_PLACEHOLDER/${{ steps.sha.outputs.linux_arm }}/g" icm.rb
# Remove leading spaces from heredoc
sed -i 's/^ //' icm.rb
- name: Push to homebrew-tap
run: |
CONTENT=$(base64 -w 0 icm.rb)
SHA=$(gh api repos/rtk-ai/homebrew-tap/contents/Formula/icm.rb --jq '.sha' 2>/dev/null || echo "")
if [ -n "$SHA" ]; then
gh api -X PUT repos/rtk-ai/homebrew-tap/contents/Formula/icm.rb \
-f message="icm ${{ steps.version.outputs.version }}" \
-f content="$CONTENT" \
-f sha="$SHA"
else
gh api -X PUT repos/rtk-ai/homebrew-tap/contents/Formula/icm.rb \
-f message="icm ${{ steps.version.outputs.version }}" \
-f content="$CONTENT"
fi
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}