Skip to content

test(c): add embedding and multi-connection tests, refactor memory ch… #473

test(c): add embedding and multi-connection tests, refactor memory ch…

test(c): add embedding and multi-connection tests, refactor memory ch… #473

Workflow file for this run

name: Build, Test and Release
on:
push:
workflow_dispatch:
permissions:
contents: write
id-token: write
env:
GGUF_MODEL_DIR: tests/models/unsloth/gemma-3-270m-it-GGUF
GGUF_MODEL_NAME: gemma-3-270m-it-UD-IQ2_M.gguf
GGUF_MODEL_URL: https://huggingface.co/unsloth/gemma-3-270m-it-GGUF/resolve/main/gemma-3-270m-it-UD-IQ2_M.gguf
jobs:
download-model:
outputs:
cache-key: gguf-${{ steps.meta.outputs.hash }}
model-path: ${{ env.GGUF_MODEL_DIR }}/${{ env.GGUF_MODEL_NAME }}
name: Download GGUF model
runs-on: ubuntu-22.04
steps:
- name: Compute model URL hash
id: meta
run: |
if command -v sha256sum >/dev/null 2>&1; then
hash=$(echo -n "${{ env.GGUF_MODEL_URL }}" | sha256sum | cut -d' ' -f1)
else
hash=$(echo -n "${{ env.GGUF_MODEL_URL }}" | shasum -a 256 | cut -d' ' -f1)
fi
echo "hash=$hash" >> "$GITHUB_OUTPUT"
- name: Prepare model directory
run: mkdir -p "${{ env.GGUF_MODEL_DIR }}"
- name: Restore GGUF cache
id: cache
uses: actions/cache@v4
with:
path: ${{ env.GGUF_MODEL_DIR }}/${{ env.GGUF_MODEL_NAME }}
key: gguf-${{ steps.meta.outputs.hash }}
- name: Download GGUF model
if: steps.cache.outputs.cache-hit != 'true'
run: |
curl -L --fail --retry 3 "${{ env.GGUF_MODEL_URL }}" -o "${{ env.GGUF_MODEL_DIR }}/${{ env.GGUF_MODEL_NAME }}"
- name: Verify GGUF model
run: test -f "${{ env.GGUF_MODEL_DIR }}/${{ env.GGUF_MODEL_NAME }}"
build:
needs: download-model
runs-on: ${{ matrix.os }}
container: ${{ matrix.container && matrix.container || '' }}
name: ${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }} build${{ matrix.arch != 'arm64-v8a' && matrix.name != 'ios-sim' && matrix.name != 'ios' && matrix.name != 'apple-xcframework' && matrix.name != 'android-aar' && ( matrix.name != 'macos' || matrix.arch != 'x86_64' ) && ' + test' || ''}}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- os: macos-15
name: macos
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: macos-15
arch: x86_64
name: macos
make: ARCH=x86_64 LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: macos-15
arch: arm64
name: macos
make: ARCH=arm64 LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: ubuntu-22.04
arch: x86_64
name: linux-cpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_AVX2=ON"
- os: ubuntu-22.04
arch: x86_64
name: linux-gpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_VULKAN=ON -DGGML_OPENCL=ON"
- os: ubuntu-22.04-arm
arch: arm64
name: linux-cpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_CPU_ARM_ARCH=armv8.2-a"
- os: ubuntu-22.04-arm
arch: arm64
name: linux-gpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_VULKAN=ON -DGGML_OPENCL=ON"
- os: ubuntu-22.04
arch: x86_64
name: linux-musl-cpu
container: alpine:latest
make: SKIP_UNITTEST=1 LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_AVX2=ON"
- os: ubuntu-22.04
arch: x86_64
name: linux-musl-gpu
container: alpine:latest
make: SKIP_UNITTEST=1 LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_OPENCL=ON"
- os: ubuntu-22.04-arm
arch: arm64
name: linux-musl-cpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_CPU_ARM_ARCH=armv8.2-a"
- os: ubuntu-22.04-arm
arch: arm64
name: linux-musl-gpu
make: LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_OPENCL=ON"
- os: windows-2022
arch: x86_64
name: windows-cpu
make: SKIP_UNITTEST=1 LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_AVX2=ON"
- os: windows-2022
arch: x86_64
name: windows-gpu
make: SKIP_UNITTEST=1 LLAMA="-DGGML_NATIVE=OFF -DGGML_CPU=ON -DGGML_VULKAN=ON -DGGML_OPENCL=ON"
- os: ubuntu-22.04
arch: x86_64
name: android
make: SKIP_UNITTEST=1 PLATFORM=android ARCH=x86_64
sqlite-amalgamation-zip: https://sqlite.org/2025/sqlite-amalgamation-3490100.zip
- os: ubuntu-22.04
arch: arm64-v8a
name: android
make: SKIP_UNITTEST=1 PLATFORM=android ARCH=arm64-v8a
- os: macos-15
name: ios
make: PLATFORM=ios LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: macos-15
name: ios-sim
make: PLATFORM=ios-sim LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: macos-15
name: apple-xcframework
make: xcframework LLAMA="-DGGML_NATIVE=OFF -DGGML_METAL=ON -DGGML_ACCELERATE=ON -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=Apple" WHISPER="-DWHISPER_COREML=ON -DWHISPER_COREML_ALLOW_FALLBACK=ON"
- os: ubuntu-22.04
name: android-aar
make: aar
defaults:
run:
shell: ${{ matrix.container && 'sh' || 'bash' }}
steps:
- name: linux-musl x86_64 install dependencies
if: contains(matrix.name, 'linux-musl') && matrix.arch == 'x86_64'
run: apk update && apk add --no-cache git gcc g++ make cmake sqlite musl-dev linux-headers python3
- uses: actions/[email protected]
with:
submodules: true
- name: Prepare GGUF model directory
run: mkdir -p "${{ env.GGUF_MODEL_DIR }}"
- name: Restore GGUF cache
uses: actions/cache@v4
with:
path: ${{ needs.download-model.outputs.model-path }}
key: ${{ needs.download-model.outputs.cache-key }}
- name: android setup java
if: matrix.name == 'android-aar'
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: calculate cache version hashes for modules and dependencies
id: submodule-hashes
env:
MATRIX_MAKE: ${{ matrix.make && matrix.make || '' }}
run: |
LLAMA_HASH=$(git -C modules/llama.cpp rev-parse HEAD)
WHISPER_HASH=$(git -C modules/whisper.cpp rev-parse HEAD)
MINIAUDIO_HASH=$(git -C modules/miniaudio rev-parse HEAD)
if command -v sha256sum >/dev/null 2>&1; then
MAKE_HASH=$(echo "$MATRIX_MAKE" | sha256sum | cut -d' ' -f1)
MAKEFILE_HASH=$(sha256sum Makefile | cut -d' ' -f1)
DPCPP_MKL_HASH=$(echo "$WINDOWS_DPCPP_MKL" | sha256sum | cut -d' ' -f1)
BASEKIT_URL_HASH=$(echo "$WINDOWS_BASEKIT_URL" | sha256sum | cut -d' ' -f1)
elif command -v shasum >/dev/null 2>&1; then
MAKE_HASH=$(echo "$MATRIX_MAKE" | shasum -a 256 | cut -d' ' -f1)
MAKEFILE_HASH=$(shasum -a 256 Makefile | cut -d' ' -f1)
DPCPP_MKL_HASH=$(echo "$WINDOWS_DPCPP_MKL" | shasum -a 256 | cut -d' ' -f1)
BASEKIT_URL_HASH=$(echo "$WINDOWS_BASEKIT_URL" | shasum -a 256 | cut -d' ' -f1)
else
MAKE_HASH=$(echo "$MATRIX_MAKE" | openssl dgst -sha256 | cut -d' ' -f2)
MAKEFILE_HASH=$(openssl dgst -sha256 Makefile | cut -d' ' -f2)
DPCPP_MKL_HASH=$(echo "$WINDOWS_DPCPP_MKL" | openssl dgst -sha256 | cut -d' ' -f2)
BASEKIT_URL_HASH=$(echo "$WINDOWS_BASEKIT_URL" | openssl dgst -sha256 | cut -d' ' -f2)
fi
echo "llama=$LLAMA_HASH" >> $GITHUB_OUTPUT
echo "whisper=$WHISPER_HASH" >> $GITHUB_OUTPUT
echo "miniaudio=$MINIAUDIO_HASH" >> $GITHUB_OUTPUT
echo "make=$MAKE_HASH" >> $GITHUB_OUTPUT
echo "makefile=$MAKEFILE_HASH" >> $GITHUB_OUTPUT
echo "dpcpp-mkl=$DPCPP_MKL_HASH" >> $GITHUB_OUTPUT
echo "basekit-url=$BASEKIT_URL_HASH" >> $GITHUB_OUTPUT
- uses: msys2/[email protected]
if: matrix.os == 'windows-2022'
with:
msystem: mingw64
install: >-
git
make
sqlite
mingw-w64-x86_64-cc
mingw-w64-x86_64-cmake
${{ matrix.name == 'windows-gpu' && 'mingw-w64-x86_64-vulkan-headers' || '' }}
${{ matrix.name == 'windows-gpu' && 'mingw-w64-x86_64-vulkan-loader' || '' }}
${{ matrix.name == 'windows-gpu' && 'mingw-w64-x86_64-shaderc' || '' }}
${{ matrix.name == 'windows-gpu' && 'mingw-w64-x86_64-opencl-headers' || '' }}
${{ matrix.name == 'windows-gpu' && 'mingw-w64-x86_64-opencl-icd' || '' }}
- name: macos install dependencies
if: matrix.name == 'macos'
run: brew link sqlite --force
- name: linux-musl arm64 setup container
if: contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64'
run: |
docker run -d --name alpine \
--platform linux/arm64 \
-v ${{ github.workspace }}:/workspace \
-w /workspace \
alpine:latest \
tail -f /dev/null
docker exec alpine sh -c "apk update && apk add --no-cache gcc g++ make cmake sqlite musl-dev linux-headers python3"
- name: linux install opencl
if: matrix.name == 'linux-gpu'
run: sudo apt-get install -y opencl-headers ocl-icd-opencl-dev
- name: linux-musl install opencl
if: matrix.name == 'linux-musl-gpu'
run: ${{ matrix.arch == 'arm64' && 'docker exec alpine' || '' }} apk add --no-cache opencl-headers opencl-icd-loader-dev
- name: linux-x86_64 install vulkan
if: matrix.name == 'linux-gpu' && matrix.arch == 'x86_64'
run: |
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
sudo apt-get update -y
sudo apt-get install -y mesa-vulkan-drivers
# Vulkan is no longer packed for Ubuntu
wget https://sdk.lunarg.com/sdk/download/latest/linux/vulkan-sdk.tar.xz?Human=true -O vulkan-sdk.tar.xz
tar -xf vulkan-sdk.tar.xz
cd $(ls -d 1.* | head -n1)
source setup-env.sh
echo "VULKAN_SDK=$VULKAN_SDK" >> $GITHUB_ENV
echo "PATH=$PATH" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV
echo "VK_ADD_LAYER_PATH=$VK_ADD_LAYER_PATH" >> $GITHUB_ENV
- name: linux-arm64 install vulkan
if: matrix.name == 'linux-gpu' && matrix.arch == 'arm64'
run: |
sudo dpkg --add-architecture arm64
# Add arch-specific repositories for non-amd64 architectures
cat << EOF | sudo tee /etc/apt/sources.list.d/arm64-ports.list
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble-updates main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble-security main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble-backports main universe
EOF
sudo apt-get update || true ;# Prevent failure due to missing URLs.
sudo apt-get install -y --no-install-recommends build-essential glslc crossbuild-essential-arm64 libvulkan-dev:arm64
- name: cache llama.cpp build
id: cache-llama
uses: actions/cache@v4
with:
path: |
build/ggml
build/llama.cpp
build/llama.cpp.stamp
key: llama-${{ matrix.name }}-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.submodule-hashes.outputs.llama }}-${{ hashFiles('modules/llama.cpp/**') }}-${{ steps.submodule-hashes.outputs.make }}-${{ steps.submodule-hashes.outputs.makefile }}
- name: cache whisper.cpp build
id: cache-whisper
uses: actions/cache@v4
with:
path: |
build/whisper.cpp
build/whisper.cpp.stamp
key: whisper-${{ matrix.name }}-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.submodule-hashes.outputs.whisper }}-${{ hashFiles('modules/whisper.cpp/**') }}-${{ steps.submodule-hashes.outputs.make }}-${{ steps.submodule-hashes.outputs.makefile }}
- name: cache miniaudio build
id: cache-miniaudio
uses: actions/cache@v4
with:
path: |
build/miniaudio
build/miniaudio.stamp
key: miniaudio-${{ matrix.name }}-${{ matrix.os }}-${{ matrix.arch }}-${{ steps.submodule-hashes.outputs.miniaudio }}-${{ hashFiles('modules/miniaudio/**') }}-${{ steps.submodule-hashes.outputs.make }}-${{ steps.submodule-hashes.outputs.makefile }}
- name: windows build llama.cpp
if: matrix.os == 'windows-2022' && steps.cache-llama.outputs.cache-hit != 'true'
shell: msys2 {0}
run: make build/llama.cpp.stamp ${{ matrix.make && matrix.make || ''}}
env:
VULKAN_SDK: "C:/msys64/mingw64"
- name: unix build llama.cpp
if: matrix.os != 'windows-2022'&& matrix.name != 'android-aar' && matrix.name != 'apple-xcframework' && steps.cache-llama.outputs.cache-hit != 'true'
run: ${{ contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64' && 'docker exec alpine' || '' }} make build/llama.cpp.stamp ${{ matrix.make && matrix.make || ''}}
- name: windows build whisper.cpp
if: matrix.os == 'windows-2022' && steps.cache-whisper.outputs.cache-hit != 'true'
shell: msys2 {0}
run: make build/whisper.cpp.stamp ${{ matrix.make && matrix.make || ''}}
env:
VULKAN_SDK: "C:/msys64/mingw64"
- name: unix build whisper.cpp
if: matrix.os != 'windows-2022'&& matrix.name != 'android-aar' && matrix.name != 'apple-xcframework' && steps.cache-whisper.outputs.cache-hit != 'true'
run: ${{ contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64' && 'docker exec alpine' || '' }} make build/whisper.cpp.stamp ${{ matrix.make && matrix.make || ''}}
- name: windows build miniaudio
if: matrix.os == 'windows-2022' && steps.cache-miniaudio.outputs.cache-hit != 'true'
shell: msys2 {0}
run: make build/miniaudio.stamp ${{ matrix.make && matrix.make || ''}}
- name: unix build miniaudio
if: matrix.os != 'windows-2022'&& matrix.name != 'android-aar' && matrix.name != 'apple-xcframework' && steps.cache-miniaudio.outputs.cache-hit != 'true'
run: ${{ contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64' && 'docker exec alpine' || '' }} make build/miniaudio.stamp ${{ matrix.make && matrix.make || ''}}
- name: windows build sqlite-ai
if: matrix.os == 'windows-2022'
run: make extension ${{ matrix.make && matrix.make || ''}}
shell: msys2 {0}
env:
VULKAN_SDK: "C:/msys64/mingw64"
- name: unix build sqlite-ai
if: matrix.os != 'windows-2022'
run: ${{ contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64' && 'docker exec alpine' || '' }} make extension ${{ matrix.make && matrix.make || ''}}
- name: create keychain for codesign
if: matrix.os == 'macos-15'
run: |
echo "${{ secrets.APPLE_CERTIFICATE }}" | base64 --decode > certificate.p12
security create-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
security import certificate.p12 -k build.keychain -P "${{ secrets.CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${{ secrets.KEYCHAIN_PASSWORD }}" build.keychain
- name: codesign and notarize dylib
if: matrix.os == 'macos-15' && matrix.name != 'apple-xcframework'
run: |
codesign --sign "${{ secrets.APPLE_TEAM_ID }}" --timestamp --options runtime dist/ai.dylib
ditto -c -k dist/ai.dylib dist/ai.zip
xcrun notarytool submit dist/ai.zip --apple-id "${{ secrets.APPLE_ID }}" --password "${{ secrets.APPLE_PASSWORD }}" --team-id "${{ secrets.APPLE_TEAM_ID }}" --wait
rm dist/ai.zip
- name: codesign and notarize xcframework
if: matrix.name == 'apple-xcframework'
run: |
find dist/ai.xcframework -name "*.framework" -exec echo "Signing: {}" \; -exec codesign --sign "${{ secrets.APPLE_TEAM_ID }}" --timestamp --options runtime {} \; # Sign each individual framework FIRST
codesign --sign "${{ secrets.APPLE_TEAM_ID }}" --timestamp --options runtime dist/ai.xcframework # Then sign the xcframework wrapper
ditto -c -k --keepParent dist/ai.xcframework dist/ai.xcframework.zip
xcrun notarytool submit dist/ai.xcframework.zip --apple-id "${{ secrets.APPLE_ID }}" --password "${{ secrets.APPLE_PASSWORD }}" --team-id "${{ secrets.APPLE_TEAM_ID }}" --wait
rm dist/ai.xcframework.zip
- name: cleanup keychain for codesign
if: matrix.os == 'macos-15'
run: |
rm certificate.p12
security delete-keychain build.keychain
- name: android setup test environment
if: matrix.name == 'android' && matrix.arch != 'arm64-v8a'
run: |
echo "::group::enable kvm group perms"
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
echo "::endgroup::"
echo "::group::download and build sqlite3 without SQLITE_OMIT_LOAD_EXTENSION"
curl -O ${{ matrix.sqlite-amalgamation-zip }}
unzip sqlite-amalgamation-*.zip
export ${{ matrix.make }}
$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/${{ matrix.arch }}-linux-android26-clang sqlite-amalgamation-*/shell.c sqlite-amalgamation-*/sqlite3.c -o sqlite3 -ldl
# remove unused folders to save up space
rm -rf sqlite-amalgamation-*.zip sqlite-amalgamation-*
echo "::endgroup::"
echo "::group::prepare the test script"
make test PLATFORM=$PLATFORM ARCH=$ARCH || echo "It should fail. Running remaining commands in the emulator"
cat > commands.sh << EOF
mv -f /data/local/tmp/sqlite3 /system/xbin
cd /data/local/tmp
$(make test PLATFORM=$PLATFORM ARCH=$ARCH -n)
EOF
# remove big unused folders to avoid emulator errors
echo "::endgroup::"
- name: android test sqlite-ai
if: matrix.name == 'android' && matrix.arch != 'arm64-v8a'
uses: reactivecircus/[email protected]
with:
api-level: 26
arch: ${{ matrix.arch }}
script: |
adb root
adb remount
adb push ${{ github.workspace }}/commands.sh /data/local/tmp/
adb push ${{ github.workspace }}/sqlite3 /data/local/tmp/
adb push ${{ github.workspace }}/dist /data/local/tmp/
adb push ${{ github.workspace }}/Makefile /data/local/tmp/
adb shell "sh /data/local/tmp/commands.sh"
- name: windows test sqlite-ai
if: matrix.os == 'windows-2022'
run: make test ${{ matrix.make && matrix.make || ''}}
shell: msys2 {0}
env:
VULKAN_SDK: "C:/msys64/mingw64"
- name: unix test sqlite-ai
if: contains(matrix.name, 'linux') || ( matrix.name == 'macos' && matrix.arch != 'x86_64' )
run: ${{ contains(matrix.name, 'linux-musl') && matrix.arch == 'arm64' && 'docker exec alpine' || '' }} make test ${{ matrix.make && matrix.make || ''}}
- uses: actions/[email protected]
if: always()
with:
name: ai-${{ matrix.name }}${{ matrix.arch && format('-{0}', matrix.arch) || '' }}
path: dist/ai.*
if-no-files-found: error
release:
runs-on: ubuntu-22.04
name: release
needs: build
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
with:
path: artifacts
- name: zip artifacts
run: |
VERSION=$(make version)
for folder in "artifacts"/*; do
if [ -d "$folder" ]; then
name=$(basename "$folder")
if [[ "$name" != "ai-apple-xcframework" && "$name" != "ai-android-aar" ]]; then
tar -czf "${name}-${VERSION}.tar.gz" -C "$folder" .
fi
if [[ "$name" != "ai-android-aar" ]]; then
(cd "$folder" && zip -rq "../../${name}-${VERSION}.zip" .)
else
cp "$folder"/*.aar "${name}-${VERSION}.aar"
fi
fi
done
- name: release tag version from sqlite-ai.h
id: tag
run: |
VERSION=$(make version)
if [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
LATEST_RELEASE=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" https://api.github.com/repos/${{ github.repository }}/releases/latest)
LATEST=$(echo "$LATEST_RELEASE" | jq -r '.name')
# Check artifact sizes against previous release
if [ -n "$LATEST" ] && [ "$LATEST" != "null" ]; then
echo "Checking artifact sizes against previous release: $LATEST"
FAILED=0
for artifact in ai-*-${VERSION}.*; do
if [ ! -f "$artifact" ]; then
continue
fi
# Get current artifact size
NEW_SIZE=$(stat -c%s "$artifact" 2>/dev/null || stat -f%z "$artifact")
# Get artifact name for previous release
ARTIFACT_NAME=$(echo "$artifact" | sed "s/${VERSION}/${LATEST}/")
# Get previous artifact size from GitHub API
OLD_SIZE=$(echo "$LATEST_RELEASE" | jq -r ".assets[] | select(.name == \"$(basename "$ARTIFACT_NAME")\") | .size")
if [ -z "$OLD_SIZE" ] || [ "$OLD_SIZE" = "null" ]; then
echo "⚠️ Previous artifact not found: $(basename "$ARTIFACT_NAME"), skipping comparison"
continue
fi
# Calculate percentage increase
INCREASE=$(awk "BEGIN {printf \"%.2f\", (($NEW_SIZE - $OLD_SIZE) / $OLD_SIZE) * 100}")
echo "📦 $artifact: $OLD_SIZE → $NEW_SIZE bytes (${INCREASE}% change)"
# Check if increase is more than 5%
if (( $(echo "$INCREASE > 5" | bc -l) )); then
echo "❌ ERROR: $artifact size increased by ${INCREASE}% (limit: 5%)"
FAILED=1
fi
done
if [ $FAILED -eq 1 ]; then
echo ""
echo "❌ One or more artifacts exceeded the 5% size increase limit"
exit 1
fi
echo "✅ All artifacts within 5% size increase limit"
fi
if [[ "$VERSION" != "$LATEST" || "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "::warning file=src/sqlite-ai.h::To release a new version, please update the SQLITE_AI_VERSION in src/sqlite-ai.h to be different than the latest $LATEST"
fi
exit 0
fi
echo "❌ SQLITE_AI_VERSION not found in sqlite-ai.h"
exit 1
- uses: actions/setup-java@v4
if: steps.tag.outputs.version != ''
with:
distribution: 'temurin'
java-version: '17'
- name: release android aar to maven central
if: steps.tag.outputs.version != ''
run: cd packages/android && ./gradlew publishAggregationToCentralPortal -PSIGNING_KEY="${{ secrets.SIGNING_KEY }}" -PSIGNING_PASSWORD="${{ secrets.SIGNING_PASSWORD }}" -PSONATYPE_USERNAME="${{ secrets.MAVEN_CENTRAL_USERNAME }}" -PSONATYPE_PASSWORD="${{ secrets.MAVEN_CENTRAL_TOKEN }}" -PVERSION="${{ steps.tag.outputs.version }}" -PAAR_PATH="../../artifacts/ai-android-aar/ai.aar"
- uses: actions/setup-node@v4
if: steps.tag.outputs.version != ''
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: update npm
if: steps.tag.outputs.version != ''
run: npm install -g [email protected]
- name: build and publish npm packages
if: steps.tag.outputs.version != ''
run: |
cd packages/node
# Update version in package.json
echo "Updating versions to ${{ steps.tag.outputs.version }}..."
# Update package.json
jq --arg version "${{ steps.tag.outputs.version }}" \
'.version = $version | .optionalDependencies = (.optionalDependencies | with_entries(.value = $version))' \
package.json > package.tmp.json && mv package.tmp.json package.json
echo "✓ Updated package.json to version ${{ steps.tag.outputs.version }}"
# Generate platform packages
echo "Generating platform packages..."
node generate-platform-packages.js "${{ steps.tag.outputs.version }}" "../../artifacts" "./platform-packages"
echo "✓ Generated 7 platform packages"
ls -la platform-packages/
# Build main package
echo "Building main package..."
npm install
npm run build
npm test
echo "✓ Main package built and tested"
# Publish platform packages
echo "Publishing platform packages to npm..."
cd platform-packages
for platform_dir in */; do
platform_name=$(basename "$platform_dir")
echo " Publishing @sqliteai/sqlite-ai-${platform_name}..."
cd "$platform_dir"
npm publish --provenance --access public
cd ..
echo " ✓ Published @sqliteai/sqlite-ai-${platform_name}"
done
cd ..
# Publish main package
echo "Publishing main package to npm..."
npm publish --provenance --access public
echo "✓ Published @sqliteai/sqlite-ai@${{ steps.tag.outputs.version }}"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Successfully published 8 packages to npm"
echo " Main: @sqliteai/sqlite-ai@${{ steps.tag.outputs.version }}"
echo " Platform packages: 7"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- uses: softprops/[email protected]
if: steps.tag.outputs.version != ''
with:
body: |
# Packages
[**Node**](https://www.npmjs.com/package/@sqliteai/sqlite-ai): `npm install @sqliteai/sqlite-ai`
[**Android**](https://central.sonatype.com/artifact/ai.sqlite/ai): `ai.sqlite:ai:${{ steps.tag.outputs.version }}`
[**Python**](https://pypi.org/project/sqlite-ai): `pip install sqlite-ai`
[**Swift**](https://github.com/sqliteai/sqlite-ai#swift-package): [Installation Guide](https://github.com/sqliteai/sqlite-ai#swift-package)
---
generate_release_notes: true
tag_name: ${{ steps.tag.outputs.version }}
files: ai-*-${{ steps.tag.outputs.version }}.*
make_latest: true