Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
name: Setup spiceio
description: Install a released version of spiceio and start the S3-to-SMB proxy

inputs:
version:
description: Release tag to install (e.g. "v0.1.0"). Use "latest" for the most recent release.
required: false
default: latest
smb-server:
description: SMB server hostname or IP
required: true
smb-user:
description: SMB username
required: true
smb-pass:
description: SMB password
required: true
smb-share:
description: SMB share name
required: true
smb-port:
description: SMB port
required: false
default: "445"
smb-domain:
description: SMB domain
required: false
default: ""
bucket:
description: Virtual S3 bucket name
required: false
default: spiceio
region:
description: AWS region to advertise
required: false
default: us-east-1
bind:
description: Listen address for the S3 endpoint
required: false
default: "127.0.0.1:8333"
token:
description: GitHub token for downloading release assets from private repos
required: true

outputs:
endpoint:
description: The S3-compatible endpoint URL
value: ${{ steps.start.outputs.endpoint }}
pid:
description: PID of the spiceio background process (empty if skipped)
value: ${{ steps.start.outputs.pid }}

runs:
using: composite
steps:
- name: Download and install spiceio
id: install
shell: bash
env:
GH_TOKEN: ${{ inputs.token }}
run: |
set -euo pipefail

REPO="spiceai/spiceio"
INSTALL_DIR="${RUNNER_TEMP}/spiceio-bin"
ASSET="spiceio-${RUNNER_OS}-${RUNNER_ARCH}.tar.gz"
VERSION="${{ inputs.version }}"

mkdir -p "$INSTALL_DIR"

if [[ "$VERSION" == "latest" ]]; then
gh release download --repo "$REPO" --pattern "$ASSET" --pattern "${ASSET}.sha256" --dir "$INSTALL_DIR"
else
gh release download "$VERSION" --repo "$REPO" --pattern "$ASSET" --pattern "${ASSET}.sha256" --dir "$INSTALL_DIR"
fi

# Verify integrity
cd "$INSTALL_DIR"
shasum -a 256 -c "${ASSET}.sha256"

tar xzf "$ASSET" -C "$INSTALL_DIR"
chmod +x "$INSTALL_DIR/spiceio"
echo "$INSTALL_DIR" >> "$GITHUB_PATH"
echo "pid_file=${RUNNER_TEMP}/spiceio.pid" >> "$GITHUB_OUTPUT"

- name: Start spiceio
id: start
shell: bash
env:
Comment thread
lukekim marked this conversation as resolved.
SPICEIO_SMB_SERVER: ${{ inputs.smb-server }}
SPICEIO_SMB_PORT: ${{ inputs.smb-port }}
SPICEIO_SMB_USER: ${{ inputs.smb-user }}
SPICEIO_SMB_PASS: ${{ inputs.smb-pass }}
Comment thread
lukekim marked this conversation as resolved.
Outdated
SPICEIO_SMB_DOMAIN: ${{ inputs.smb-domain }}
SPICEIO_SMB_SHARE: ${{ inputs.smb-share }}
SPICEIO_BUCKET: ${{ inputs.bucket }}
SPICEIO_REGION: ${{ inputs.region }}
SPICEIO_BIND: ${{ inputs.bind }}
run: |
set -euo pipefail

ENDPOINT="http://${SPICEIO_BIND}"
PID_FILE="${{ steps.install.outputs.pid_file }}"

# Skip if spiceio is already listening on the requested address
if curl -sf -o /dev/null "$ENDPOINT/" 2>/dev/null; then
echo "spiceio already running at $ENDPOINT — skipping start"
echo "endpoint=$ENDPOINT" >> "$GITHUB_OUTPUT"
echo "pid=" >> "$GITHUB_OUTPUT"
echo "skipped=true" >> "$GITHUB_OUTPUT"
exit 0
Comment thread
lukekim marked this conversation as resolved.
fi

spiceio &
PID=$!
echo "$PID" > "$PID_FILE"
echo "pid=$PID" >> "$GITHUB_OUTPUT"
echo "endpoint=$ENDPOINT" >> "$GITHUB_OUTPUT"
echo "skipped=false" >> "$GITHUB_OUTPUT"

Comment thread
lukekim marked this conversation as resolved.
# Wait for readiness
echo "Waiting for spiceio on ${SPICEIO_BIND}..."
for i in $(seq 1 30); do
if curl -sf -o /dev/null "$ENDPOINT/" 2>/dev/null; then
echo "spiceio ready at $ENDPOINT (PID $PID)"
exit 0
fi
if ! kill -0 "$PID" 2>/dev/null; then
echo "::error::spiceio exited unexpectedly"
exit 1
fi
sleep 1
done
echo "::error::spiceio failed to start within 30s"
exit 1

- name: Register cleanup
if: always() && steps.start.outputs.skipped != 'true'
shell: bash
run: |
PID_FILE="${{ steps.install.outputs.pid_file }}"
if [[ -f "$PID_FILE" ]]; then
PID=$(cat "$PID_FILE")
if kill -0 "$PID" 2>/dev/null; then
echo "Stopping spiceio (PID $PID)"
kill "$PID" 2>/dev/null || true
wait "$PID" 2>/dev/null || true
fi
rm -f "$PID_FILE"
fi
76 changes: 76 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: CI

