name: Rust CI/CD
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '0 0 * * 0'
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
quality:
name: Code Quality
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Cache cargo dependencies
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
security:
name: Security Audit
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: |
if ! command -v cargo-audit &> /dev/null; then
cargo install cargo-audit
else
echo "cargo-audit is already installed"
fi
- name: Run security audit
run: cargo audit
test:
name: Test Suite
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, beta] exclude:
- os: windows-latest
rust: beta
- os: macos-latest
rust: beta
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache cargo dependencies
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.rust }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-${{ matrix.rust }}-cargo-
- name: Build
run: cargo build --verbose --all-features
- name: Run tests
run: cargo test --verbose --all-features
- name: Run examples
shell: bash
run: |
if [ -d "examples" ]; then
for example in examples/*.rs; do
if [ -f "$example" ]; then
example_name=$(basename "$example" .rs)
echo "Running example: $example_name"
cargo run --example "$example_name" || echo "Example $example_name failed or requires interaction"
fi
done
else
echo "No examples directory found"
fi
android:
name: Android Cross-compilation
runs-on: ubuntu-latest
strategy:
matrix:
target:
- aarch64-linux-android - armv7-linux-androideabi - i686-linux-android - x86_64-linux-android steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup Android NDK
uses: nttld/setup-ndk@v1
with:
ndk-version: r26d
add-to-path: false
- name: Cache cargo dependencies
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-android-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-android-${{ matrix.target }}-cargo-
- name: Setup Android cross-compilation environment
run: |
export ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME
echo "ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT" >> $GITHUB_ENV
# Set up linker for each target
case "${{ matrix.target }}" in
aarch64-linux-android)
echo "CC_aarch64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang" >> $GITHUB_ENV
echo "CXX_aarch64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang++" >> $GITHUB_ENV
echo "AR_aarch64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang" >> $GITHUB_ENV
;;
armv7-linux-androideabi)
echo "CC_armv7_linux_androideabi=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi33-clang" >> $GITHUB_ENV
echo "CXX_armv7_linux_androideabi=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi33-clang++" >> $GITHUB_ENV
echo "AR_armv7_linux_androideabi=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi33-clang" >> $GITHUB_ENV
;;
i686-linux-android)
echo "CC_i686_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android33-clang" >> $GITHUB_ENV
echo "CXX_i686_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android33-clang++" >> $GITHUB_ENV
echo "AR_i686_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CARGO_TARGET_I686_LINUX_ANDROID_LINKER=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android33-clang" >> $GITHUB_ENV
;;
x86_64-linux-android)
echo "CC_x86_64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android33-clang" >> $GITHUB_ENV
echo "CXX_x86_64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android33-clang++" >> $GITHUB_ENV
echo "AR_x86_64_linux_android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" >> $GITHUB_ENV
echo "CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android33-clang" >> $GITHUB_ENV
;;
esac
- name: Build for Android target
run: |
echo "Building for target: ${{ matrix.target }}"
cargo build --target ${{ matrix.target }} --verbose --all-features
- name: Check if examples can be built for Android
run: |
echo "Checking if examples can be built for ${{ matrix.target }}"
if [ -d "examples" ]; then
for example in examples/*.rs; do
if [ -f "$example" ]; then
example_name=$(basename "$example" .rs)
echo "Building example: $example_name for ${{ matrix.target }}"
cargo build --target ${{ matrix.target }} --example "$example_name" || echo "Example $example_name failed to build for ${{ matrix.target }}"
fi
done
else
echo "No examples directory found"
fi
- name: Verify Android compatibility
run: |
echo "✓ Successfully built rsactor for Android target: ${{ matrix.target }}"
echo " - Target architecture: ${{ matrix.target }}"
echo " - Android API level: 33"
echo " - NDK version: r26d"
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-docs-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Build documentation
run: cargo doc --no-deps --all-features
- name: Check for broken doc links
run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: "-D warnings"
coverage:
name: Code Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools-preview
- name: Cache cargo dependencies
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-coverage-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install cargo-llvm-cov
run: |
if ! command -v cargo-llvm-cov &> /dev/null; then
cargo install cargo-llvm-cov
else
echo "cargo-llvm-cov is already installed"
fi
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: lcov.info
fail_ci_if_error: false
ci-success:
name: CI Success
runs-on: ubuntu-latest
needs: [quality, security, test, android, docs, coverage]
if: always()
steps:
- name: Check all jobs
run: |
if [[ "${{ needs.quality.result }}" != "success" ]]; then
echo "Quality check failed"
exit 1
fi
if [[ "${{ needs.security.result }}" != "success" ]]; then
echo "Security audit failed"
exit 1
fi
if [[ "${{ needs.test.result }}" != "success" ]]; then
echo "Tests failed"
exit 1
fi
if [[ "${{ needs.android.result }}" != "success" ]]; then
echo "Android cross-compilation tests failed"
exit 1
fi
if [[ "${{ needs.docs.result }}" != "success" ]]; then
echo "Documentation build failed"
exit 1
fi
if [[ "${{ needs.coverage.result }}" != "success" ]]; then
echo "Coverage check failed"
exit 1
fi
echo "All CI jobs passed! (MSRV testing handled by separate msrv.yml workflow)"