Skip to content

Release

Release #144

Workflow file for this run

name: Release
on:
workflow_dispatch:
inputs:
platforms:
description: "Comma-separated platforms (like --platforms). Ignored if all_platforms=true"
required: false
default: "linux-x86_64,macos-aarch64,linux-aarch64"
all_platforms:
description: "Build all platforms (like --all-platforms)"
type: boolean
required: false
default: false
github_release:
description: "Publish GitHub Release"
type: boolean
required: false
default: true
pre_release:
description: "Mark GitHub release as pre-release"
type: boolean
required: false
default: true
docker_push:
description: "Push Docker image to Docker Hub"
type: boolean
required: false
default: true
docker_repo:
description: "Docker Hub repo (owner/name)"
required: false
default: "jamals86/kalamdb"
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
RUST_VERSION: "1.92.0"
WASM_PACK_VERSION: "0.14.0"
PG_EXTENSION_MAJOR: "16"
PG_EXTENSION_FLAVOR: "pg16"
PGRX_VERSION: "0.17.0"
DEFAULT_PLATFORMS: "linux-x86_64,macos-aarch64,linux-aarch64"
RUSTC_WRAPPER: ""
CARGO_BUILD_RUSTC_WRAPPER: ""
jobs:
read_version:
name: Read Version from Cargo.toml
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
pre_release: ${{ steps.version.outputs.pre_release }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Read version and set outputs
id: version
shell: bash
run: |
set -euo pipefail
VERSION="$(sed -n '/^\[workspace.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p}' Cargo.toml | head -n1)"
if [[ -z "$VERSION" ]]; then
echo "Failed to read [workspace.package].version from Cargo.toml" >&2
exit 1
fi
TAG="v${VERSION}"
PRE_RELEASE="${{ github.event.inputs.pre_release }}"
if [[ -z "$PRE_RELEASE" ]]; then
PRE_RELEASE="true"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "pre_release=$PRE_RELEASE" >> "$GITHUB_OUTPUT"
echo "✅ Version: $VERSION, Tag: $TAG, Pre-release: $PRE_RELEASE"
license_compliance:
name: License Compliance
runs-on: ubuntu-latest
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Setup Node 22
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install cargo-license
shell: bash
run: |
set -euo pipefail
cargo install cargo-license --locked
- name: Generate Rust runtime license report
shell: bash
run: |
set -euo pipefail
mkdir -p target/license
cargo license -j --avoid-dev-deps --avoid-build-deps -o target/license/rust-runtime-licenses.json
- name: Install UI dependencies
shell: bash
working-directory: ui
run: |
set -euo pipefail
npm install --no-audit --no-fund
- name: Generate UI production license report
shell: bash
run: |
set -euo pipefail
cd ui
npx --yes license-checker --production --excludePrivatePackages --json > ../target/license/ui-licenses.json
- name: Install SDK dependencies
shell: bash
working-directory: link/sdks/typescript/client
run: |
set -euo pipefail
npm install --no-audit --no-fund
- name: Generate SDK production license report
shell: bash
run: |
set -euo pipefail
cd link/sdks/typescript/client
npx --yes license-checker --production --json > "$GITHUB_WORKSPACE/target/license/sdk-licenses.json"
- name: Validate and generate third-party report
shell: bash
run: |
set -euo pipefail
python3 scripts/license_compliance.py \
--policy cargo-license-check.toml \
--rust-report target/license/rust-runtime-licenses.json \
--ui-report target/license/ui-licenses.json \
--sdk-report target/license/sdk-licenses.json \
--output target/license/THIRD_PARTY_LICENSES.md
- name: Build release license bundle
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p "dist/$VERSION"
cp LICENSE.txt "dist/$VERSION/LICENSE.txt"
cp NOTICE "dist/$VERSION/NOTICE"
cp target/license/THIRD_PARTY_LICENSES.md "dist/$VERSION/THIRD_PARTY_LICENSES.md"
- name: Upload license artifact
uses: actions/upload-artifact@v6
with:
name: dist-licenses
path: |
dist/${{ needs.read_version.outputs.version }}/LICENSE.txt
dist/${{ needs.read_version.outputs.version }}/NOTICE
dist/${{ needs.read_version.outputs.version }}/THIRD_PARTY_LICENSES.md
if-no-files-found: error
build_ui:
name: Build Admin UI
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: wasm32-unknown-unknown
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: wasm-build
cache-on-failure: true
- name: Setup Node 22
uses: actions/setup-node@v6
with:
node-version: 22
- name: Install wasm-pack
shell: bash
run: |
set -euo pipefail
cargo install wasm-pack --version "$WASM_PACK_VERSION" --locked
wasm-pack --version
- name: Build SDK (@kalamdb/client)
shell: bash
run: |
set -euo pipefail
cd link/sdks/typescript/client
npm install
npm run build
- name: Build Admin UI
shell: bash
run: |
set -euo pipefail
cd ui
npm install
npm run build
- name: Upload UI dist
uses: actions/upload-artifact@v6
with:
name: ui-dist
path: ui/dist/
if-no-files-found: error
build_cli_linux_x86_64:
name: Build CLI Linux x86_64
runs-on: ubuntu-24.04
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: x86_64-unknown-linux-gnu
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: cli-linux-x86_64-ubuntu-24-04
cache-on-failure: true
- name: Install system deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang \
libclang-dev \
pkg-config \
libssl-dev
- name: Build CLI linux-x86_64
shell: bash
run: |
set -euo pipefail
cargo build \
--profile release-dist \
--target x86_64-unknown-linux-gnu \
--bin kalam
- name: Package CLI artifact
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
CLI_OUT="kalamcli-${VERSION}-linux-x86_64"
cp "target/x86_64-unknown-linux-gnu/release-dist/kalam" "dist/$VERSION/$CLI_OUT"
chmod +x "dist/$VERSION/$CLI_OUT"
(cd "dist/$VERSION" && tar -czf "$CLI_OUT.tar.gz" "$CLI_OUT")
- name: Upload CLI artifact
uses: actions/upload-artifact@v6
with:
name: dist-cli-linux-x86_64
path: dist/${{ needs.read_version.outputs.version }}/*.tar.gz
if-no-files-found: error
build_cli_windows_x86_64:
name: Build CLI Windows x86_64
runs-on: ubuntu-latest
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: x86_64-pc-windows-gnu
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: cli-windows-x86_64
cache-on-failure: true
- name: Install system deps
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends zip mingw-w64
- name: Install cross
shell: bash
run: |
set -euo pipefail
cargo install cross --locked
- name: Build CLI windows-x86_64
shell: bash
env:
CROSS_CONTAINER_ENGINE_NO_BUILDKIT: "0"
run: |
set -euo pipefail
cross build \
--profile release-dist \
--target x86_64-pc-windows-gnu \
--bin kalam
- name: Package CLI artifact
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
CLI_OUT="kalamcli-${VERSION}-windows-x86_64.exe"
cp "target/x86_64-pc-windows-gnu/release-dist/kalam.exe" "dist/$VERSION/$CLI_OUT"
CROSS_IMAGE="ghcr.io/cross-rs/x86_64-pc-windows-gnu:latest"
docker run --rm -v "$PWD":/work "$CROSS_IMAGE" bash -lc '\
set -euo pipefail; \
DLL_DIR=/usr/x86_64-w64-mingw32/bin; \
for dll in libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll; do \
if [[ ! -f "${DLL_DIR}/${dll}" ]]; then \
echo "WARNING: Missing ${dll} in ${DLL_DIR}" >&2; \
continue; \
fi; \
cp "${DLL_DIR}/${dll}" "/work/dist/'"$VERSION"'/"; \
done' || echo "WARNING: Failed to copy Windows runtime DLLs from cross image"
(cd "dist/$VERSION" && zip -q "${CLI_OUT%.exe}.zip" "$CLI_OUT" *.dll 2>/dev/null || zip -q "${CLI_OUT%.exe}.zip" "$CLI_OUT")
- name: Upload CLI artifact
uses: actions/upload-artifact@v6
with:
name: dist-cli-windows-x86_64
path: dist/${{ needs.read_version.outputs.version }}/*.zip
if-no-files-found: error
build_cli_macos_arm:
name: Build CLI macOS ARM
runs-on: macos-14
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: aarch64-apple-darwin
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: cli-macos-aarch64
cache-on-failure: true
- name: Install LLVM (fix libclang.dylib)
shell: bash
run: |
set -euo pipefail
brew install llvm@16
echo "LIBCLANG_PATH=$(brew --prefix llvm@16)/lib" >> "$GITHUB_ENV"
echo "LLVM_CONFIG_PATH=$(brew --prefix llvm@16)/bin/llvm-config" >> "$GITHUB_ENV"
- name: Build CLI macos-aarch64
shell: bash
run: |
set -euo pipefail
cargo build \
--profile release-dist \
--target aarch64-apple-darwin \
--bin kalam
- name: Package CLI artifact
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
CLI_OUT="kalamcli-${VERSION}-macos-aarch64"
cp "target/aarch64-apple-darwin/release-dist/kalam" "dist/$VERSION/$CLI_OUT"
chmod +x "dist/$VERSION/$CLI_OUT"
(cd "dist/$VERSION" && tar -czf "$CLI_OUT.tar.gz" "$CLI_OUT")
- name: Upload CLI artifact
uses: actions/upload-artifact@v6
with:
name: dist-cli-macos-arm
path: dist/${{ needs.read_version.outputs.version }}/*.tar.gz
if-no-files-found: error
build_linux_x86_64:
name: Build Server Linux x86_64
runs-on: ubuntu-24.04
needs: [build_ui, read_version]
env:
PLATFORMS_INPUT: ${{ github.event.inputs.platforms }}
ALL_PLATFORMS_INPUT: ${{ github.event.inputs.all_platforms }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Determine platforms
id: vars
shell: bash
run: |
set -euo pipefail
# Determine requested platforms for this run
PLATFORMS_INPUT="${PLATFORMS_INPUT:-}"
if [[ -z "${PLATFORMS_INPUT}" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${{ github.event_name }}" == "push" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${ALL_PLATFORMS_INPUT:-}" == "true" ]]; then
PLATFORMS_INPUT="linux-x86_64,linux-aarch64,macos-x86_64,macos-aarch64,windows-x86_64"
fi
BUILD_LINUX=false
if [[ "$PLATFORMS_INPUT" == *"linux-x86_64"* ]]; then BUILD_LINUX=true; fi
{
echo "platforms=$PLATFORMS_INPUT"
echo "build_linux=$BUILD_LINUX"
} >> "$GITHUB_OUTPUT"
- name: Download UI dist
if: ${{ steps.vars.outputs.build_linux == 'true' }}
uses: actions/download-artifact@v6
with:
name: ui-dist
path: ui/dist
- name: Setup Rust
if: ${{ steps.vars.outputs.build_linux == 'true' }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: x86_64-unknown-linux-gnu
- name: Cache Rust
if: ${{ steps.vars.outputs.build_linux == 'true' }}
uses: Swatinem/rust-cache@v2
with:
shared-key: server-linux-x86_64-ubuntu-24-04
cache-on-failure: true
- name: Install system deps
if: ${{ steps.vars.outputs.build_linux == 'true' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang \
libclang-dev \
pkg-config \
libssl-dev
- name: Build linux-x86_64 (release-dist)
if: ${{ steps.vars.outputs.build_linux == 'true' }}
shell: bash
env:
SKIP_UI_BUILD: "1"
run: |
set -euo pipefail
cargo build \
--profile release-dist \
--target x86_64-unknown-linux-gnu \
--bin kalamdb-server
- name: Package linux artifacts
if: ${{ steps.vars.outputs.build_linux == 'true' }}
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
SRV_OUT="kalamdb-server-${VERSION}-linux-x86_64"
cp "target/x86_64-unknown-linux-gnu/release-dist/kalamdb-server" "dist/$VERSION/$SRV_OUT"
chmod +x "dist/$VERSION/$SRV_OUT"
(cd "dist/$VERSION" && tar -czf "$SRV_OUT.tar.gz" "$SRV_OUT")
- name: Upload artifacts
if: ${{ steps.vars.outputs.build_linux == 'true' }}
uses: actions/upload-artifact@v6
with:
name: dist-linux-x86_64
path: dist/${{ needs.read_version.outputs.version }}/*
if-no-files-found: error
build_linux_aarch64:
name: Build Server Linux ARM64
runs-on: ubuntu-latest
needs: [build_ui, read_version]
env:
PLATFORMS_INPUT: ${{ github.event.inputs.platforms }}
ALL_PLATFORMS_INPUT: ${{ github.event.inputs.all_platforms }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set build flags
id: vars
shell: bash
run: |
set -euo pipefail
# Always build linux-aarch64 for Docker multi-arch support
BUILD_LINUX_ARM=true
{
echo "build_linux_arm=$BUILD_LINUX_ARM"
} >> "$GITHUB_OUTPUT"
- name: Download UI dist
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
uses: actions/download-artifact@v6
with:
name: ui-dist
path: ui/dist
- name: Setup Rust
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: aarch64-unknown-linux-gnu
- name: Cache Rust
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
uses: Swatinem/rust-cache@v2
with:
shared-key: server-linux-aarch64
cache-on-failure: true
- name: Install cross
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
shell: bash
run: |
set -euo pipefail
cargo install cross --locked
- name: Build linux-aarch64 (cross, docker profile)
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
shell: bash
env:
CROSS_CONTAINER_ENGINE_NO_BUILDKIT: "0"
SKIP_UI_BUILD: "1"
run: |
set -euo pipefail
cross build \
--profile docker \
--target aarch64-unknown-linux-gnu \
--bin kalamdb-server
- name: Package linux-aarch64 artifacts
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
SRV_OUT="kalamdb-server-${VERSION}-linux-aarch64"
cp "target/aarch64-unknown-linux-gnu/docker/kalamdb-server" "dist/$VERSION/$SRV_OUT"
chmod +x "dist/$VERSION/$SRV_OUT"
(cd "dist/$VERSION" && tar -czf "$SRV_OUT.tar.gz" "$SRV_OUT")
- name: Upload artifacts
if: ${{ steps.vars.outputs.build_linux_arm == 'true' }}
uses: actions/upload-artifact@v6
with:
name: dist-linux-aarch64
path: dist/${{ needs.read_version.outputs.version }}/*
if-no-files-found: error
build_cli_linux_aarch64:
name: Build CLI Linux ARM64
runs-on: ubuntu-latest
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: aarch64-unknown-linux-gnu
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: cli-linux-aarch64
cache-on-failure: true
- name: Install cross
shell: bash
run: |
set -euo pipefail
cargo install cross --locked
- name: Build CLI linux-aarch64
shell: bash
env:
CROSS_CONTAINER_ENGINE_NO_BUILDKIT: "0"
run: |
set -euo pipefail
cross build \
--profile docker \
--target aarch64-unknown-linux-gnu \
--bin kalam
- name: Package CLI artifact
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
CLI_OUT="kalamcli-${VERSION}-linux-aarch64"
cp "target/aarch64-unknown-linux-gnu/docker/kalam" "dist/$VERSION/$CLI_OUT"
chmod +x "dist/$VERSION/$CLI_OUT"
(cd "dist/$VERSION" && tar -czf "$CLI_OUT.tar.gz" "$CLI_OUT")
- name: Upload CLI artifact
uses: actions/upload-artifact@v6
with:
name: dist-cli-linux-aarch64
path: dist/${{ needs.read_version.outputs.version }}/*.tar.gz
if-no-files-found: error
build_windows_x86_64:
name: Build Windows x86_64
runs-on: ubuntu-latest
needs: [build_ui, read_version]
env:
PLATFORMS_INPUT: ${{ github.event.inputs.platforms }}
ALL_PLATFORMS_INPUT: ${{ github.event.inputs.all_platforms }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Determine platforms
id: vars
shell: bash
run: |
set -euo pipefail
PLATFORMS_INPUT="${PLATFORMS_INPUT:-}"
if [[ -z "${PLATFORMS_INPUT}" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${{ github.event_name }}" == "push" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${ALL_PLATFORMS_INPUT:-}" == "true" ]]; then
PLATFORMS_INPUT="linux-x86_64,linux-aarch64,macos-x86_64,macos-aarch64,windows-x86_64"
fi
BUILD_WINDOWS=false
if [[ "$PLATFORMS_INPUT" == *"windows-x86_64"* ]]; then BUILD_WINDOWS=true; fi
{
echo "platforms=$PLATFORMS_INPUT"
echo "build_windows=$BUILD_WINDOWS"
} >> "$GITHUB_OUTPUT"
- name: Download UI dist
if: ${{ steps.vars.outputs.build_windows == 'true' }}
uses: actions/download-artifact@v6
with:
name: ui-dist
path: ui/dist
- name: Setup Rust
if: ${{ steps.vars.outputs.build_windows == 'true' }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: x86_64-pc-windows-gnu
- name: Cache Rust
if: ${{ steps.vars.outputs.build_windows == 'true' }}
uses: Swatinem/rust-cache@v2
with:
shared-key: server-windows-x86_64
cache-on-failure: true
- name: Install system deps
if: ${{ steps.vars.outputs.build_windows == 'true' }}
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends zip mingw-w64
- name: Install cross
if: ${{ steps.vars.outputs.build_windows == 'true' }}
shell: bash
run: |
set -euo pipefail
cargo install cross --locked
- name: Build windows-x86_64 (cross, release-dist profile)
if: ${{ steps.vars.outputs.build_windows == 'true' }}
shell: bash
env:
CROSS_CONTAINER_ENGINE_NO_BUILDKIT: "0"
SKIP_UI_BUILD: "1"
run: |
set -euo pipefail
cross build \
--profile release-dist \
--target x86_64-pc-windows-gnu \
--bin kalamdb-server
- name: Package windows artifacts
if: ${{ steps.vars.outputs.build_windows == 'true' }}
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
SRV_OUT="kalamdb-server-${VERSION}-windows-x86_64.exe"
cp "target/x86_64-pc-windows-gnu/release-dist/kalamdb-server.exe" "dist/$VERSION/$SRV_OUT"
for dll in libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll; do
dll_path="$(x86_64-w64-mingw32-gcc -print-file-name="$dll" 2>/dev/null || true)"
if [[ -z "$dll_path" || "$dll_path" == "$dll" || ! -f "$dll_path" ]]; then
dll_path="$(find /usr /opt -path "*x86_64-w64-mingw32*" -type f -name "$dll" -print -quit 2>/dev/null || true)"
fi
if [[ -z "$dll_path" || ! -f "$dll_path" ]]; then
echo "Missing $dll (searched via x86_64-w64-mingw32-gcc and /usr,/opt)" >&2
exit 1
fi
cp "$dll_path" "dist/$VERSION/"
done
(cd "dist/$VERSION" && zip -q "${SRV_OUT%.exe}.zip" "$SRV_OUT" libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll)
- name: Upload artifacts
if: ${{ steps.vars.outputs.build_windows == 'true' }}
uses: actions/upload-artifact@v6
with:
name: dist-windows-x86_64
path: dist/${{ needs.read_version.outputs.version }}/*
if-no-files-found: error
build_macos_arm:
name: Build macOS ARM
runs-on: macos-14
needs: [build_ui, read_version]
env:
PLATFORMS_INPUT: ${{ github.event.inputs.platforms }}
ALL_PLATFORMS_INPUT: ${{ github.event.inputs.all_platforms }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Determine platforms
id: vars
shell: bash
run: |
set -euo pipefail
PLATFORMS_INPUT="${PLATFORMS_INPUT:-}"
if [[ -z "${PLATFORMS_INPUT}" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${{ github.event_name }}" == "push" ]]; then
PLATFORMS_INPUT="${{ env.DEFAULT_PLATFORMS }}"
fi
if [[ "${ALL_PLATFORMS_INPUT:-}" == "true" ]]; then
PLATFORMS_INPUT="linux-x86_64,linux-aarch64,macos-x86_64,macos-aarch64,windows-x86_64"
fi
BUILD_MACOS=false
if [[ "$PLATFORMS_INPUT" == *"macos-aarch64"* ]]; then BUILD_MACOS=true; fi
{
echo "platforms=$PLATFORMS_INPUT"
echo "build_macos=$BUILD_MACOS"
} >> "$GITHUB_OUTPUT"
- name: Download UI dist
if: ${{ steps.vars.outputs.build_macos == 'true' }}
uses: actions/download-artifact@v6
with:
name: ui-dist
path: ui/dist
- name: Setup Rust
if: ${{ steps.vars.outputs.build_macos == 'true' }}
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
targets: aarch64-apple-darwin
- name: Cache Rust
if: ${{ steps.vars.outputs.build_macos == 'true' }}
uses: Swatinem/rust-cache@v2
with:
shared-key: server-macos-aarch64
cache-on-failure: true
- name: Install LLVM (fix libclang.dylib)
if: ${{ steps.vars.outputs.build_macos == 'true' }}
shell: bash
run: |
set -euo pipefail
brew install llvm@16
echo "LIBCLANG_PATH=$(brew --prefix llvm@16)/lib" >> "$GITHUB_ENV"
echo "LLVM_CONFIG_PATH=$(brew --prefix llvm@16)/bin/llvm-config" >> "$GITHUB_ENV"
- name: Build macos-aarch64 (release-dist)
if: ${{ steps.vars.outputs.build_macos == 'true' }}
shell: bash
env:
SKIP_UI_BUILD: "1"
run: |
set -euo pipefail
cargo build \
--profile release-dist \
--target aarch64-apple-darwin \
--bin kalamdb-server
- name: Smoke test macOS ARM server
if: ${{ steps.vars.outputs.build_macos == 'true' }}
shell: bash
run: |
set -euo pipefail
cp backend/server.example.toml server.toml
sed -i '' 's|rocksdb_path = "./data/rocksdb"|rocksdb_path = "./test-data/rocksdb"|g' server.toml
sed -i '' 's|default_storage_path = "./data/storage"|default_storage_path = "./test-data/storage"|g' server.toml
sed -i '' 's|logs_path = "./logs"|logs_path = "./test-data/logs"|g' server.toml
sed -i '' 's|jwt_secret = ".*"|jwt_secret = "smoke-test-secret-key-minimum-32-characters-long-for-ci"|g' server.toml
mkdir -p test-data/rocksdb test-data/storage test-data/logs
./target/aarch64-apple-darwin/release-dist/kalamdb-server &
SERVER_PID=$!
echo "Waiting for server to start..."
for i in {1..60}; do
if curl -s http://localhost:8080/v1/api/healthcheck > /dev/null 2>&1; then
echo "Server is ready!"
break
fi
if ! kill -0 $SERVER_PID 2>/dev/null; then
echo "Server process died"
exit 1
fi
echo "Waiting... ($i/60)"
sleep 1
done
if ! curl -s http://localhost:8080/v1/api/healthcheck > /dev/null 2>&1; then
echo "Server failed to start within 60 seconds"
kill $SERVER_PID 2>/dev/null || true
exit 1
fi
kill $SERVER_PID 2>/dev/null || true
- name: Package macOS artifacts
if: ${{ steps.vars.outputs.build_macos == 'true' }}
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p dist/$VERSION
SRV_OUT="kalamdb-server-${VERSION}-macos-aarch64"
cp "target/aarch64-apple-darwin/release-dist/kalamdb-server" "dist/$VERSION/$SRV_OUT"
chmod +x "dist/$VERSION/$SRV_OUT"
(cd "dist/$VERSION" && tar -czf "$SRV_OUT.tar.gz" "$SRV_OUT")
- name: Upload artifacts (macos arm)
if: ${{ steps.vars.outputs.build_macos == 'true' }}
uses: actions/upload-artifact@v6
with:
name: dist-macos-arm
path: dist/${{ needs.read_version.outputs.version }}/*
if-no-files-found: error
# ═══════════════════════════════════════════════════════════════════════════
# PG EXTENSION - Build pg_kalam PostgreSQL extension for Linux
# ═══════════════════════════════════════════════════════════════════════════
build_pg_extension_x86_64:
name: Build PG Extension Linux x86_64
runs-on: ubuntu-latest
needs: read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Build extension in Docker (x86_64 Bookworm)
shell: bash
run: |
set -euo pipefail
# Build the builder image for the selected PostgreSQL major.
docker build \
--build-arg PG_MAJOR="$PG_EXTENSION_MAJOR" \
--build-arg PGRX_VERSION="$PGRX_VERSION" \
-f pg/docker/Dockerfile.builder-base \
-t pg-kalam-builder-x86 \
.
# Compile extension inside the Bookworm container
mkdir -p artifacts .cargo/pg-extension-x86/registry .cargo/pg-extension-x86/git .cargo/pg-extension-x86/target
docker run --rm \
-v "$PWD:/src:ro" \
-v "$PWD/artifacts:/artifacts" \
-v "$PWD/.cargo/pg-extension-x86/registry:/usr/local/cargo/registry" \
-v "$PWD/.cargo/pg-extension-x86/git:/usr/local/cargo/git" \
-v "$PWD/.cargo/pg-extension-x86/target:/target-cache" \
-e CARGO_INCREMENTAL=0 \
-e CARGO_TARGET_DIR=/target-cache \
-e RUSTFLAGS="-Cdebuginfo=0" \
-e PG_EXTENSION_MAJOR \
-e PG_EXTENSION_FLAVOR \
pg-kalam-builder-x86 \
bash -c '
cd /src && \
cargo pgrx install \
-p kalam-pg-extension \
-c "/usr/lib/postgresql/${PG_EXTENSION_MAJOR}/bin/pg_config" \
--no-default-features \
--profile release-pg \
-F ${PG_EXTENSION_FLAVOR} && \
LIBDIR="$(/usr/lib/postgresql/${PG_EXTENSION_MAJOR}/bin/pg_config --pkglibdir)" && \
SQLDIR="$(/usr/lib/postgresql/${PG_EXTENSION_MAJOR}/bin/pg_config --sharedir)/extension" && \
cp "$LIBDIR/pg_kalam.so" /artifacts/ && \
cp "$SQLDIR/pg_kalam.control" /artifacts/ && \
cp "$SQLDIR"/pg_kalam--*.sql /artifacts/
'
- name: Package extension artifacts
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
PKG="pg_kalam-${VERSION}-${PG_EXTENSION_FLAVOR}-linux-x86_64"
mkdir -p "dist/${VERSION}/${PKG}"
cp artifacts/pg_kalam.so "dist/${VERSION}/${PKG}/"
cp artifacts/pg_kalam.control "dist/${VERSION}/${PKG}/"
cp artifacts/pg_kalam--*.sql "dist/${VERSION}/${PKG}/"
cat > "dist/${VERSION}/${PKG}/install.sh" << 'INSTALL_EOF'
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PG_CONFIG="${1:-pg_config}"
LIBDIR="$("$PG_CONFIG" --pkglibdir)"
SHAREDIR="$("$PG_CONFIG" --sharedir)/extension"
echo "Installing pg_kalam to $LIBDIR and $SHAREDIR ..."
install -m 755 "$SCRIPT_DIR/pg_kalam.so" "$LIBDIR/"
install -m 644 "$SCRIPT_DIR/pg_kalam.control" "$SHAREDIR/"
install -m 644 "$SCRIPT_DIR"/pg_kalam--*.sql "$SHAREDIR/"
echo "Done. Run: CREATE EXTENSION pg_kalam;"
INSTALL_EOF
chmod +x "dist/${VERSION}/${PKG}/install.sh"
(cd "dist/${VERSION}" && tar -czf "${PKG}.tar.gz" "${PKG}")
rm -rf "dist/${VERSION}/${PKG}"
ls -lh "dist/${VERSION}/"
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
name: dist-pg-extension-linux-x86_64
path: dist/${{ needs.read_version.outputs.version }}/*
if-no-files-found: error
# ═══════════════════════════════════════════════════════════════════════════
# SMOKE TESTS - Run against the pre-built Linux x86_64 binary
# ═══════════════════════════════════════════════════════════════════════════
smoke_tests:
name: Smoke Tests (Linux x86_64)
runs-on: ubuntu-latest
needs:
- build_linux_x86_64
- build_cli_linux_x86_64
- read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download server binary
uses: actions/download-artifact@v6
with:
name: dist-linux-x86_64
path: dist/
- name: Download CLI binary
uses: actions/download-artifact@v6
with:
name: dist-cli-linux-x86_64
path: dist/
- name: Install system dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang \
libclang-dev \
llvm-dev \
pkg-config \
libssl-dev
- name: Setup Rust (for running tests)
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: smoke-tests
cache-on-failure: true
- name: Install cargo-nextest
shell: bash
run: |
set -euo pipefail
curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
cargo nextest --version
- name: Prepare binaries
shell: bash
run: |
set -euo pipefail
find dist -name "*.tar.gz" -type f -exec tar -xzf {} -C dist/ \;
SERVER_BIN=$(find dist -name "kalamdb-server-*-linux-x86_64" -type f ! -name "*.tar.gz" | head -1)
[[ -z "$SERVER_BIN" ]] && { echo "Server binary not found"; exit 1; }
chmod +x "$SERVER_BIN" && cp "$SERVER_BIN" ./kalamdb-server
CLI_BIN=$(find dist -name "kalamcli-*-linux-x86_64" -type f ! -name "*.tar.gz" | head -1)
[[ -z "$CLI_BIN" ]] && { echo "CLI binary not found"; exit 1; }
chmod +x "$CLI_BIN" && cp "$CLI_BIN" ./kalamcli
ls -la ./kalamdb-server ./kalamcli
- name: Create server config
shell: bash
run: |
cp backend/server.example.toml server.toml
sed -i 's|rocksdb_path = "./data/rocksdb"|rocksdb_path = "./test-data/rocksdb"|g' server.toml
sed -i 's|default_storage_path = "./data/storage"|default_storage_path = "./test-data/storage"|g' server.toml
sed -i 's|logs_path = "./logs"|logs_path = "./test-data/logs"|g' server.toml
sed -i 's|jwt_secret = ".*"|jwt_secret = "smoke-test-secret-key-minimum-32-characters-long-for-ci"|g' server.toml
sed -i 's|max_queries_per_sec = 1000|max_queries_per_sec = 10000|g' server.toml
sed -i 's|max_messages_per_sec = 500|max_messages_per_sec = 1000|g' server.toml
- name: Start server
shell: bash
run: |
set -euo pipefail
mkdir -p test-data/rocksdb test-data/storage test-data/logs
./kalamdb-server > server.log 2>&1 &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV"
for i in {1..60}; do
if curl -sf http://localhost:8080/health > /dev/null 2>&1; then
echo "✅ Server is ready (${i}s)"
exit 0
fi
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "❌ Server process died"
cat server.log || true
exit 1
fi
echo " Waiting... ($i/60)"
sleep 1
done
echo "❌ Timed out waiting for server"
cat server.log || true
exit 1
- name: Run smoke tests
id: smoke_tests
continue-on-error: true
shell: bash
env:
KALAMDB_SERVER_URL: "http://localhost:8080"
KALAMDB_ROOT_PASSWORD: "kalamdb123"
run: |
set -euo pipefail
cd cli
if cargo nextest run --features e2e-tests --test smoke 2>&1 | tee ../smoke-test-output.txt; then
echo "smoke_tests_passed=true" >> "$GITHUB_OUTPUT"
echo "✅ All smoke tests passed!"
else
echo "smoke_tests_passed=false" >> "$GITHUB_OUTPUT"
echo "⚠️ Some smoke tests failed"
fi
- name: Validate packaged CLI binary
shell: bash
env:
KALAMDB_SERVER_URL: "http://localhost:8080"
run: |
set -euo pipefail
./kalamcli \
-u "$KALAMDB_SERVER_URL" \
--username root \
--password kalamdb123 \
--json \
--command "SELECT 1 AS packaged_cli_release_check" \
| tee packaged-cli-smoke-output.txt
grep -q 'packaged_cli_release_check' packaged-cli-smoke-output.txt
- name: Stop server
if: always()
shell: bash
run: |
[[ -n "${SERVER_PID:-}" ]] && kill "$SERVER_PID" 2>/dev/null || true
- name: Upload test results
if: always()
uses: actions/upload-artifact@v6
with:
name: smoke-test-results
path: |
smoke-test-output.txt
packaged-cli-smoke-output.txt
server.log
if-no-files-found: ignore
- name: Report test status
if: always()
shell: bash
run: |
if [[ "${{ steps.smoke_tests.outputs.smoke_tests_passed }}" == "true" ]]; then
echo "::notice::✅ Smoke tests passed successfully"
else
echo "::error::Smoke tests failed - review smoke-test-results artifact for details"
fi
- name: Fail if smoke tests failed
if: always() && steps.smoke_tests.outputs.smoke_tests_passed != 'true'
shell: bash
run: |
echo "Smoke tests failed" >&2
exit 1
# ═══════════════════════════════════════════════════════════════════════════
# PG EXTENSION TESTS - Native e2e suite against live KalamDB + pgrx PostgreSQL
# ═══════════════════════════════════════════════════════════════════════════
pg_extension_tests_native:
name: PG Extension Tests (native)
runs-on: ubuntu-latest
needs:
- build_linux_x86_64
- read_version
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Download server binary
uses: actions/download-artifact@v6
with:
name: dist-linux-x86_64
path: dist/
- name: Install system dependencies
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
clang \
libclang-dev \
llvm-dev \
pkg-config \
libssl-dev \
libreadline-dev
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
shared-key: pg-extension-tests-native
cache-on-failure: true
- name: Cache pgrx PostgreSQL install
id: cache-pgrx-install
uses: actions/cache@v5
with:
path: |
~/.pgrx/${{ env.PG_EXTENSION_MAJOR }}.*
~/.pgrx/config.toml
key: ${{ runner.os }}-pgrx-install-${{ env.PGRX_VERSION }}-pg${{ env.PG_EXTENSION_MAJOR }}
- name: Install cargo-nextest and cargo-pgrx
shell: bash
run: |
set -euo pipefail
curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin
if ! command -v cargo-pgrx >/dev/null 2>&1 || ! cargo pgrx --version | grep -q "cargo-pgrx ${PGRX_VERSION}$"; then
cargo install cargo-pgrx --version "${PGRX_VERSION}" --locked
fi
cargo nextest --version
cargo pgrx --version
- name: Initialize pgrx PostgreSQL
if: steps.cache-pgrx-install.outputs.cache-hit != 'true'
shell: bash
run: |
set -euo pipefail
cargo pgrx init "--pg${PG_EXTENSION_MAJOR}" download
- name: Prepare server binary
shell: bash
run: |
set -euo pipefail
find dist -name "*.tar.gz" -type f -exec tar -xzf {} -C dist/ \;
SERVER_BIN=$(find dist -name "kalamdb-server-*-linux-x86_64" -type f ! -name "*.tar.gz" | head -1)
[[ -z "$SERVER_BIN" ]] && { echo "Server binary not found"; exit 1; }
chmod +x "$SERVER_BIN"
cp "$SERVER_BIN" ./kalamdb-server
ls -la ./kalamdb-server
- name: Create server config
shell: bash
run: |
set -euo pipefail
cp backend/server.example.toml server.toml
sed -i 's|rocksdb_path = "./data/rocksdb"|rocksdb_path = "./test-data/rocksdb"|g' server.toml
sed -i 's|default_storage_path = "./data/storage"|default_storage_path = "./test-data/storage"|g' server.toml
sed -i 's|logs_path = "./logs"|logs_path = "./test-data/logs"|g' server.toml
sed -i 's|jwt_secret = ".*"|jwt_secret = "pg-native-test-secret-key-minimum-32-characters-long"|g' server.toml
- name: Start server
shell: bash
env:
KALAMDB_SERVER_HOST: "0.0.0.0"
KALAMDB_ROOT_PASSWORD: "kalamdb123"
KALAMDB_JWT_SECRET: "pg-native-test-secret-key-minimum-32-characters-long"
KALAMDB_NODE_ID: "1"
KALAMDB_CLUSTER_RPC_ADDR: "0.0.0.0:9188"
KALAMDB_CLUSTER_API_ADDR: "http://127.0.0.1:8080"
run: |
set -euo pipefail
mkdir -p test-data/rocksdb test-data/storage test-data/logs
./kalamdb-server > pg-server.log 2>&1 &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> "$GITHUB_ENV"
for i in {1..60}; do
if curl -sf http://127.0.0.1:8080/health >/dev/null 2>&1; then
echo "✅ Server is ready (${i}s)"
exit 0
fi
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "❌ Server process died"
cat pg-server.log || true
exit 1
fi
echo " Waiting... ($i/60)"
sleep 1
done
echo "❌ Timed out waiting for server"
cat pg-server.log || true
exit 1
- name: Setup pgrx PostgreSQL and install extension
shell: bash
env:
PG_MAJOR: ${{ env.PG_EXTENSION_MAJOR }}
PG_EXTENSION_FLAVOR: ${{ env.PG_EXTENSION_FLAVOR }}
run: |
set -euo pipefail
./pg/scripts/pgrx-test-setup.sh 2>&1 | tee pg-pgrx-setup-output.txt
- name: Run native PG extension perf tests first
shell: bash
env:
KALAMDB_SERVER_URL: "http://127.0.0.1:8080"
KALAMDB_ROOT_PASSWORD: "kalamdb123"
run: |
set -euo pipefail
: > pg-native-test-output.txt
cargo nextest run \
-p kalam-pg-extension \
--features e2e \
--test e2e_perf \
--test-threads 1 \
2>&1 | tee pg-native-test-output.txt
- name: Run remaining native PG extension e2e tests
shell: bash
env:
KALAMDB_SERVER_URL: "http://127.0.0.1:8080"
KALAMDB_ROOT_PASSWORD: "kalamdb123"
run: |
set -euo pipefail
cargo nextest run \
-p kalam-pg-extension \
--features e2e \
--test e2e_ddl \
--test e2e_dml \
--test e2e_scenarios \
--test extension_metadata \
--test session_settings \
--test-threads 1 \
2>&1 | tee -a pg-native-test-output.txt
- name: Stop pgrx PostgreSQL
if: always()
shell: bash
env:
PG_MAJOR: ${{ env.PG_EXTENSION_MAJOR }}
PG_EXTENSION_FLAVOR: ${{ env.PG_EXTENSION_FLAVOR }}
run: |
./pg/scripts/pgrx-test-setup.sh --stop || true
- name: Stop server
if: always()
shell: bash
run: |
[[ -n "${SERVER_PID:-}" ]] && kill "$SERVER_PID" 2>/dev/null || true
- name: Collect PG native test logs
if: always()
shell: bash
run: |
set -euo pipefail
mkdir -p pg-native-test-results
cp pg-server.log pg-native-test-results/ 2>/dev/null || true
cp pg-pgrx-setup-output.txt pg-native-test-results/ 2>/dev/null || true
cp pg-native-test-output.txt pg-native-test-results/ 2>/dev/null || true
cp "$HOME/.pgrx/data-${PG_EXTENSION_MAJOR}/pgrx.log" pg-native-test-results/pgrx.log 2>/dev/null || true
- name: Upload PG native test results
if: always()
uses: actions/upload-artifact@v6
with:
name: pg-native-test-results
path: pg-native-test-results/*
if-no-files-found: ignore
release:
name: Publish GitHub Release
runs-on: ubuntu-latest
needs:
- license_compliance
- build_cli_linux_x86_64
- build_cli_linux_aarch64
- build_cli_windows_x86_64
- build_cli_macos_arm
- build_linux_x86_64
- build_linux_aarch64
- build_windows_x86_64
- build_macos_arm
- build_pg_extension_x86_64
- read_version
- smoke_tests
- pg_extension_tests_native
- docker
- docker_pg
if: ${{ github.event_name == 'push' || github.event.inputs.github_release == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Update README release metadata
shell: bash
run: |
set -euo pipefail
TAG="${{ needs.read_version.outputs.tag }}"
git fetch origin main
git checkout main
sed -i "s/^\*\*Latest release:\*\* .*/**Latest release:** ${TAG}/" README.md
if git diff --quiet -- README.md; then
echo "README already up to date"
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add README.md
git commit -m "docs: update release metadata"
git push origin main
- name: Download all artifacts
uses: actions/download-artifact@v6
with:
path: release-assets
pattern: 'dist-*'
- name: Generate SHA256SUMS
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p "dist/$VERSION"
# Flatten artifacts into dist/<version>/
find release-assets -type f -maxdepth 4 -print0 | while IFS= read -r -d '' f; do
cp "$f" "dist/$VERSION/"
done
cd "dist/$VERSION"
if ! ls -1 ./*.tar.gz ./*.zip >/dev/null 2>&1; then
echo "No release artifacts found. Check workflow inputs.platforms / all_platforms." >&2
exit 1
fi
PG_EXTENSION_ASSET="pg_kalam-${VERSION}-${PG_EXTENSION_FLAVOR}-linux-x86_64.tar.gz"
if [[ ! -f "$PG_EXTENSION_ASSET" ]]; then
echo "Missing PostgreSQL extension release asset: $PG_EXTENSION_ASSET" >&2
exit 1
fi
sha256sum ./*.tar.gz ./*.zip > SHA256SUMS
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.read_version.outputs.tag }}
prerelease: ${{ needs.read_version.outputs.pre_release == 'true' }}
generate_release_notes: true
files: |
dist/${{ needs.read_version.outputs.version }}/*.tar.gz
dist/${{ needs.read_version.outputs.version }}/*.zip
dist/${{ needs.read_version.outputs.version }}/SHA256SUMS
dist/${{ needs.read_version.outputs.version }}/LICENSE.txt
dist/${{ needs.read_version.outputs.version }}/NOTICE
dist/${{ needs.read_version.outputs.version }}/THIRD_PARTY_LICENSES.md
docker:
name: Push Docker image (Docker Hub)
runs-on: ubuntu-latest
needs:
- build_cli_linux_x86_64
- build_cli_linux_aarch64
- build_linux_x86_64
- build_linux_aarch64
- read_version
if: ${{ github.event_name == 'push' || github.event.inputs.docker_push == 'true' }}
env:
DOCKER_REPO_INPUT: ${{ github.event.inputs.docker_repo }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set Docker repo
id: vars
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
DOCKER_REPO="${DOCKER_REPO_INPUT:-}"
DOCKER_IMAGE_SOURCE="https://github.com/${{ github.repository }}"
DOCKER_IMAGE_CREATED="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
if [[ -z "$DOCKER_REPO" ]]; then
DOCKER_REPO="jamals86/kalamdb"
fi
SHORT_SHA="${GITHUB_SHA:-}"
if [[ -n "$SHORT_SHA" ]]; then
SHORT_SHA="${SHORT_SHA:0:7}"
else
SHORT_SHA="$(git rev-parse --short=7 HEAD)"
fi
DOCKER_VERSION_COMMIT_TAG="${VERSION}-h${SHORT_SHA}"
DOCKER_VALIDATE_TAG_AMD64="${VERSION}-ci-${SHORT_SHA}-amd64"
DOCKER_VALIDATE_TAG_ARM64="${VERSION}-ci-${SHORT_SHA}-arm64"
echo "docker_repo=$DOCKER_REPO" >> "$GITHUB_OUTPUT"
echo "docker_version_commit_tag=$DOCKER_VERSION_COMMIT_TAG" >> "$GITHUB_OUTPUT"
echo "docker_validate_tag_amd64=$DOCKER_VALIDATE_TAG_AMD64" >> "$GITHUB_OUTPUT"
echo "docker_validate_tag_arm64=$DOCKER_VALIDATE_TAG_ARM64" >> "$GITHUB_OUTPUT"
echo "docker_image_source=$DOCKER_IMAGE_SOURCE" >> "$GITHUB_OUTPUT"
echo "docker_image_created=$DOCKER_IMAGE_CREATED" >> "$GITHUB_OUTPUT"
- name: Download pre-built artifacts (x86_64)
uses: actions/download-artifact@v6
with:
name: dist-linux-x86_64
path: artifacts/server-amd64
- name: Download pre-built CLI artifact (x86_64)
uses: actions/download-artifact@v6
with:
name: dist-cli-linux-x86_64
path: artifacts/cli-amd64
- name: Download pre-built artifacts (aarch64)
uses: actions/download-artifact@v6
with:
name: dist-linux-aarch64
path: artifacts/server-arm64
- name: Download pre-built CLI artifact (aarch64)
uses: actions/download-artifact@v6
with:
name: dist-cli-linux-aarch64
path: artifacts/cli-arm64
- name: Extract and prepare binaries (amd64)
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p binaries-amd64
# Extract server binary
tar -xzf "artifacts/server-amd64/kalamdb-server-${VERSION}-linux-x86_64.tar.gz" -C binaries-amd64
mv "binaries-amd64/kalamdb-server-${VERSION}-linux-x86_64" binaries-amd64/kalamdb-server
# Extract CLI binary
tar -xzf "artifacts/cli-amd64/kalamcli-${VERSION}-linux-x86_64.tar.gz" -C binaries-amd64
mv "binaries-amd64/kalamcli-${VERSION}-linux-x86_64" binaries-amd64/kalam
chmod +x binaries-amd64/kalamdb-server binaries-amd64/kalam
ls -la binaries-amd64/
- name: Extract and prepare binaries (arm64)
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
mkdir -p binaries-arm64
# Extract server binary
tar -xzf "artifacts/server-arm64/kalamdb-server-${VERSION}-linux-aarch64.tar.gz" -C binaries-arm64
mv "binaries-arm64/kalamdb-server-${VERSION}-linux-aarch64" binaries-arm64/kalamdb-server
# Extract CLI binary
tar -xzf "artifacts/cli-arm64/kalamcli-${VERSION}-linux-aarch64.tar.gz" -C binaries-arm64
mv "binaries-arm64/kalamcli-${VERSION}-linux-aarch64" binaries-arm64/kalam
chmod +x binaries-arm64/kalamdb-server binaries-arm64/kalam
ls -la binaries-arm64/
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker image candidate (linux/amd64)
uses: docker/build-push-action@v6
with:
context: .
file: docker/build/Dockerfile.prebuilt
push: true
platforms: linux/amd64
build-contexts: |
binaries=binaries-amd64
build-args: |
OCI_IMAGE_VERSION=${{ needs.read_version.outputs.version }}
OCI_IMAGE_REVISION=${{ github.sha }}
OCI_IMAGE_CREATED=${{ steps.vars.outputs.docker_image_created }}
OCI_IMAGE_SOURCE=${{ steps.vars.outputs.docker_image_source }}
OCI_IMAGE_URL=https://kalamdb.org
OCI_IMAGE_DOCUMENTATION=https://kalamdb.org/docs
OCI_IMAGE_AUTHORS=Jamal Saad
OCI_IMAGE_VENDOR=KalamDB
OCI_IMAGE_LICENSES=Apache-2.0
tags: |
${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_amd64 }}
- name: Build and push Docker image candidate (linux/arm64)
uses: docker/build-push-action@v6
with:
context: .
file: docker/build/Dockerfile.prebuilt
push: true
platforms: linux/arm64
build-contexts: |
binaries=binaries-arm64
build-args: |
OCI_IMAGE_VERSION=${{ needs.read_version.outputs.version }}
OCI_IMAGE_REVISION=${{ github.sha }}
OCI_IMAGE_CREATED=${{ steps.vars.outputs.docker_image_created }}
OCI_IMAGE_SOURCE=${{ steps.vars.outputs.docker_image_source }}
OCI_IMAGE_URL=https://kalamdb.org
OCI_IMAGE_DOCUMENTATION=https://kalamdb.org/docs
OCI_IMAGE_AUTHORS=Jamal Saad
OCI_IMAGE_VENDOR=KalamDB
OCI_IMAGE_LICENSES=Apache-2.0
tags: |
${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_arm64 }}
- name: Validate Docker image candidate (linux/amd64)
shell: bash
run: |
set -euo pipefail
export KALAMDB_JWT_SECRET="$(openssl rand -base64 32)"
export TEST_PORT=18081
export TIMEOUT=45
export MAX_IMAGE_SIZE_BYTES=300000000
export EXPECTED_OCI_VERSION="${{ needs.read_version.outputs.version }}"
export EXPECTED_OCI_SOURCE="${{ steps.vars.outputs.docker_image_source }}"
IMAGE="${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_amd64 }}"
docker pull "$IMAGE"
chmod +x docker/build/test-docker-image.sh
./docker/build/test-docker-image.sh "$IMAGE" 2>&1 | tee docker-image-amd64-verification.txt
- name: Validate Docker image candidate (linux/arm64)
shell: bash
run: |
set -euo pipefail
export KALAMDB_JWT_SECRET="$(openssl rand -base64 32)"
export DOCKER_PLATFORM="linux/arm64"
export TEST_PORT=18082
export TIMEOUT=75
export MAX_IMAGE_SIZE_BYTES=300000000
export EXPECTED_OCI_VERSION="${{ needs.read_version.outputs.version }}"
export EXPECTED_OCI_SOURCE="${{ steps.vars.outputs.docker_image_source }}"
IMAGE="${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_arm64 }}"
docker pull --platform linux/arm64 "$IMAGE"
chmod +x docker/build/test-docker-image.sh
./docker/build/test-docker-image.sh "$IMAGE" 2>&1 | tee docker-image-arm64-verification.txt
- name: Inspect validated Docker image candidates
if: always()
shell: bash
run: |
set -euo pipefail
{
echo "Docker image candidate inspection"
echo "================================"
for image in \
"${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_amd64 }}" \
"${{ steps.vars.outputs.docker_repo }}:${{ steps.vars.outputs.docker_validate_tag_arm64 }}"; do
echo
echo "IMAGE: $image"
if docker image inspect "$image" >/dev/null 2>&1; then
docker image inspect "$image" --format 'user={{.Config.User}} size={{.Size}} bytes created={{.Created}}'
docker image inspect "$image" --format 'version={{ index .Config.Labels "org.opencontainers.image.version" }} source={{ index .Config.Labels "org.opencontainers.image.source" }} revision={{ index .Config.Labels "org.opencontainers.image.revision" }}'
else
echo "image not present locally"
fi
done
} > docker-image-inspection.txt
- name: Upload Docker verification artifacts
if: always()
uses: actions/upload-artifact@v6
with:
name: docker-image-verification
path: |
docker-image-amd64-verification.txt
docker-image-arm64-verification.txt
docker-image-inspection.txt
if-no-files-found: ignore
- name: Promote validated Docker image (linux/amd64)
shell: bash
run: |
set -euo pipefail
REPO="${{ steps.vars.outputs.docker_repo }}"
VERSION="${{ needs.read_version.outputs.version }}"
CANDIDATE_TAG="${{ steps.vars.outputs.docker_validate_tag_amd64 }}"
docker buildx imagetools create -t "${REPO}:${VERSION}-amd64" "${REPO}:${CANDIDATE_TAG}"
- name: Promote validated Docker image (linux/arm64)
shell: bash
run: |
set -euo pipefail
REPO="${{ steps.vars.outputs.docker_repo }}"
VERSION="${{ needs.read_version.outputs.version }}"
CANDIDATE_TAG="${{ steps.vars.outputs.docker_validate_tag_arm64 }}"
docker buildx imagetools create -t "${REPO}:${VERSION}-arm64" "${REPO}:${CANDIDATE_TAG}"
- name: Create and push multi-arch manifest
shell: bash
run: |
set -euo pipefail
REPO="${{ steps.vars.outputs.docker_repo }}"
VERSION="${{ needs.read_version.outputs.version }}"
VERSION_COMMIT_TAG="${{ steps.vars.outputs.docker_version_commit_tag }}"
# Create manifest for versioned tag
docker buildx imagetools create -t "${REPO}:${VERSION}" \
"${REPO}:${VERSION}-amd64" \
"${REPO}:${VERSION}-arm64"
# Create manifest for version + commit tag
docker buildx imagetools create -t "${REPO}:${VERSION_COMMIT_TAG}" \
"${REPO}:${VERSION}-amd64" \
"${REPO}:${VERSION}-arm64"
# Create manifest for latest tag
docker buildx imagetools create -t "${REPO}:latest" \
"${REPO}:${VERSION}-amd64" \
"${REPO}:${VERSION}-arm64"
echo "✅ Multi-arch manifest created for ${REPO}:${VERSION}, ${REPO}:${VERSION_COMMIT_TAG}, and ${REPO}:latest"
- name: Update Docker Hub repository README
id: dockerhub_readme
continue-on-error: true
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: ${{ steps.vars.outputs.docker_repo }}
readme-filepath: ./docker/REPO-README.md
- name: Warn if Docker Hub README update failed
if: steps.dockerhub_readme.outcome == 'failure'
shell: bash
run: |
echo "::warning::Docker image publish succeeded, but Docker Hub README update failed. Ensure DOCKERHUB_USERNAME is a repo admin and DOCKERHUB_TOKEN has read/write/delete scope."
# ═══════════════════════════════════════════════════════════════════════════
# DOCKER PG - Push PostgreSQL + pg_kalam extension image to Docker Hub
# ═══════════════════════════════════════════════════════════════════════════
docker_pg:
name: Push PG Extension Docker image
runs-on: ubuntu-latest
needs:
- build_pg_extension_x86_64
- docker
- smoke_tests
- pg_extension_tests_native
- read_version
if: ${{ github.event_name == 'push' || github.event.inputs.docker_push == 'true' }}
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set PG candidate tag
id: pg_vars
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
SHORT_SHA="${GITHUB_SHA:-}"
if [[ -n "$SHORT_SHA" ]]; then
SHORT_SHA="${SHORT_SHA:0:7}"
else
SHORT_SHA="$(git rev-parse --short=7 HEAD)"
fi
PG_REPO="jamals86/pg-kalam"
PG_CANDIDATE_TAG="${VERSION}-${PG_EXTENSION_FLAVOR}-ci-${SHORT_SHA}"
echo "pg_repo=$PG_REPO" >> "$GITHUB_OUTPUT"
echo "pg_candidate_tag=$PG_CANDIDATE_TAG" >> "$GITHUB_OUTPUT"
- name: Read PG image description
id: pg_image_description
shell: bash
run: |
set -euo pipefail
DESCRIPTION="$(tr '\n' ' ' < pg/docker/image-description.txt | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
echo "value=$DESCRIPTION" >> "$GITHUB_OUTPUT"
- name: Download PG extension artifact (x86_64)
uses: actions/download-artifact@v6
with:
name: dist-pg-extension-linux-x86_64
path: pg-artifacts-amd64/
- name: Prepare extension files
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
# Extract x86_64 tarball
mkdir -p pg-ext-amd64
tar -xzf "pg-artifacts-amd64/pg_kalam-${VERSION}-${PG_EXTENSION_FLAVOR}-linux-x86_64.tar.gz" -C pg-ext-amd64 --strip-components=1
echo "=== x86_64 ==="
ls -lh pg-ext-amd64/
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push PG image candidate (amd64)
uses: docker/build-push-action@v6
with:
context: pg-ext-amd64
file: pg/docker/Dockerfile.release-pg
push: true
platforms: linux/amd64
build-args: |
PG_MAJOR=${{ env.PG_EXTENSION_MAJOR }}
POSTGRES_BASE_IMAGE=public.ecr.aws/docker/library/postgres:${{ env.PG_EXTENSION_MAJOR }}-bookworm
OCI_IMAGE_DESCRIPTION=${{ steps.pg_image_description.outputs.value }}
labels: |
org.opencontainers.image.title=pg-kalam
org.opencontainers.image.description=${{ steps.pg_image_description.outputs.value }}
tags: |
${{ steps.pg_vars.outputs.pg_repo }}:${{ steps.pg_vars.outputs.pg_candidate_tag }}
- name: Install PostgreSQL client tools
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends postgresql-client
- name: Run PG Docker compose smoke suite
id: pg_docker_tests
continue-on-error: true
shell: bash
run: |
set -euo pipefail
VERSION="${{ needs.read_version.outputs.version }}"
SERVER_REPO="${{ github.event.inputs.docker_repo }}"
if [[ -z "$SERVER_REPO" ]]; then
SERVER_REPO="jamals86/kalamdb"
fi
SERVER_IMAGE="${SERVER_REPO}:${VERSION}"
PG_IMAGE="${{ steps.pg_vars.outputs.pg_repo }}:${{ steps.pg_vars.outputs.pg_candidate_tag }}"
COMPOSE_FILE="pg/docker/docker-compose.test.yml"
COMPOSE_PROJECT="pg-kalam-release"
export KALAMDB_IMAGE="$SERVER_IMAGE"
export KALAMDB_PG_IMAGE="$PG_IMAGE"
export KALAMDB_TEST_HTTP_PORT=18088
export KALAMDB_TEST_RPC_PORT=19188
export KALAMDB_TEST_PG_PORT=15433
export RUST_LOG=info
docker pull "$SERVER_IMAGE"
docker pull "$PG_IMAGE"
docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT" down -v --remove-orphans 2>/dev/null || true
docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT" up -d kalamdb
KALAMDB_CONTAINER_ID="$(docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT" ps -q kalamdb)"
if [[ -z "$KALAMDB_CONTAINER_ID" ]]; then
echo "Failed to resolve kalamdb container id" >&2
docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT" ps || true
echo "pg_docker_tests_passed=false" >> "$GITHUB_OUTPUT"
exit 1
fi
KALAMDB_READY=false
for i in {1..90}; do
if docker exec "$KALAMDB_CONTAINER_ID" /usr/local/bin/busybox wget -q -O /dev/null http://127.0.0.1:8080/health >/dev/null 2>&1; then
KALAMDB_READY=true
break
fi
if [[ "$(docker inspect --format '{{.State.Status}}' "$KALAMDB_CONTAINER_ID")" != "running" ]]; then
echo "KalamDB container stopped before becoming ready" >&2
docker logs "$KALAMDB_CONTAINER_ID" || true
break
fi
sleep 1
done
if [[ "$KALAMDB_READY" != "true" ]]; then
echo "KalamDB container did not become ready in time" >&2
docker logs "$KALAMDB_CONTAINER_ID" || true
echo "pg_docker_tests_passed=false" >> "$GITHUB_OUTPUT"
exit 1
fi
docker compose -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT" up -d postgres
if PGHOST=127.0.0.1 \
PGPORT="$KALAMDB_TEST_PG_PORT" \
PGUSER=kalamdb \
PGPASSWORD=kalamdb123 \
PGDATABASE=kalamdb \
KALAMDB_API_URL="http://127.0.0.1:${KALAMDB_TEST_HTTP_PORT}" \
KALAMDB_PASSWORD=kalamdb123 \
./pg/docker/test.sh 2>&1 | tee pg-docker-test-output.txt; then
echo "pg_docker_tests_passed=true" >> "$GITHUB_OUTPUT"
echo "✅ PG Docker image verified with compose-based smoke suite: ${PG_IMAGE}"
else
echo "pg_docker_tests_passed=false" >> "$GITHUB_OUTPUT"
echo "⚠️ PG Docker compose smoke suite failed"
fi
- name: Inspect validated PG Docker image candidate
if: always()
shell: bash
run: |
set -euo pipefail
IMAGE="${{ steps.pg_vars.outputs.pg_repo }}:${{ steps.pg_vars.outputs.pg_candidate_tag }}"
if docker image inspect "$IMAGE" >/dev/null 2>&1; then
docker image inspect "$IMAGE" --format 'user={{.Config.User}} size={{.Size}} bytes created={{.Created}}' > pg-docker-image-inspection.txt
docker image inspect "$IMAGE" --format 'version={{ index .Config.Labels "org.opencontainers.image.version" }} description={{ index .Config.Labels "org.opencontainers.image.description" }}' >> pg-docker-image-inspection.txt
else
echo "image not present locally" > pg-docker-image-inspection.txt
fi
- name: Capture PG Docker logs
if: always()
shell: bash
run: |
set -euo pipefail
docker compose -f pg/docker/docker-compose.test.yml -p pg-kalam-release logs --no-color > pg-docker-compose.log 2>&1 || true
- name: Upload PG Docker test artifacts
if: always()
uses: actions/upload-artifact@v6
with:
name: pg-docker-test-results
path: |
pg-docker-test-output.txt
pg-docker-compose.log
pg-docker-image-inspection.txt
if-no-files-found: ignore
- name: Stop PG Docker compose stack
if: always()
shell: bash
run: |
docker compose -f pg/docker/docker-compose.test.yml -p pg-kalam-release down -v --remove-orphans 2>/dev/null || true
- name: Report PG Docker test status
if: always()
shell: bash
run: |
if [[ "${{ steps.pg_docker_tests.outputs.pg_docker_tests_passed }}" == "true" ]]; then
echo "::notice::✅ PG Docker compose smoke suite passed"
else
echo "::error::PG Docker compose smoke suite failed - review pg-docker-test-results artifact for details"
fi
- name: Fail if PG Docker tests failed
if: always() && steps.pg_docker_tests.outputs.pg_docker_tests_passed != 'true'
shell: bash
run: |
echo "PG Docker compose smoke suite failed" >&2
exit 1
- name: Promote validated PG Docker image
shell: bash
run: |
set -euo pipefail
REPO="${{ steps.pg_vars.outputs.pg_repo }}"
VERSION="${{ needs.read_version.outputs.version }}"
CANDIDATE_TAG="${{ steps.pg_vars.outputs.pg_candidate_tag }}"
docker buildx imagetools create -t "${REPO}:${VERSION}-${PG_EXTENSION_FLAVOR}" "${REPO}:${CANDIDATE_TAG}"
docker buildx imagetools create -t "${REPO}:latest-${PG_EXTENSION_FLAVOR}" "${REPO}:${CANDIDATE_TAG}"