on:
pull_request:
types: [opened, synchronize, reopened]

Comment thread
lukekim marked this conversation as resolved.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
CARGO_NET_GIT_FETCH_WITH_CLI: true

jobs:
ci:
name: Format, lint, build, and test
# Self-hosted macOS runner required for CommonCrypto FFI and NAS access.
# Skip fork PRs to prevent untrusted code execution on self-hosted runners.
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: spiceai-macos
permissions:
contents: read

Comment thread
lukekim marked this conversation as resolved.
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt,clippy

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2

- name: Check formatting
run: cargo fmt --all --check

- name: Run cargo check
run: cargo check --locked --all-targets --all-features

- name: Run clippy
run: cargo clippy --locked --all-targets --all-features -- -D warnings -D clippy::all -D clippy::cargo -A clippy::cargo-common-metadata

- name: Check rustdoc
run: RUSTDOCFLAGS="-D warnings" cargo doc --locked --workspace --no-deps --document-private-items

- name: Build debug binary
run: cargo build --locked

- name: Run unit tests
run: cargo test --locked

- name: Run sccache integration test
# Skipped when UNAS_SMB_PASS secret is not configured (e.g. fork PRs)
if: env.SPICEIO_SMB_PASS != ''
env:
SPICEIO_SMB_SERVER: ${{ vars.SPICEIO_SMB_SERVER || '192.168.3.148' }}
SPICEIO_SMB_USER: ${{ vars.SPICEIO_SMB_USER || 'runner' }}
SPICEIO_SMB_PASS: ${{ secrets.UNAS_SMB_PASS }}
SPICEIO_SMB_SHARE: ${{ vars.SPICEIO_SMB_SHARE || 'ai_platform_dev' }}
SPICEIO_BUCKET: ${{ vars.SPICEIO_BUCKET || 'spiceio' }}
SPICEIO_REGION: ${{ vars.SPICEIO_REGION || 'us-west-1' }}
run: ./scripts/test-sccache.sh
Comment thread
lukekim marked this conversation as resolved.
Comment thread
lukekim marked this conversation as resolved.
Comment thread
lukekim marked this conversation as resolved.

- name: Build release artifact
run: cargo build --release --locked --bin spiceio

- name: Upload release artifact
uses: actions/upload-artifact@v4
with:
name: spiceio-${{ runner.os }}-${{ runner.arch }}
path: target/release/spiceio
if-no-files-found: error
49 changes: 49 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Release

on:
release:
types: [created]
Comment thread
lukekim marked this conversation as resolved.

Comment thread
lukekim marked this conversation as resolved.
Comment thread
lukekim marked this conversation as resolved.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
CARGO_NET_GIT_FETCH_WITH_CLI: true

jobs:
build:
name: Build release binary
# Self-hosted macOS runner required for CommonCrypto FFI
runs-on: spiceai-macos
permissions:
contents: write

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Rust toolchain
uses: dtolnay/rust-toolchain@stable

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2

- name: Build release binary
run: cargo build --release --locked --bin spiceio

- name: Package binary
run: |
cd target/release
tar czf spiceio-${{ runner.os }}-${{ runner.arch }}.tar.gz spiceio
shasum -a 256 spiceio-${{ runner.os }}-${{ runner.arch }}.tar.gz > spiceio-${{ runner.os }}-${{ runner.arch }}.tar.gz.sha256

- name: Upload release assets
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release upload "${{ github.event.release.tag_name }}" --clobber \
target/release/spiceio-${{ runner.os }}-${{ runner.arch }}.tar.gz \
target/release/spiceio-${{ runner.os }}-${{ runner.arch }}.tar.gz.sha256
15 changes: 15 additions & 0 deletions scripts/test-sccache.sh
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ CARGO_TARGET_DIR="$TEST_TARGET_DIR" cargo build 2>&1
echo ""
echo "[test] === warm build (should hit cache) ==="
rm -rf "$TEST_TARGET_DIR"
sccache --zero-stats 2>/dev/null || true
CARGO_TARGET_DIR="$TEST_TARGET_DIR" cargo build 2>&1

echo ""
Expand All @@ -267,3 +268,17 @@ echo "[test] sccache stats:"
echo "======================================="
sccache --show-stats
echo "======================================="

# ── Verify cache hits ───────────────────────────────────────────────────────

STATS=$(sccache --show-stats 2>&1)
CACHE_HITS=$(echo "$STATS" | grep -m1 "^Cache hits" | awk '{print $NF}' || echo "0")
WRITE_ERRORS=$(echo "$STATS" | grep -m1 "Cache write errors" | awk '{print $NF}' || echo "0")

echo ""
if [[ "${CACHE_HITS:-0}" -gt 0 && "${WRITE_ERRORS:-0}" -eq 0 ]]; then
echo "[test] PASS: warm build got $CACHE_HITS cache hits, 0 write errors"
else
echo "[test] FAIL: expected cache hits > 0 (got ${CACHE_HITS:-0}) and write errors == 0 (got ${WRITE_ERRORS:-0})"
Comment thread
lukekim marked this conversation as resolved.
exit 1
fi
Loading