Skip to content

Build and Release

Build and Release #16

Workflow file for this run

# Copyright 2025 The Drasi Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Build and Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Version Tag (optional, defaults to version from Cargo.toml)'
required: false
image_prefix:
description: 'Image Prefix (defaults to ghcr.io/<repo-owner>)'
required: false
default: ''
dry_run:
description: 'Dry run — build everything but skip publish/release steps'
required: false
type: boolean
default: false
schedule:
- cron: '0 8 * * 1' # Weekly Monday 08:00 UTC dry-run
permissions:
id-token: write
contents: read
packages: write
env:
IMAGE_NAME: drasi-server
# Resolved image prefix: use the user-provided value if set, otherwise default
# to ghcr.io/<repo-owner> so forks publish to their own namespace by default.
IMAGE_PREFIX: ${{ inputs.image_prefix != '' && inputs.image_prefix || format('ghcr.io/{0}', github.repository_owner) }}
jobs:
# Determine the version tag to use
determine-version:
name: Determine Version Tag
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Determine version
id: version
run: |
if [ -n "${{ inputs.tag }}" ]; then
echo "Using provided tag: ${{ inputs.tag }}"
echo "tag=${{ inputs.tag }}" >> $GITHUB_OUTPUT
else
# Extract version from Cargo.toml
VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
TAG="v${VERSION}"
echo "Using version from Cargo.toml: ${TAG}"
echo "tag=${TAG}" >> $GITHUB_OUTPUT
fi
# Build all Linux binaries on a single runner using cross (the whole point of cross)
build-linux:
name: Build Linux binaries
runs-on: ubuntu-latest
needs: determine-version
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
# Install cross from a pinned git commit: the last tagged release (0.2.5,
# March 2022) ships container images based on glibc 2.27, which is too old
# for current rustc's build-script binaries (require GLIBC_2.34). Main has
# newer base images, but we pin to a specific commit for reproducibility.
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross --rev 65fe72b0cdb1e7e0cc0652517498d4389cc8f5cf --locked
- name: Build all Linux targets
run: |
TARGETS=(
"x86_64-unknown-linux-gnu:x86_64-linux-gnu"
"aarch64-unknown-linux-gnu:aarch64-linux-gnu"
"x86_64-unknown-linux-musl:x86_64-linux-musl"
"aarch64-unknown-linux-musl:aarch64-linux-musl"
)
for entry in "${TARGETS[@]}"; do
TARGET="${entry%%:*}"
SUFFIX="${entry##*:}"
ARTIFACT_NAME="drasi-server-${SUFFIX}"
echo "========================================="
echo "Building ${TARGET} -> ${ARTIFACT_NAME}"
echo "========================================="
rustup target add "${TARGET}"
cross build --release --target "${TARGET}" --bin drasi-server
SRC="target/${TARGET}/release/drasi-server"
if [[ -f "$SRC" ]]; then
cp "$SRC" "$ARTIFACT_NAME"
chmod +x "$ARTIFACT_NAME"
echo "✓ ${ARTIFACT_NAME}"
else
echo "Error: Built binary not found at $SRC"
ls -la "target/${TARGET}/release/" || true
exit 1
fi
done
echo "All Linux binaries built:"
ls -lh drasi-server-*
- name: Upload x86_64-linux-gnu
uses: actions/upload-artifact@v5
with:
name: drasi-server-x86_64-linux-gnu
path: drasi-server-x86_64-linux-gnu
if-no-files-found: error
- name: Upload aarch64-linux-gnu
uses: actions/upload-artifact@v5
with:
name: drasi-server-aarch64-linux-gnu
path: drasi-server-aarch64-linux-gnu
if-no-files-found: error
- name: Upload x86_64-linux-musl
uses: actions/upload-artifact@v5
with:
name: drasi-server-x86_64-linux-musl
path: drasi-server-x86_64-linux-musl
if-no-files-found: error
- name: Upload aarch64-linux-musl
uses: actions/upload-artifact@v5
with:
name: drasi-server-aarch64-linux-musl
path: drasi-server-aarch64-linux-musl
if-no-files-found: error
# macOS requires native builds (can't cross-compile to macOS)
build-macos:
name: Build macOS (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
needs: determine-version
strategy:
matrix:
include:
- target: aarch64-apple-darwin
artifact_suffix: aarch64-apple-darwin
arch: ARM64
runner: macos-latest
- target: x86_64-apple-darwin
artifact_suffix: x86_64-apple-darwin
arch: x86_64
runner: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Add Rust target
run: rustup target add ${{ matrix.target }}
- name: Install build dependencies
run: brew install protobuf pkg-config jq oniguruma autoconf automake libtool
- name: Build x86_64 static libs (for cross-compile)
if: matrix.target == 'x86_64-apple-darwin'
run: |
set -euo pipefail
X86_PREFIX="${{ github.workspace }}/x86_64-deps"
mkdir -p "$X86_PREFIX"
# Build oniguruma for x86_64
git clone --depth 1 https://github.com/kkos/oniguruma.git /tmp/oniguruma
cd /tmp/oniguruma
autoreconf -vfi
./configure --host=x86_64-apple-darwin --prefix="$X86_PREFIX" \
CC="clang -arch x86_64" CFLAGS="-arch x86_64" \
--enable-static --disable-shared
make -j$(sysctl -n hw.ncpu) && make install
# Build jq for x86_64 (with oniguruma)
git clone --depth 1 https://github.com/jqlang/jq.git /tmp/jq
cd /tmp/jq
git submodule update --init
autoreconf -fi
./configure --host=x86_64-apple-darwin --prefix="$X86_PREFIX" \
CC="clang -arch x86_64" CFLAGS="-arch x86_64" \
--with-oniguruma="$X86_PREFIX" \
--enable-static --disable-shared --disable-maintainer-mode
make -j$(sysctl -n hw.ncpu) && make install
echo "x86_64 static libs installed to $X86_PREFIX:"
ls -la "$X86_PREFIX/lib/"
- name: Build binary
run: |
export LIBJQ_STATIC=1
export LIBONIG_STATIC=1
if [[ "${{ matrix.target }}" == "x86_64-apple-darwin" ]]; then
X86_PREFIX="${{ github.workspace }}/x86_64-deps"
export JQ_LIB_DIR="$X86_PREFIX/lib"
export ONIG_LIB_DIR="$X86_PREFIX/lib"
export PKG_CONFIG_PATH="$X86_PREFIX/lib/pkgconfig"
export CFLAGS_x86_64_apple_darwin="-arch x86_64"
else
export JQ_LIB_DIR="$(brew --prefix jq)/lib"
export ONIG_LIB_DIR="$(brew --prefix oniguruma)/lib"
fi
cargo build --release --target ${{ matrix.target }} --bin drasi-server
SRC_BINARY="target/${{ matrix.target }}/release/drasi-server"
ARTIFACT_NAME="drasi-server-${{ matrix.artifact_suffix }}"
if [[ -f "$SRC_BINARY" ]]; then
cp "$SRC_BINARY" "$ARTIFACT_NAME"
chmod +x "$ARTIFACT_NAME"
else
echo "Error: Built binary not found at $SRC_BINARY"
exit 1
fi
- name: Upload binary artifact
uses: actions/upload-artifact@v5
with:
name: drasi-server-${{ matrix.artifact_suffix }}
path: drasi-server-${{ matrix.artifact_suffix }}
if-no-files-found: error
# Windows MSVC native build (uses vendored static libs for jq and OpenSSL)
build-windows-msvc:
name: Build Windows MSVC (x86_64)
runs-on: windows-latest
needs: determine-version
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Pull vendored native libraries
shell: bash
run: cargo xtask vendor pull x86_64-pc-windows-msvc --tag v1
- name: Build binary
shell: bash
run: |
cargo build --release --target x86_64-pc-windows-msvc --bin drasi-server
SRC_BINARY="target/x86_64-pc-windows-msvc/release/drasi-server.exe"
ARTIFACT_NAME="drasi-server-x86_64-windows-msvc.exe"
echo "=== Build output ==="
ls -lh "$SRC_BINARY" || true
file "$SRC_BINARY" || true
if [[ -f "$SRC_BINARY" ]]; then
cp "$SRC_BINARY" "$ARTIFACT_NAME"
echo "✓ Built $ARTIFACT_NAME ($(du -h "$ARTIFACT_NAME" | cut -f1))"
else
echo "Error: Built binary not found at $SRC_BINARY"
echo "=== release directory listing ==="
ls -la "target/x86_64-pc-windows-msvc/release/" || true
exit 1
fi
- name: Upload binary artifact
uses: actions/upload-artifact@v5
with:
name: drasi-server-x86_64-windows-msvc
path: drasi-server-x86_64-windows-msvc.exe
if-no-files-found: error
# ─── SSE CLI builds (pure Rust, no C dependencies) ───
# Build all Linux SSE CLI binaries using cross
build-sse-cli-linux:
name: Build SSE CLI Linux binaries
runs-on: ubuntu-latest
needs: determine-version
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
# See note in build-linux about why we install cross from a pinned commit.
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross --rev 65fe72b0cdb1e7e0cc0652517498d4389cc8f5cf --locked
- name: Build all Linux targets
run: |
TARGETS=(
"x86_64-unknown-linux-gnu:x86_64-linux-gnu"
"aarch64-unknown-linux-gnu:aarch64-linux-gnu"
"x86_64-unknown-linux-musl:x86_64-linux-musl"
"aarch64-unknown-linux-musl:aarch64-linux-musl"
)
for entry in "${TARGETS[@]}"; do
TARGET="${entry%%:*}"
SUFFIX="${entry##*:}"
ARTIFACT_NAME="drasi-sse-cli-${SUFFIX}"
echo "========================================="
echo "Building SSE CLI ${TARGET} -> ${ARTIFACT_NAME}"
echo "========================================="
rustup target add "${TARGET}"
cross build --release --target "${TARGET}" --manifest-path examples/sse-cli/Cargo.toml
SRC="examples/sse-cli/target/${TARGET}/release/drasi-sse-cli"
if [[ -f "$SRC" ]]; then
cp "$SRC" "$ARTIFACT_NAME"
chmod +x "$ARTIFACT_NAME"
echo "✓ ${ARTIFACT_NAME}"
else
echo "Error: Built binary not found at $SRC"
ls -la "target/${TARGET}/release/" || true
exit 1
fi
done
echo "All SSE CLI Linux binaries built:"
ls -lh drasi-sse-cli-*
- name: Upload x86_64-linux-gnu
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-x86_64-linux-gnu
path: drasi-sse-cli-x86_64-linux-gnu
if-no-files-found: error
- name: Upload aarch64-linux-gnu
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-aarch64-linux-gnu
path: drasi-sse-cli-aarch64-linux-gnu
if-no-files-found: error
- name: Upload x86_64-linux-musl
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-x86_64-linux-musl
path: drasi-sse-cli-x86_64-linux-musl
if-no-files-found: error
- name: Upload aarch64-linux-musl
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-aarch64-linux-musl
path: drasi-sse-cli-aarch64-linux-musl
if-no-files-found: error
# macOS SSE CLI builds (native)
build-sse-cli-macos:
name: Build SSE CLI macOS (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
needs: determine-version
strategy:
matrix:
include:
- target: aarch64-apple-darwin
artifact_suffix: aarch64-apple-darwin
arch: ARM64
runner: macos-latest
- target: x86_64-apple-darwin
artifact_suffix: x86_64-apple-darwin
arch: x86_64
runner: macos-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- name: Add Rust target
run: rustup target add ${{ matrix.target }}
- name: Build binary
run: |
cargo build --release --target ${{ matrix.target }} --manifest-path examples/sse-cli/Cargo.toml
SRC_BINARY="examples/sse-cli/target/${{ matrix.target }}/release/drasi-sse-cli"
ARTIFACT_NAME="drasi-sse-cli-${{ matrix.artifact_suffix }}"
if [[ -f "$SRC_BINARY" ]]; then
cp "$SRC_BINARY" "$ARTIFACT_NAME"
chmod +x "$ARTIFACT_NAME"
else
echo "Error: Built binary not found at $SRC_BINARY"
ls -la "examples/sse-cli/target/${{ matrix.target }}/release/" || true
exit 1
fi
- name: Upload binary artifact
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-${{ matrix.artifact_suffix }}
path: drasi-sse-cli-${{ matrix.artifact_suffix }}
if-no-files-found: error
# Windows SSE CLI build (cross-compiled from Linux)
build-sse-cli-windows:
name: Build SSE CLI Windows (x86_64)
runs-on: ubuntu-latest
needs: determine-version
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
# See note in build-linux about why we install cross from a pinned commit.
- name: Install cross
run: cargo install cross --git https://github.com/cross-rs/cross --rev 65fe72b0cdb1e7e0cc0652517498d4389cc8f5cf --locked
- name: Build binary
run: |
rustup target add x86_64-pc-windows-gnu
cross build --release --target x86_64-pc-windows-gnu --manifest-path examples/sse-cli/Cargo.toml
SRC_BINARY="examples/sse-cli/target/x86_64-pc-windows-gnu/release/drasi-sse-cli.exe"
ARTIFACT_NAME="drasi-sse-cli-x86_64-windows.exe"
if [[ -f "$SRC_BINARY" ]]; then
cp "$SRC_BINARY" "$ARTIFACT_NAME"
echo "✓ Built $ARTIFACT_NAME ($(du -h "$ARTIFACT_NAME" | cut -f1))"
else
echo "Error: Built binary not found at $SRC_BINARY"
ls -la "examples/sse-cli/target/x86_64-pc-windows-gnu/release/" || true
exit 1
fi
- name: Upload binary artifact
uses: actions/upload-artifact@v5
with:
name: drasi-sse-cli-x86_64-windows
path: drasi-sse-cli-x86_64-windows.exe
if-no-files-found: error
# Build Docker images for Linux platforms
build-docker:
name: Build Docker ${{ matrix.platform }}
runs-on: ${{ matrix.runner }}
needs: determine-version
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
suffix: amd64
- platform: linux/arm64
runner: ubuntu-24.04-arm
suffix: arm64
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Log in to GitHub Container Registry
if: ${{ github.event_name != 'schedule' && !inputs.dry_run }}
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push platform-specific image
if: ${{ github.event_name != 'schedule' && !inputs.dry_run }}
env:
IMAGE_PREFIX: ${{ env.IMAGE_PREFIX }}
DOCKER_TAG_VERSION: ${{ needs.determine-version.outputs.tag }}-${{ matrix.suffix }}
DOCKERX_OPTS: --platform ${{ matrix.platform }} --push --cache-from type=gha --cache-to type=gha,mode=max
run: |
make docker-build
- name: Build platform-specific image (dry run)
if: ${{ github.event_name == 'schedule' || inputs.dry_run }}
env:
IMAGE_PREFIX: local
DOCKER_TAG_VERSION: dry-run-${{ matrix.suffix }}
DOCKERX_OPTS: --platform ${{ matrix.platform }} --cache-from type=gha --cache-to type=gha,mode=max --load
run: |
make docker-build
# Create multi-arch manifest
create-manifest:
name: Create Multi-Arch Manifest
if: ${{ github.event_name != 'schedule' && !inputs.dry_run }}
needs: [determine-version, build-docker]
runs-on: ubuntu-latest
steps:
- name: Log in to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create and push manifest list
run: |
docker buildx imagetools create -t ${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }} \
${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-amd64 \
${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-arm64
- name: Create and push 'latest' tag
run: |
docker buildx imagetools create -t ${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:latest \
${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-amd64 \
${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-arm64
- name: Generate summary
run: |
echo "## Docker Image Published :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Image:** \`${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "**Platforms:** \`linux/amd64, linux/arm64\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Tags Created" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:latest\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Platform-Specific Tags" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-amd64\`" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}-arm64\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Pull Commands" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:latest" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "## Binary Artifacts Published :package:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Binary artifacts are available as workflow artifacts for the following targets:" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-aarch64-apple-darwin\` (macOS ARM64 / Apple Silicon)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-x86_64-apple-darwin\` (macOS x86_64 / Intel)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-x86_64-windows-msvc.exe\` (Windows x86_64 MSVC)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-aarch64-linux-gnu\` (Linux ARM64 glibc)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-x86_64-linux-gnu\` (Linux x86_64 glibc)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-aarch64-linux-musl\` (Linux ARM64 musl)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-x86_64-linux-musl\` (Linux x86_64 musl)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### SSE CLI Binaries" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-aarch64-apple-darwin\` (macOS ARM64)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-x86_64-apple-darwin\` (macOS x86_64)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-x86_64-windows.exe\` (Windows x86_64)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-aarch64-linux-gnu\` (Linux ARM64 glibc)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-x86_64-linux-gnu\` (Linux x86_64 glibc)" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-sse-cli-aarch64-linux-musl\` (Linux ARM64 musl)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "### Examples" >> $GITHUB_STEP_SUMMARY
echo "- \`drasi-server-examples.zip\`" >> $GITHUB_STEP_SUMMARY echo "- \`drasi-sse-cli-x86_64-linux-musl\` (Linux x86_64 musl)" >> $GITHUB_STEP_SUMMARY
# Smoke-test each binary with the hello-world (mock source) config
test-binaries:
name: Test ${{ matrix.artifact_suffix }}
needs: [build-linux, build-macos, build-windows-msvc]
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
# macOS ARM64 (Apple Silicon)
- artifact_suffix: aarch64-apple-darwin
runner: macos-latest
binary_name: "drasi-server-aarch64-apple-darwin"
# macOS x86_64 (Intel) - cross-compiled, tested via Rosetta 2
- artifact_suffix: x86_64-apple-darwin
runner: macos-latest
binary_name: "drasi-server-x86_64-apple-darwin"
# Linux x86_64
- artifact_suffix: x86_64-linux-gnu
runner: ubuntu-latest
binary_name: "drasi-server-x86_64-linux-gnu"
# Linux ARM64
- artifact_suffix: aarch64-linux-gnu
runner: ubuntu-24.04-arm
binary_name: "drasi-server-aarch64-linux-gnu"
# Windows x86_64 (MSVC)
- artifact_suffix: x86_64-windows-msvc
runner: windows-latest
binary_name: "drasi-server-x86_64-windows-msvc.exe"
# Linux x86_64 (musl) — tested inside Alpine container
- artifact_suffix: x86_64-linux-musl
runner: ubuntu-latest
binary_name: "drasi-server-x86_64-linux-musl"
use_alpine: true
# Linux ARM64 (musl) — tested inside Alpine container
- artifact_suffix: aarch64-linux-musl
runner: ubuntu-24.04-arm
binary_name: "drasi-server-aarch64-linux-musl"
use_alpine: true
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download binary artifact
uses: actions/download-artifact@v5
with:
name: drasi-server-${{ matrix.artifact_suffix }}
path: bin
- name: Run hello-world smoke test (Unix)
if: runner.os != 'Windows' && !matrix.use_alpine
run: |
chmod +x "bin/${{ matrix.binary_name }}"
BINARY="$(pwd)/bin/${{ matrix.binary_name }}"
# Plugins are auto-installed at server startup (autoInstallPlugins: true
# in the test config) from the default OCI registry.
SERVER_BINARY="$BINARY" \
tests/integration/hello-world/run-integration-test.sh
- name: Run hello-world smoke test (Alpine/musl)
if: matrix.use_alpine
run: |
chmod +x "bin/${{ matrix.binary_name }}"
docker run --rm \
-v "$(pwd)/bin:/bin-artifacts" \
-v "$(pwd)/tests:/tests" \
alpine:latest \
sh -c "
apk add --no-cache bash curl libstdc++ libgcc &&
SERVER_BINARY=/bin-artifacts/${{ matrix.binary_name }} \
bash /tests/integration/hello-world/run-integration-test.sh
"
- name: Run hello-world smoke test (Windows)
if: runner.os == 'Windows'
shell: bash
run: |
BINARY_PATH="$(pwd)/bin/${{ matrix.binary_name }}"
echo "=== Downloaded artifact contents ==="
find bin/ -type f | head -30
echo ""
echo "=== Binary info ==="
file "$BINARY_PATH" || true
echo "Size: $(du -h "$BINARY_PATH" | cut -f1)"
echo ""
# Quick sanity: try running with --help first
echo "=== Sanity check: --help ==="
"$BINARY_PATH" --help 2>&1 || echo "(exit code: $?)"
echo ""
# Plugins are auto-installed at server startup (autoInstallPlugins: true
# in the test config) from the default OCI registry.
SERVER_BINARY="$BINARY_PATH" \
tests/integration/hello-world/run-integration-test.sh
- name: Show server logs on failure
if: failure()
shell: bash
run: |
echo "=== Server logs ==="
if [ -f tests/integration/hello-world/server.log ]; then
cat tests/integration/hello-world/server.log
else
echo "No server log file found"
fi
echo ""
echo "=== bin/ directory ==="
ls -la bin/ 2>/dev/null || true
echo ""
echo "=== Windows Event Logs (Application) ==="
powershell -Command "Get-WinEvent -LogName Application -MaxEvents 10 | Format-List" 2>/dev/null || true
# Create GitHub Release with binary artifacts
create-release:
name: Create GitHub Release
if: ${{ github.event_name != 'schedule' && !inputs.dry_run }}
needs: [determine-version, create-manifest, test-binaries, build-sse-cli-linux, build-sse-cli-macos, build-sse-cli-windows]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download drasi-server artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
pattern: drasi-server-*
merge-multiple: false
- name: Download drasi-sse-cli artifacts
uses: actions/download-artifact@v5
with:
path: artifacts
pattern: drasi-sse-cli-*
merge-multiple: false
- name: Zip examples folder
run: |
zip -r "drasi-server-examples.zip" examples/ \
-x 'examples/sse-cli/target/*' \
-x 'examples/trading/target/*'
echo "✓ Created drasi-server-examples.zip"
ls -lh "drasi-server-examples.zip"
- name: Prepare release assets
id: prepare
run: |
mkdir -p release-assets
find artifacts -type f -exec cp {} release-assets/ \;
cp drasi-server-examples.zip release-assets/
ls -lh release-assets/
ASSET_COUNT=$(ls -1 release-assets/ | wc -l)
echo "asset_count=${ASSET_COUNT}" >> $GITHUB_OUTPUT
- name: Create release notes
run: |
cat > release-notes.md << 'NOTES_EOF'
## Binary Artifacts
This release includes pre-built binaries for the following platforms:
### macOS
- ARM64 (Apple Silicon) - `drasi-server-aarch64-apple-darwin`
- x86_64 (Intel) - `drasi-server-x86_64-apple-darwin`
### Windows
- x86_64 (MSVC) - `drasi-server-x86_64-windows-msvc.exe`
### Linux (glibc)
- ARM64 - `drasi-server-aarch64-linux-gnu`
- x86_64 - `drasi-server-x86_64-linux-gnu`
### Linux (musl)
- ARM64 - `drasi-server-aarch64-linux-musl`
- x86_64 - `drasi-server-x86_64-linux-musl`
## Examples
- `drasi-server-examples.zip` — Example configurations, scripts, and projects
## SSE CLI Binaries
Pre-built binaries for the `drasi-sse-cli` tool (streams query change events via SSE):
### macOS
- ARM64 (Apple Silicon) - `drasi-sse-cli-aarch64-apple-darwin`
- x86_64 (Intel) - `drasi-sse-cli-x86_64-apple-darwin`
### Windows
- x86_64 - `drasi-sse-cli-x86_64-windows.exe`
### Linux (glibc)
- ARM64 - `drasi-sse-cli-aarch64-linux-gnu`
- x86_64 - `drasi-sse-cli-x86_64-linux-gnu`
### Linux (musl)
- ARM64 - `drasi-sse-cli-aarch64-linux-musl`
- x86_64 - `drasi-sse-cli-x86_64-linux-musl`
## Docker Images
Multi-arch Docker images are available at:
- `${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:${{ needs.determine-version.outputs.tag }}`
- `${{ env.IMAGE_PREFIX }}/${{ env.IMAGE_NAME }}:latest`
Supported architectures: `linux/amd64`, `linux/arm64`
NOTES_EOF
- name: Create or update versioned GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ needs.determine-version.outputs.tag }}"
if gh release view "${TAG}" --repo ${{ github.repository }} 2>/dev/null; then
echo "Release ${TAG} already exists. Updating with new artifacts..."
gh release upload "${TAG}" release-assets/* --repo ${{ github.repository }} --clobber
else
echo "Creating new release ${TAG}..."
gh release create "${TAG}" \
release-assets/* \
--repo ${{ github.repository }} \
--title "Release ${TAG}" \
--notes-file release-notes.md
fi
- name: Create or update 'latest' GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ needs.determine-version.outputs.tag }}"
# Delete existing 'latest' release and tag if they exist
if gh release view "latest" --repo ${{ github.repository }} 2>/dev/null; then
echo "Deleting existing 'latest' release..."
gh release delete "latest" --repo ${{ github.repository }} --yes --cleanup-tag
fi
echo "Creating 'latest' release (pointing to ${TAG})..."
gh release create "latest" \
release-assets/* \
--repo ${{ github.repository }} \
--title "Latest Release (${TAG})" \
--notes-file release-notes.md \
--prerelease
- name: Update summary
run: |
TAG="${{ needs.determine-version.outputs.tag }}"
echo "" >> $GITHUB_STEP_SUMMARY
echo "## GitHub Releases Published :tada:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Versioned: [\`${TAG}\`](https://github.com/${{ github.repository }}/releases/tag/${TAG})" >> $GITHUB_STEP_SUMMARY
echo "- Latest: [\`latest\`](https://github.com/${{ github.repository }}/releases/tag/latest) (points to ${TAG})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Total assets: ${{ steps.prepare.outputs.asset_count }}" >> $GITHUB_STEP_SUMMARY