name: Release
on:
push:
tags:
- 'v*'
env:
CARGO_TERM_COLOR: always
jobs:
quality-gate:
name: Quality gate
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
- name: Install Linux dependencies
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config libssl-dev
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-release-gate-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-release-gate-
- name: Cache fastembed model
uses: actions/cache@v4
with:
path: ~/.cache/huggingface/
key: fastembed-allminilml6v2
- name: Run tests
run: cargo test --all-features
build:
name: Build ${{ matrix.name }}
needs: [quality-gate]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
os: ubuntu-latest
name: linux-x86_64
cross: false
- target: x86_64-apple-darwin
os: macos-15
name: macos-x86_64
cross: false
- target: aarch64-apple-darwin
os: macos-14
name: macos-aarch64
cross: false
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install cross
if: matrix.cross
run: cargo install cross --git https://github.com/cross-rs/cross
- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest' && !matrix.cross
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config libssl-dev
- name: Build (native)
if: '!matrix.cross'
run: cargo build --release --target ${{ matrix.target }} --features "browser"
- name: Build (cross)
if: matrix.cross
run: cross build --release --target ${{ matrix.target }}
- name: Package (Unix)
run: |
cd target/${{ matrix.target }}/release
tar -czvf ../../../aidaemon-${{ matrix.name }}.tar.gz aidaemon
cd ../../..
shasum -a 256 aidaemon-${{ matrix.name }}.tar.gz > aidaemon-${{ matrix.name }}.tar.gz.sha256
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: aidaemon-${{ matrix.name }}
path: |
aidaemon-${{ matrix.name }}.tar.gz
aidaemon-${{ matrix.name }}.tar.gz.sha256
release:
name: Create Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Create Release
uses: softprops/action-gh-release@v2
with:
draft: false
generate_release_notes: true
files: |
artifacts/*.tar.gz
artifacts/*.sha256
publish-crates:
name: Publish to crates.io
needs: release
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Validate tag matches crate version
id: release_meta
run: |
set -euo pipefail
TAG_VERSION="${GITHUB_REF_NAME#v}"
CRATE_VERSION="$(
awk '
/^\[package\]$/ { in_package=1; next }
/^\[/ && in_package { exit }
in_package && /^version = / {
gsub(/^version = "/, "", $0)
gsub(/"$/, "", $0)
print
exit
}
' Cargo.toml
)"
echo "tag_version=${TAG_VERSION}" >> "$GITHUB_OUTPUT"
echo "crate_version=${CRATE_VERSION}" >> "$GITHUB_OUTPUT"
if [ "$TAG_VERSION" != "$CRATE_VERSION" ]; then
echo "::error::Tag ${GITHUB_REF_NAME} does not match Cargo.toml version ${CRATE_VERSION}."
exit 1
fi
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config libssl-dev
- name: Publish
id: publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
run: |
set -euo pipefail
if [ -z "${CARGO_REGISTRY_TOKEN:-}" ]; then
echo "::error::CARGO_REGISTRY_TOKEN is not configured."
exit 1
fi
LOG_FILE="$(mktemp)"
set +e
cargo publish --token "$CARGO_REGISTRY_TOKEN" 2>&1 | tee "$LOG_FILE"
STATUS=${PIPESTATUS[0]}
set -e
if [ "$STATUS" -eq 0 ]; then
echo "result=published" >> "$GITHUB_OUTPUT"
exit 0
fi
if grep -Eqi 'already (uploaded|exists|been uploaded)|crate version .* is already uploaded' "$LOG_FILE"; then
echo "result=already-published" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "::error::cargo publish failed for version ${{ steps.release_meta.outputs.crate_version }}."
exit "$STATUS"
- name: Verify crates.io version
env:
EXPECTED_VERSION: ${{ steps.release_meta.outputs.crate_version }}
PUBLISH_RESULT: ${{ steps.publish.outputs.result }}
run: |
set -euo pipefail
echo "Waiting for crates.io to report aidaemon ${EXPECTED_VERSION} (${PUBLISH_RESULT})."
for ATTEMPT in $(seq 1 30); do
REPORTED_VERSION="$(
python3 -c 'import json, urllib.request; payload = json.load(urllib.request.urlopen("https://crates.io/api/v1/crates/aidaemon", timeout=20)); print(payload["crate"]["max_version"])'
)"
if [ "$REPORTED_VERSION" = "$EXPECTED_VERSION" ]; then
echo "crates.io now reports aidaemon ${REPORTED_VERSION}."
exit 0
fi
echo "Attempt ${ATTEMPT}/30: crates.io reports ${REPORTED_VERSION}; waiting for ${EXPECTED_VERSION}."
sleep 10
done
echo "::error::crates.io did not report aidaemon ${EXPECTED_VERSION} within 5 minutes."
exit 1
update-homebrew:
name: Update Homebrew Tap
needs: release
runs-on: ubuntu-latest
steps:
- name: Get release info
id: release
run: |
VERSION="${GITHUB_REF_NAME#v}"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT"
- name: Download release assets and compute SHA256
id: sha
run: |
BASE_URL="https://github.com/${{ github.repository }}/releases/download/${{ steps.release.outputs.tag }}"
curl -sL "${BASE_URL}/aidaemon-macos-aarch64.tar.gz" -o macos-aarch64.tar.gz
curl -sL "${BASE_URL}/aidaemon-macos-x86_64.tar.gz" -o macos-x86_64.tar.gz
curl -sL "${BASE_URL}/aidaemon-linux-x86_64.tar.gz" -o linux-x86_64.tar.gz
echo "macos_arm64=$(sha256sum macos-aarch64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "macos_x86_64=$(sha256sum macos-x86_64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
echo "linux_x86_64=$(sha256sum linux-x86_64.tar.gz | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
- name: Checkout tap
uses: actions/checkout@v5
with:
repository: davo20019/homebrew-tap
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
- name: Update formula
run: |
mkdir -p Formula
cat > Formula/aidaemon.rb << 'RUBY'
class Aidaemon < Formula
desc "Personal AI agent daemon with tool use, MCP integration, and persistent memory"
homepage "https://aidaemon.ai/"
license "MIT"
version "VERSION_PLACEHOLDER"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/davo20019/aidaemon/releases/download/vVERSION_PLACEHOLDER/aidaemon-macos-aarch64.tar.gz"
sha256 "SHA_MACOS_ARM64_PLACEHOLDER"
else
url "https://github.com/davo20019/aidaemon/releases/download/vVERSION_PLACEHOLDER/aidaemon-macos-x86_64.tar.gz"
sha256 "SHA_MACOS_X86_64_PLACEHOLDER"
end
end
on_linux do
url "https://github.com/davo20019/aidaemon/releases/download/vVERSION_PLACEHOLDER/aidaemon-linux-x86_64.tar.gz"
sha256 "SHA_LINUX_X86_64_PLACEHOLDER"
end
def install
bin.install "aidaemon"
end
test do
assert_match "aidaemon", shell_output("#{bin}/aidaemon --version", 0)
end
end
RUBY
sed -i "s/VERSION_PLACEHOLDER/${{ steps.release.outputs.version }}/g" Formula/aidaemon.rb
sed -i "s/SHA_MACOS_ARM64_PLACEHOLDER/${{ steps.sha.outputs.macos_arm64 }}/g" Formula/aidaemon.rb
sed -i "s/SHA_MACOS_X86_64_PLACEHOLDER/${{ steps.sha.outputs.macos_x86_64 }}/g" Formula/aidaemon.rb
sed -i "s/SHA_LINUX_X86_64_PLACEHOLDER/${{ steps.sha.outputs.linux_x86_64 }}/g" Formula/aidaemon.rb
- name: Commit and push
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/aidaemon.rb
git commit -m "Update aidaemon to ${{ steps.release.outputs.version }}"
git push