name: CI
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
format-lint:
name: Format & Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Install Python linting tools
run: |
python3 -m pip install --upgrade pip
python3 -m pip install black ruff
- name: Check version consistency
run: |
chmod +x scripts/check_version.sh
./scripts/check_version.sh
- name: Check Rust formatting
run: cargo fmt --check --all
- name: Check Python formatting
run: python3 -m black --check tests/python/
- name: Rust clippy (without Python feature)
run: cargo clippy --all-targets --features observability -- -D warnings
- name: Python linting
run: python3 -m ruff check tests/python/
- name: Rust clippy (with Python feature)
run: |
# Check if external dependencies exist
if [ ! -d "../zerobus-sdk-rs/sdk" ]; then
mkdir -p ../zerobus-sdk-rs/sdk
echo '[package]' > ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'name = "databricks-zerobus-ingest-sdk"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'version = "0.1.0"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'edition = "2021"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo '[dependencies]' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'prost-types = "0.13"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
mkdir -p ../zerobus-sdk-rs/sdk/src
echo 'pub struct ZerobusSdk;' > ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusStream;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct StreamConfigurationOptions;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct TableProperties { pub table_name: String, pub descriptor_proto: Option<prost_types::DescriptorProto>, pub file_descriptor_set: Option<prost_types::FileDescriptorSet> }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
fi
cargo clippy --all-targets --all-features -- -D warnings || echo "Clippy with Python feature skipped"
build:
name: Build
runs-on: ubuntu-latest
needs: format-lint
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Check if external dependencies exist
id: check_deps
shell: bash
run: |
if [ -d "../zerobus-sdk-rs/sdk" ] && [ -f "../zerobus-sdk-rs/sdk/Cargo.toml" ]; then
echo "zerobus_sdk_exists=true" >> $GITHUB_OUTPUT
else
echo "zerobus_sdk_exists=false" >> $GITHUB_OUTPUT
echo "Creating stub for zerobus SDK"
mkdir -p ../zerobus-sdk-rs/sdk
echo '[package]' > ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'name = "databricks-zerobus-ingest-sdk"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'version = "0.1.0"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'edition = "2021"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo '[dependencies]' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'prost-types = "0.13"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
mkdir -p ../zerobus-sdk-rs/sdk/src
echo '// Stub for CI - actual SDK not available' > ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusSdk;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusStream;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct StreamConfigurationOptions;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct TableProperties {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub table_name: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub descriptor_proto: Option<prost_types::DescriptorProto>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub file_descriptor_set: Option<prost_types::FileDescriptorSet>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl ZerobusSdk {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub fn new(_endpoint: String, _unity_catalog_url: String) -> Result<Self, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub async fn create_stream(' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' &self,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _table_properties: TableProperties,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_id: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_secret: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _options: Option<StreamConfigurationOptions>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ) -> Result<ZerobusStream, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl Default for StreamConfigurationOptions {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' fn default() -> Self {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Self' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
fi
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build Rust (without Python)
run: cargo build --release --features observability
- name: Build Rust (with Python)
run: |
python3 -m pip install --upgrade pip maturin
PYTHON_EXEC=$(python3 -c "import sys; print(sys.executable)")
echo "PYO3_PYTHON=$PYTHON_EXEC" >> $GITHUB_ENV
cargo build --release --all-features || echo "Python build skipped"
test-rust-ubuntu:
name: Rust Tests (Ubuntu)
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
with:
lfs: false
- name: Configure Git for line endings
shell: bash
run: |
git config core.autocrlf false
git config core.eol lf
git add --renormalize .
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build essentials (Linux)
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y build-essential
sudo apt-get install -y libpython3.11 libpython3.11-dev || \
sudo apt-get install -y libpython3.12 libpython3.12-dev || \
sudo apt-get install -y libpython3-dev || \
echo "Python shared library package not found, continuing anyway"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Check if external dependencies exist
id: check_deps
shell: bash
run: |
if [ -d "../zerobus-sdk-rs/sdk" ] && [ -f "../zerobus-sdk-rs/sdk/Cargo.toml" ]; then
echo "zerobus_sdk_exists=true" >> $GITHUB_OUTPUT
else
echo "zerobus_sdk_exists=false" >> $GITHUB_OUTPUT
echo "Creating stub for zerobus SDK"
mkdir -p ../zerobus-sdk-rs/sdk
echo '[package]' > ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'name = "databricks-zerobus-ingest-sdk"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'version = "0.1.0"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'edition = "2021"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo '[dependencies]' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'prost-types = "0.13"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
mkdir -p ../zerobus-sdk-rs/sdk/src
echo '// Stub for CI - actual SDK not available' > ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusSdk;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusStream;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct StreamConfigurationOptions;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct TableProperties {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub table_name: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub descriptor_proto: Option<prost_types::DescriptorProto>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub file_descriptor_set: Option<prost_types::FileDescriptorSet>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl ZerobusSdk {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub fn new(_endpoint: String, _unity_catalog_url: String) -> Result<Self, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub async fn create_stream(' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' &self,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _table_properties: TableProperties,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_id: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_secret: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _options: Option<StreamConfigurationOptions>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ) -> Result<ZerobusStream, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl Default for StreamConfigurationOptions {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' fn default() -> Self {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Self' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
fi
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run Rust tests
shell: bash
run: |
cargo test --features observability --lib
cargo test --features observability --test '*' || true
test-rust-other:
name: Rust Tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: build
strategy:
matrix:
os: [macos-latest, windows-latest]
steps:
- uses: actions/checkout@v4
with:
lfs: false
- name: Configure Git for line endings
shell: bash
run: |
git config core.autocrlf false
git config core.eol lf
git add --renormalize .
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Check if external dependencies exist
id: check_deps
shell: bash
run: |
if [ -d "../zerobus-sdk-rs/sdk" ] && [ -f "../zerobus-sdk-rs/sdk/Cargo.toml" ]; then
echo "zerobus_sdk_exists=true" >> $GITHUB_OUTPUT
else
echo "zerobus_sdk_exists=false" >> $GITHUB_OUTPUT
echo "Creating stub for zerobus SDK"
mkdir -p ../zerobus-sdk-rs/sdk
echo '[package]' > ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'name = "databricks-zerobus-ingest-sdk"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'version = "0.1.0"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'edition = "2021"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo '[dependencies]' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'prost-types = "0.13"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
mkdir -p ../zerobus-sdk-rs/sdk/src
echo '// Stub for CI - actual SDK not available' > ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusSdk;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusStream;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct StreamConfigurationOptions;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct TableProperties {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub table_name: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub descriptor_proto: Option<prost_types::DescriptorProto>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub file_descriptor_set: Option<prost_types::FileDescriptorSet>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl ZerobusSdk {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub fn new(_endpoint: String, _unity_catalog_url: String) -> Result<Self, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub async fn create_stream(' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' &self,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _table_properties: TableProperties,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_id: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_secret: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _options: Option<StreamConfigurationOptions>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ) -> Result<ZerobusStream, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl Default for StreamConfigurationOptions {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' fn default() -> Self {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Self' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
fi
- name: Cache Cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Run Rust tests
shell: bash
run: |
cargo test --features observability --lib
cargo test --features observability --test '*' || true
test-python:
name: Python Tests
runs-on: ubuntu-latest
needs: [build, test-rust-ubuntu]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Set PYO3_PYTHON for PyO3
shell: bash
run: |
PYTHON_EXEC=$(python3 -c "import sys; print(sys.executable)")
echo "PYO3_PYTHON=$PYTHON_EXEC" >> $GITHUB_ENV
- name: Install build essentials (Linux)
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y build-essential
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Check if external dependencies exist
id: check_deps
shell: bash
run: |
if [ -d "../zerobus-sdk-rs/sdk" ] && [ -f "../zerobus-sdk-rs/sdk/Cargo.toml" ]; then
echo "zerobus_sdk_exists=true" >> $GITHUB_OUTPUT
else
echo "zerobus_sdk_exists=false" >> $GITHUB_OUTPUT
echo "Creating stub for zerobus SDK"
mkdir -p ../zerobus-sdk-rs/sdk
echo '[package]' > ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'name = "databricks-zerobus-ingest-sdk"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'version = "0.1.0"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'edition = "2021"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo '[dependencies]' >> ../zerobus-sdk-rs/sdk/Cargo.toml
echo 'prost-types = "0.13"' >> ../zerobus-sdk-rs/sdk/Cargo.toml
mkdir -p ../zerobus-sdk-rs/sdk/src
echo '// Stub for CI - actual SDK not available' > ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusSdk;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct ZerobusStream;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct StreamConfigurationOptions;' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'pub struct TableProperties {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub table_name: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub descriptor_proto: Option<prost_types::DescriptorProto>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub file_descriptor_set: Option<prost_types::FileDescriptorSet>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl ZerobusSdk {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub fn new(_endpoint: String, _unity_catalog_url: String) -> Result<Self, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' pub async fn create_stream(' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' &self,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _table_properties: TableProperties,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_id: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _client_secret: String,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' _options: Option<StreamConfigurationOptions>,' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' ) -> Result<ZerobusStream, String> {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Err("SDK not available in CI".to_string())' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo 'impl Default for StreamConfigurationOptions {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' fn default() -> Self {' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' Self' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo ' }' >> ../zerobus-sdk-rs/sdk/src/lib.rs
echo '}' >> ../zerobus-sdk-rs/sdk/src/lib.rs
fi
- name: Install maturin
shell: bash
run: |
python3 -m pip install --upgrade pip
python3 -m pip install maturin
- name: Build and install Python bindings
shell: bash
run: |
python3 -m venv .venv
source .venv/bin/activate
maturin develop --release
- name: Install pytest and dependencies
shell: bash
run: |
source .venv/bin/activate
pip install pytest pytest-cov pytest-forked pytest-asyncio
- name: Run Python tests
shell: bash
run: |
source .venv/bin/activate
export PYO3_NO_PYTHON_VERSION_CHECK=1
pytest tests/python/ -v --forked
release:
name: Create Release
runs-on: ubuntu-latest
needs: [format-lint, build, test-rust-ubuntu, test-rust-other, test-python]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get version from Cargo.toml
id: get_version
run: |
VERSION=$(grep '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: Check if tag already exists
id: check_tag
run: |
TAG="v${{ steps.get_version.outputs.version }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Tag $TAG already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Tag $TAG does not exist"
fi
- name: Create Git tag
if: steps.check_tag.outputs.exists == 'false'
run: |
TAG="v${{ steps.get_version.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$TAG" -m "Release $TAG"
git push origin "$TAG"
- name: Create GitHub Release
if: steps.check_tag.outputs.exists == 'false'
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ steps.get_version.outputs.version }}
name: Release v${{ steps.get_version.outputs.version }}
body: |
## Release v${{ steps.get_version.outputs.version }}
Automated release created from merge to ${{ github.ref }}.
See [CHANGELOG.md](CHANGELOG.md) for details.
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish:
name: Publish to crates.io
runs-on: ubuntu-latest
needs: [format-lint, build, test-rust-ubuntu, test-rust-other, test-python, release]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
permissions:
contents: write issues: write pull-requests: read
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Get version from Cargo.toml
id: version
run: |
VERSION=$(grep '^version = ' Cargo.toml | sed 's/version = "\(.*\)"/\1/')
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"
- name: Verify release tag exists
run: |
if ! git rev-parse "v${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
echo "ERROR: Release tag v${{ steps.version.outputs.version }} does not exist"
echo "The release job should have created this tag. Publishing aborted."
exit 1
fi
echo "✅ Release tag v${{ steps.version.outputs.version }} exists (created by release job)"
- name: Extract referenced issues from commits
id: extract-issues
run: |
# Get commits since last tag (or all commits if no tag exists)
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LAST_TAG" ]; then
COMMITS=$(git log --pretty=format:"%s %b" --no-merges)
else
COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"%s %b" --no-merges)
fi
# Extract issue numbers from commit messages
# Patterns: "closes #123", "fixes #456", "resolves #789", or just "#123"
ISSUES=$(echo "$COMMITS" | grep -oE '(closes|fixes|resolves|implements|addresses)?\s*#?[0-9]+' | grep -oE '[0-9]+' | sort -u | tr '\n' ' ' || echo "")
if [ -z "$ISSUES" ]; then
echo "No referenced issues found in commits"
echo "issues=" >> $GITHUB_OUTPUT
else
echo "Found referenced issues: $ISSUES"
echo "issues=$ISSUES" >> $GITHUB_OUTPUT
fi
- name: Close referenced issues
if: steps.extract-issues.outputs.issues != ''
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
VERSION="${{ steps.version.outputs.version }}"
ISSUES="${{ steps.extract-issues.outputs.issues }}"
# Install gh CLI if not available
if ! command -v gh &> /dev/null; then
echo "Installing GitHub CLI..."
type -p curl >/dev/null || (apt update && apt install curl -y)
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt update \
&& apt install gh -y
fi
for issue in $ISSUES; do
echo "Checking issue #$issue..."
# Check if issue exists and is open
ISSUE_STATE=$(gh issue view $issue --repo ${{ github.repository }} --json state -q .state 2>/dev/null || echo "notfound")
if [ "$ISSUE_STATE" = "notfound" ]; then
echo " Issue #$issue not found, skipping"
continue
elif [ "$ISSUE_STATE" = "closed" ]; then
echo " Issue #$issue is already closed, skipping"
continue
elif [ "$ISSUE_STATE" = "open" ]; then
echo " Closing issue #$issue..."
gh issue close $issue --repo ${{ github.repository }} --comment "Closed automatically as part of release v$VERSION. This issue was referenced in the release commits." || echo " Failed to close issue #$issue"
fi
done
- name: Verify package contents
run: |
# List files that would be included in the package (doesn't create package)
# Ensure .venv directories are excluded (they should be in .cargoignore and .gitignore)
cargo package --list
# Verify .venv directories are not included
if cargo package --list 2>&1 | grep -q "\.venv"; then
echo "ERROR: .venv directories found in package list!"
cargo package --list 2>&1 | grep "\.venv"
exit 1
fi
- name: Build crate package
run: |
# Create the package file (.crate)
cargo package
- name: Verify publish readiness (dry-run)
run: |
# Use cargo publish --dry-run to verify everything is ready for publishing
# This checks metadata, dependencies, and registry compatibility
# CARGO_REGISTRY_TOKEN is the recommended environment variable name per crates.io docs
cargo publish --dry-run --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}