Skip to content

Full CI

Full CI #338

Workflow file for this run

# Complete CI workflow
# Run by GitHub merge queues, and enforced before any PR is merged
# Includes unit and integration tests for all platforms, as well as optional nightly jobs
# See also: https://matklad.github.io/2021/09/04/fast-rust-builds.html
# Note: if workflow-wide fail-fast is needed, this step can be added at the end of *each* job.
# It will immediately cancel all outstanding jobs. This can be useful to free them up for other runs.
# - name: If job failed, cancel workflow...
# if: failure()
# uses: andymckay/cancel-action@0.2
# Note: important points about Rust caching (Swatinem/rust-cache action), which contributes majorly to speedup:
# 1. It caches only dependencies. The own crates are always fully recompiled, and incremental compilation is disabled.
# 2. It takes the current job ID as a key, which means it CANNOT cache between jobs.
# Running a prior job to reuse build artifacts is thus pointless with this action.
# 3. The dependencies in Cargo.toml are hashed into the key, thus it cannot happen that removing a dependency will still work
# due to the dependency being cached. On the other hand, it means dependency changes come with full recompile.
# 4. As the effectivity of the cache depends on previous runs and available runners, workflow execution times
# can easily vary by 30%.
# Note: if CI becomes more complex, we might look into code-generation of the action and workflow files at some point.
# 300+ LOC for a few platform tests is still OK, but there's a lot of repetition, and quite some limitations that could be addressed.
name: Full CI
env:
# Local variables
# Note: using variables is limited at the moment, see https://github.com/actions/runner/issues/480
GDRUST_FEATURES: "gdnative/async,gdnative/serde,gdnative_bindings_generator/debug"
CARGO_DENY_VERSION: "0.14.20"
CARGO_DINGHY_VERSION: "0.6.8"
CARGO_MACHETE_VERSION: "0.6.2"
on:
merge_group:
defaults:
run:
shell: bash
jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
with:
components: rustfmt
- name: "Check rustfmt"
run: cargo fmt --all -- --check
clippy:
name: clippy${{ matrix.rust.postfix }}
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.rust.toolchain == 'nightly' }}
needs: rustfmt
strategy:
matrix:
rust:
- toolchain: stable
postfix: ''
- toolchain: nightly
postfix: ' (nightly)'
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: ${{ matrix.rust.toolchain }}
components: clippy
- name: "Check clippy"
run: cargo clippy --workspace --features ${GDRUST_FEATURES} -- -D clippy::style -D clippy::complexity -D clippy::perf -D clippy::dbg_macro -D clippy::todo -D clippy::unimplemented -D warnings
cargo-deny-machete:
runs-on: ubuntu-latest
needs: rustfmt
steps:
- uses: actions/checkout@v4
# Deny
# Note: manually downloading is ~30s faster than https://github.com/EmbarkStudios/cargo-deny-action
- name: "Install cargo-deny"
run: |
wget --no-verbose https://github.com/EmbarkStudios/cargo-deny/releases/download/$CARGO_DENY_VERSION/cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl.tar.gz -O cargo-deny.tar.gz
tar -zxvf cargo-deny.tar.gz
mkdir -p $HOME/.cargo/bin
mv cargo-deny-$CARGO_DENY_VERSION-x86_64-unknown-linux-musl/cargo-deny $HOME/.cargo/bin
- name: "Deny non-conforming dependencies"
run: cargo deny check --config tools/deny.toml
# Machete
- name: "Install cargo-machete"
uses: baptiste0928/cargo-install@v1
with:
crate: cargo-machete
version: ${{ env.CARGO_MACHETE_VERSION }}
- name: "Use machete to cut down dependencies"
run: cargo machete
test:
name: test-${{ matrix.os.name }}${{ matrix.rust.postfix }}
needs: rustfmt
continue-on-error: ${{ matrix.rust.toolchain == 'nightly' }}
strategy:
fail-fast: true # cancel all jobs as soon as one fails?
matrix:
# Order this way because MacOS typically has longest duration, followed by Windows, so it benefits total workflow execution time.
# Additionally, the 'linux (msrv *)' special case will then be listed next to the other 'linux' jobs.
# Note: Windows uses '--target x86_64-pc-windows-msvc' by default as Cargo argument.
os:
- id: macos-latest
name: macos
- id: windows-latest
name: windows
- id: ubuntu-latest
name: linux
rust:
- toolchain: stable
postfix: ''
- toolchain: nightly
postfix: ' (nightly)'
# All non-stable versions skip UI tests, as it's usually impossible to have them satisfy all possible compiler versions simultaneously
include:
- rust: { toolchain: 'nightly' }
testflags: '-- --skip ui_tests'
- os: { id: ubuntu-latest, name: linux }
rust: { toolchain: '1.70', postfix: ' (msrv 1.70)' }
testflags: '-- --skip ui_tests'
- os: { id: ubuntu-latest, name: linux }
rust: { toolchain: 'stable', postfix: ' (minimal-deps)', special: 'minimal-deps' }
testflags: '-- --skip ui_tests'
runs-on: ${{ matrix.os.id }}
steps:
- uses: actions/checkout@v4
- name: "Install nightly Rust (minimal-deps only)"
if: ${{ matrix.rust.special == 'minimal-deps' }}
run: rustup toolchain install nightly --profile minimal --component cargo
- name: "Install minimal dependency versions from Cargo"
run: cargo +nightly update -Z minimal-versions
if: ${{ matrix.rust.special == 'minimal-deps' }}
- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: ${{ matrix.rust.toolchain }}
cache-key: ${{ matrix.rust.special }} # 'minimal-deps' or empty/not defined
- name: "Install LLVM"
uses: ./.github/composite/llvm
if: ${{ matrix.os.id == 'windows-latest' }}
- name: "Compile tests"
run: cargo test --workspace --features ${GDRUST_FEATURES} --no-run
- name: "Test"
run: cargo test --workspace --features ${GDRUST_FEATURES} ${{ matrix.testflags }}
build-release:
name: build-release-${{ matrix.os.name }}
needs: rustfmt
strategy:
fail-fast: true # cancel all jobs as soon as one fails?
matrix:
os:
- id: macos-latest
name: macos
- id: windows-latest
name: windows
- id: ubuntu-latest
name: linux
runs-on: ${{ matrix.os.id }}
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: stable
- name: "Install LLVM"
uses: ./.github/composite/llvm
if: ${{ matrix.os.id == 'windows-latest' }}
- name: "Release build (check only)"
run: cargo check --release
build-ios:
needs: rustfmt
#continue-on-error: ${{ matrix.rust == 'nightly' }}
#strategy:
# matrix:
# rust: [stable]
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
#with:
# rust: ${{ matrix.rust.toolchain }}
- name: "Install cargo-dinghy"
run: |
rustup target add x86_64-apple-ios
curl -L https://github.com/sonos/dinghy/releases/download/$CARGO_DINGHY_VERSION/cargo-dinghy-macos-$CARGO_DINGHY_VERSION.tgz -o cargo-dinghy-macos.tar.gz
tar -zxvf cargo-dinghy-macos.tar.gz
mkdir -p $HOME/.cargo/bin
cp cargo-dinghy-$CARGO_DINGHY_VERSION/cargo-dinghy $HOME/.cargo/bin
- name: "Cross-compile to iOS"
run: |
RUNTIME_ID=$(xcrun simctl list runtimes | grep iOS | cut -d ' ' -f 7 | tail -1)
export SIM_ID=$(xcrun simctl create My-iphone11 com.apple.CoreSimulator.SimDeviceType.iPhone-11 $RUNTIME_ID)
xcrun simctl boot $SIM_ID
cd gdnative-core
cargo dinghy --platform auto-ios-x86_64 test
cd ..
cd gdnative-sys
cargo dinghy --platform auto-ios-x86_64 test
build-android:
# Note: even though Android builds for another architecture than Linux, it can reuse downloaded crates (source code, maybe 'cargo check').
needs: rustfmt
#continue-on-error: ${{ matrix.rust == 'nightly' }}
#strategy:
# matrix:
# rust: [stable]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
#with:
# rust: ${{ matrix.rust.toolchain }}
- name: "Install Java + NDK"
run: |
# aarch64 and armv7 cover most Android phones & tablets.
rustup target add aarch64-linux-android armv7-linux-androideabi
sudo apt-get update
sudo apt-get install llvm-dev libclang-dev clang g++-multilib gcc-multilib libc6-dev libc6-dev-arm64-cross
# Relies on ls listing alphabetically, NDK versions having fixed
# digits, and the folder not containing any other files
- name: "Find highest Android NDK version"
run: |
highestNdk=$(ls $ANDROID_SDK_ROOT/ndk | tail -n1)
echo "Highest Android NDK: $highestNdk"
echo "ANDROID_NDK_VERSION=$highestNdk" >> $GITHUB_ENV
# See https://github.com/godot-rust/godot-rust/pull/920
- name: "Found version ${{ env.ANDROID_NDK_VERSION }}. Workaround Rust bug..."
run: >
find -L $ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION -name libunwind.a
-execdir sh -c 'echo "INPUT(-lunwind)" > libgcc.a' \;
- name: "Build Rust for targets: aarch64-linux-android, armv7-linux-androideabi"
run: |
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang++
export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang++
cargo build --target aarch64-linux-android --release
cargo build --target armv7-linux-androideabi --release
integration-test-godot:
name: itest-godot-${{ matrix.godot }}${{ matrix.postfix }}
needs: rustfmt
continue-on-error: ${{ matrix.rust.toolchain == 'nightly' }}
strategy:
fail-fast: false # cancel all jobs as soon as one fails?
matrix:
include:
# Latest Godot with different Rust versions
- rust: stable
godot: "3.5.1-stable"
postfix: ''
- rust: stable
godot: "3.5.1-stable"
postfix: ' (ptrcall)'
build_args: '--features ptrcall'
- rust: stable
godot: "3.5.1-stable"
postfix: ' (inventory)'
build_args: '--features inventory'
- rust: stable
godot: "3.5.1-stable"
postfix: ' (inventory)'
# Limiting no-manual-register tests to stable as to not slow down CI too far -- if inventory is
# working across all 3 Rust versions, this is likely to be as well.
build_args: '--features inventory,no-manual-register'
- rust: nightly
godot: "3.5.1-stable"
postfix: ' (nightly)'
- rust: nightly
godot: "3.5.1-stable"
postfix: ' (nightly, ptrcall)'
build_args: '--features ptrcall'
- rust: nightly
godot: "3.5.1-stable"
postfix: ' (nightly, inventory)'
build_args: '--features inventory'
- rust: '1.70'
godot: "3.5.1-stable"
postfix: ' (msrv 1.70)'
- rust: '1.70'
godot: "3.5.1-stable"
postfix: ' (msrv 1.70, ptrcall)'
build_args: '--features ptrcall'
- rust: '1.70'
godot: "3.5.1-stable"
postfix: ' (msrv 1.70, inventory)'
build_args: '--features inventory'
# Test with oldest supported engine version
- rust: stable
godot: "3.2-stable"
postfix: ''
build_args: '--features custom-godot'
- rust: stable
godot: "3.2-stable"
postfix: ' (ptrcall)'
build_args: '--features custom-godot,ptrcall'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Run Godot integration test"
uses: ./.github/composite/godot
with:
rust_toolchain: ${{ matrix.rust }}
rust_extra_args: ${{ matrix.build_args }}
godot_ver: ${{ matrix.godot }}
# Job to notify merge queue about success/failure. Named the same as the one in minimal-ci.
# It is always run, even on failure, to not wait until timeouts happen.
#
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
ci-status:
# Check for 'merge_group' not strictly necessary, but helpful when adding add-hoc `push:` trigger to `on:` for testing branch.
if: always() && github.event_name == 'merge_group'
needs:
- rustfmt
- clippy
- cargo-deny-machete
- test
- build-release
- build-ios
- build-android
- integration-test-godot
runs-on: ubuntu-latest
steps:
- name: "Success"
if: ${{ !(contains(needs.*.result, 'failure')) }}
run: exit 0
- name: "Failure"
if: ${{ contains(needs.*.result, 'failure') }}
run: exit 1