Skip to content

Full CI

Full CI #1737

Workflow file for this run

# Copyright (c) godot-rust; Bromeon and contributors.
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
name: Full CI
#
# Runs before merging. Rebases on master to make sure CI passes for latest integration, not only for the PR at the time of creation.
on:
# Allow manually triggering the workflow:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch
workflow_dispatch:
merge_group:
# push:
env:
# Applies to all 'register-docs' features across crates.
CLIPPY_FEATURES: '--features register-docs'
TEST_FEATURES: ''
RETRY: ${{ github.workspace }}/.github/other/retry.sh
# ASan options: https://github.com/google/sanitizers/wiki/AddressSanitizerFlags
# LSan options: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer
# * report_objects: list individual leaked objects when running LeakSanitizer
LSAN_OPTIONS: report_objects=1
CARGO_DENY_VERSION: "0.16.1"
CARGO_MACHETE_VERSION: "0.7.0"
defaults:
run:
shell: bash
# If a new commit is pushed before the old one's CI has completed (on the same branch), abort previous run
#concurrency:
# group: ${{ github.head_ref }}
# cancel-in-progress: true
jobs:
# Keep all in sync with minimal-ci and release-version.
rustfmt:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
with:
components: rustfmt
- name: "Check rustfmt"
run: cargo fmt --all -- --check
- name: "Run custom repo checks"
run: |
cargo run -p repo-tweak
git diff --quiet --exit-code || {
echo "::error::Godot versions out of sync; update with `cargo run -p repo-tweak`."
echo "Differences:"
echo "----------------------------------------------------"
git diff
echo "----------------------------------------------------"
exit 1
}
# Needs to be its own job (apart from sync-doc), because lints don't work with --no-deps, and because it contributes to ci-status.
doc-lints:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Patch Cargo.toml to use nightly extension API"
run: .github/other/patch-prebuilt.sh nightly
# rustdoc is included as component.
- name: "Install Rust"
uses: ./.github/composite/rust
- name: "Check rustdoc"
env:
RUSTDOCFLAGS: >
-D rustdoc::broken-intra-doc-links -D rustdoc::private-intra-doc-links -D rustdoc::invalid-codeblock-attributes
-D rustdoc::invalid-rust-codeblocks -D rustdoc::invalid-html-tags -D rustdoc::bare-urls -D rustdoc::unescaped-backticks
run: cargo doc -p godot --ignore-rust-version
clippy:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Patch Cargo.toml to use nightly extension API"
run: .github/other/patch-prebuilt.sh nightly
- name: "Install Rust"
uses: ./.github/composite/rust
with:
components: clippy
# Note: could use `-- --no-deps` to not lint dependencies, however it doesn't really speed up and also skips deps in workspace.
- name: "Check clippy"
run: |
cargo clippy --all-targets $CLIPPY_FEATURES -- \
-D clippy::suspicious \
-D clippy::style \
-D clippy::complexity \
-D clippy::perf \
-D clippy::dbg_macro \
-D clippy::todo \
-D clippy::unimplemented \
-D warnings
unit-test:
name: unit-test (${{ matrix.name }}${{ matrix.rust-special }})
runs-on: ${{ matrix.os }}
continue-on-error: false
strategy:
fail-fast: false # cancel all jobs as soon as one fails?
matrix:
# Order this way because macOS typically has the 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.
include:
- name: macos
os: macos-latest # arm64
- name: windows
os: windows-latest
# Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility.
# If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM.
- name: linux
os: ubuntu-22.04
- name: linux
os: ubuntu-22.04
rust-toolchain: nightly
rust-special: -minimal-deps
rust-cache-key: minimal-deps
- name: linux
os: ubuntu-22.04
rust-toolchain: "1.80"
rust-special: -msrv
steps:
- uses: actions/checkout@v4
- name: "Patch Cargo.toml to use nightly extension API"
# Only on Linux because godot4-prebuilt/nightly branch doesn't have artifacts for other platforms.
if: matrix.name == 'linux' && matrix.rust-special == ''
run: .github/other/patch-prebuilt.sh nightly
- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: ${{ matrix.rust-toolchain || 'stable' }}
cache-key: ${{ matrix.rust-cache-key }} # only needed when rustc version is possibly the same
- name: "Install minimal dependency versions from Cargo"
if: matrix.rust-special == '-minimal-deps'
run: cargo +nightly update -Z minimal-versions
- name: "Compile tests"
run: cargo test $TEST_FEATURES --no-run ${{ matrix.rust-extra-args }}
- name: "Test"
run: cargo test $TEST_FEATURES ${{ matrix.rust-extra-args }}
miri-test:
name: miri-test
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
with:
rust: nightly
components: miri
- name: "Setup Miri"
run: cargo miri setup
- name: "Compile tests"
run: cargo miri test -p godot-cell --no-run
- name: "Test stacked borrows"
run: cargo miri test -p godot-cell
- name: "Test tree borrows"
run: MIRIFLAGS="-Zmiri-tree-borrows" cargo miri test -p godot-cell
proptest:
name: proptest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
- name: "Compile tests"
run: cargo test -p godot-cell --features="proptest" --no-run
- name: "Test"
run: cargo test -p godot-cell --features="proptest"
# For complex matrix workflow, see https://stackoverflow.com/a/65434401
godot-itest:
name: godot-itest (${{ matrix.name }})
runs-on: ${{ matrix.os }}
continue-on-error: false
timeout-minutes: 24
strategy:
fail-fast: false # cancel all jobs as soon as one fails?
matrix:
# Naming: {os}[-{runtimeVersion}]-{apiVersion}
# runtimeVersion = version of Godot binary; apiVersion = version of GDExtension API against which gdext is compiled.
# Config overview (see job for details).
#
# Cross-platform:
# - Base version: Godot nightly, no custom
# - Double precision: Godot nightly, custom (bindgen), double
# - 4.2 compat: Godot 4.2
#
# Linux-only:
# - Full: Godot nightly, full codegen
# - Double + lazy: Godot nightly, custom, double, lazy func tables
# - Features + exp: Godot nightly, custom, threads, serde, experimental API
# - Memcheck nightly: Godot mem nightly, custom, sanitizer
# - Memcheck 4.x: Godot mem 4.0/4.1, sanitizer
# Note: Windows uses '--target x86_64-pc-windows-msvc' by default as Cargo argument.
include:
# macOS -- for the unintuitive naming, see https://github.com/actions/runner-images?tab=readme-ov-file#available-images
- name: macos-x86
os: macos-13
artifact-name: macos-x86-nightly
godot-binary: godot.macos.editor.dev.x86_64
with-hot-reload: true
- name: macos-double-x86
os: macos-13
artifact-name: macos-double-x86-nightly
godot-binary: godot.macos.editor.dev.double.x86_64
rust-extra-args: --features godot/api-custom,godot/double-precision
- name: macos-x86-4.2
os: macos-13
artifact-name: macos-x86-4.2
godot-binary: godot.macos.editor.dev.x86_64
godot-prebuilt-patch: '4.2.2'
- name: macos-arm
os: macos-latest
artifact-name: macos-arm-nightly
godot-binary: godot.macos.editor.dev.arm64
with-hot-reload: true
# api-custom on macOS arm64 not working, due to clang linker issues.
# - name: macos-double-arm
# os: macos-latest
# artifact-name: macos-double-arm-nightly
# godot-binary: godot.macos.editor.dev.double.arm64
# rust-extra-args: --features godot/api-custom,godot/double-precision
- name: macos-arm-4.2
os: macos-latest
artifact-name: macos-arm-4.2
godot-binary: godot.macos.editor.dev.arm64
godot-prebuilt-patch: '4.2.2'
# Windows
- name: windows
os: windows-latest
artifact-name: windows-nightly
godot-binary: godot.windows.editor.dev.x86_64.exe
with-hot-reload: true
- name: windows-double
os: windows-latest
artifact-name: windows-double-nightly
godot-binary: godot.windows.editor.dev.double.x86_64.exe
rust-extra-args: --features godot/api-custom,godot/double-precision
- name: windows-4.2
os: windows-latest
artifact-name: windows-4.2
godot-binary: godot.windows.editor.dev.x86_64.exe
godot-prebuilt-patch: '4.2.2'
# - name: windows-4.1
# os: windows-latest
# artifact-name: windows-4.1
# godot-binary: godot.windows.editor.dev.x86_64.exe
# godot-prebuilt-patch: '4.1.3'
# Linux
# Don't use latest Ubuntu (22.04) as it breaks lots of ecosystem compatibility.
# If ever moving to ubuntu-latest, need to manually install libtinfo5 for LLVM.
# Uses full+experimental codegen, so that compatibility breakage towards nightly is detected.
# If the experimental part causes problems, consider using only godot/__codegen-full.
- name: linux-full
os: ubuntu-22.04
artifact-name: linux-nightly
godot-binary: godot.linuxbsd.editor.dev.x86_64
rust-extra-args: --features itest/codegen-full
with-hot-reload: true
# Combines now a lot of features, but should be OK. lazy-function-tables doesn't work with experimental-threads.
- name: linux-double-lazy
os: ubuntu-22.04
artifact-name: linux-double-nightly
godot-binary: godot.linuxbsd.editor.dev.double.x86_64
rust-extra-args: --features godot/api-custom,godot/double-precision,itest/codegen-full,godot/lazy-function-tables
- name: linux-features-experimental
os: ubuntu-22.04
artifact-name: linux-nightly
godot-binary: godot.linuxbsd.editor.dev.x86_64
rust-extra-args: --features itest/experimental-threads,itest/codegen-full-experimental,godot/api-custom,godot/serde,itest/register-docs
- name: linux-release
os: ubuntu-22.04
artifact-name: linux-release-nightly
godot-binary: godot.linuxbsd.template_release.x86_64
rust-extra-args: --release
rust-cache-key: release
# Linux compat (4.1 disabled, already covered by memcheck)
- name: linux-4.3
os: ubuntu-22.04
artifact-name: linux-4.3
godot-binary: godot.linuxbsd.editor.dev.x86_64
#godot-prebuilt-patch: '4.3.x'
- name: linux-4.2
os: ubuntu-22.04
artifact-name: linux-4.2
godot-binary: godot.linuxbsd.editor.dev.x86_64
godot-prebuilt-patch: '4.2.2'
# Memory checks: special Godot binaries compiled with AddressSanitizer/LeakSanitizer to detect UB/leaks.
# See also https://rustc-dev-guide.rust-lang.org/sanitizers.html.
#
# Additionally, the Godot source is patched to make dlclose() a no-op, as unloading dynamic libraries loses stacktrace and
# cause false positives like println!. See https://github.com/google/sanitizers/issues/89.
#
# There is also a gcc variant besides clang, which is currently not used.
- name: linux-memcheck-nightly
os: ubuntu-22.04
artifact-name: linux-memcheck-nightly
godot-binary: godot.linuxbsd.editor.dev.x86_64.llvm.san
rust-toolchain: nightly
rust-env-rustflags: -Zrandomize-layout -Zsanitizer=address
rust-extra-args: --features godot/api-custom
# Sanitizers can't build proc-macros and build scripts; with --target, cargo ignores RUSTFLAGS for those two.
rust-target: x86_64-unknown-linux-gnu
- name: linux-memcheck-4.1
os: ubuntu-22.04
artifact-name: linux-memcheck-4.1
godot-binary: godot.linuxbsd.editor.dev.x86_64.llvm.san
godot-prebuilt-patch: '4.1' # check compat of API 4.1.0 with newer binaries.
rust-toolchain: nightly
rust-env-rustflags: -Zrandomize-layout -Zsanitizer=address
# Sanitizers can't build proc-macros and build scripts; with --target, cargo ignores RUSTFLAGS for those two.
rust-target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v4
- name: "Run Godot integration test"
uses: ./.github/composite/godot-itest
with:
artifact-name: godot-${{ matrix.artifact-name }}
godot-binary: ${{ matrix.godot-binary }}
godot-args: ${{ matrix.godot-args }} # currently unused
godot-prebuilt-patch: ${{ matrix.godot-prebuilt-patch }}
rust-extra-args: ${{ matrix.rust-extra-args }}
rust-toolchain: ${{ matrix.rust-toolchain || 'stable' }}
rust-env-rustflags: ${{ matrix.rust-env-rustflags }}
rust-target: ${{ matrix.rust-target }}
rust-cache-key: ${{ matrix.rust-cache-key }}
with-llvm: ${{ contains(matrix.name, 'macos') && contains(matrix.rust-extra-args, 'api-custom') }}
godot-check-header: ${{ matrix.godot-check-header }}
- name: "Build and test hot-reload"
if: ${{ matrix.with-hot-reload }}
working-directory: examples/hot-reload/godot/test
# Repeat a few times, our hot reload integration test can sometimes be a bit flaky.
run: $RETRY ./run-test.sh
shell: bash
run-examples:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Install Rust"
uses: ./.github/composite/rust
# First compile, to fail early in case of compilation errors.
- name: "Compile Rust examples (release mode)"
run: cargo build --release -p dodge-the-creeps
- name: "Install Godot"
uses: ./.github/composite/godot-install
with:
artifact-name: godot-linux-4.3
godot-binary: godot.linuxbsd.editor.dev.x86_64
- name: "Run examples for short time"
run: ./.github/other/check-example.sh dodge-the-creeps
cargo-deny-machete:
runs-on: ubuntu-22.04
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 .github/other/deny.toml
# Machete
- name: "Install cargo-machete"
uses: baptiste0928/cargo-install@v3
with:
crate: cargo-machete
version: ${{ env.CARGO_MACHETE_VERSION }}
- name: "Use machete to cut down dependencies"
run: cargo machete
license-guard:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: "Check license headers"
uses: 'apache/skywalking-eyes/[email protected]'
with:
# log: debug # optional: set the log level. The default value is `info`.
config: .github/other/licenserc.yml
# token: # optional: the token that license eye uses when it needs to comment on the pull request.
# Set to empty ("") to disable commenting on pull request. The default value is ${{ github.token }}
# mode: # optional: Which mode License-Eye should be run in. Choices are `check` or `fix`. The default value is `check`.
mode: check
# ---------------------------------------------------------------------------------------------------------------------------------------------
# CI status report
# Job to notify merge queue about success/failure
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
- doc-lints
- clippy
- unit-test
- miri-test
- proptest
- godot-itest
- run-examples
- cargo-deny-machete
- license-guard
runs-on: ubuntu-22.04
steps:
# Deliberate choice to use bash script and not GitHub Action glob syntax, as that is not well-documented and hard to get right.
# For example: contains(needs.*.result, 'success') does NOT work because * is a logical OR, thus true if a single job succeeds.
- name: "Determine success or failure"
run: |
DEPENDENCIES='${{ toJson(needs) }}'
echo "Dependency jobs:"
all_success=true
for job in $(echo "$DEPENDENCIES" | jq -r 'keys[]'); do
status=$(echo "$DEPENDENCIES" | jq -r ".[\"$job\"].result")
echo "* $job -> $status"
if [[ "$status" != "success" ]]; then
all_success=false
fi
done
if [[ "$all_success" == "false" ]]; then
echo "One or more dependency jobs failed or were cancelled."
exit 1
fi