Speed up the Build workflow

- Pass the MariaDB client library to mysqlclient-sys through its environment variables, so a cached build no longer recompiles mysqlclient-sys, diesel and diesel_migrations in every MySQL build.
- Split each channel into two parallel jobs, keeping the required check names through a result job per channel.
- Drop the sqlite,mysql,postgresql,enable_mimalloc test: enable_mimalloc is still tested on and off.
- Only save caches on main, so pull requests and branches no longer push main's caches out of the 10 GB limit.
This commit is contained in:
Daniel García committed 2026-11-13 21:39:48 +01:00
1 parent 258b7f0254
commit 840dcae418
1 file changed
+95 -29
+95 -29
View File
@@ -40,24 +40,53 @@ defaults:
jobs:
build:
name: Build and Test ${{ matrix.channel }}
name: Build and Test ${{ matrix.channel }} (part ${{ matrix.part }})
runs-on: ubuntu-24.04
timeout-minutes: 120
# Make warnings errors, this is to prevent warnings slipping through.
# This is done globally to prevent rebuilds when the RUSTFLAGS env variable changes.
env:
RUSTFLAGS: "-Dwarnings"
# Which parts passed, read by the `result` job below. Matrix jobs combine their outputs.
outputs:
rust-toolchain-1: ${{ steps.passed.outputs.rust-toolchain-1 }}
rust-toolchain-2: ${{ steps.passed.outputs.rust-toolchain-2 }}
msrv-1: ${{ steps.passed.outputs.msrv-1 }}
msrv-2: ${{ steps.passed.outputs.msrv-2 }}
strategy:
fail-fast: false
matrix:
channel:
- "rust-toolchain" # The version defined in rust-toolchain
- "msrv" # The supported MSRV
# Each step builds the whole vaultwarden crate again, so the steps are split over two jobs of
# about the same length that run in parallel.
part:
- 1 # Several backends together, and the lints
- 2 # One backend at a time
steps:
# Install dependencies
- name: "Install dependencies Ubuntu"
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends openssl build-essential libmariadb-dev-compat libpq-dev libssl-dev pkg-config
run: |
# Separate commands, so a failing update fails the step. The image already has Oracle's MySQL client
# library, so a skipped install would otherwise go unnoticed and build against that one.
sudo apt-get update
sudo apt-get install -y --no-install-recommends openssl build-essential libmariadb-dev-compat libpq-dev libssl-dev pkg-config
# Pass the MariaDB client library to mysqlclient-sys through its environment variables.
# When it finds the library with pkg-config, its build script makes cargo rerun it whenever
# anything in the library directory changes, and the install above changes that directory on
# every run. So every MySQL build recompiled mysqlclient-sys, diesel and diesel_migrations,
# even with a cached build. Cargo compares environment variables by their value instead.
lib_dir="$(pkg-config --variable=libdir mysqlclient)"
version="$(pkg-config --modversion mysqlclient)"
{
echo "MYSQLCLIENT_NO_PKG_CONFIG=1"
echo "LIBMARIADB_NO_PKG_CONFIG=1"
echo "MYSQLCLIENT_LIB_DIR=${lib_dir}"
echo "MYSQLCLIENT_VERSION=${version}"
} | tee -a "${GITHUB_ENV}"
# End Install dependencies
# Checkout the repo
@@ -119,50 +148,50 @@ jobs:
# Like changing the build host from Ubuntu 20.04 to 22.04 for example.
# Only update when really needed! Use a <year>.<month>[.<inc>] format.
prefix-key: "v2025.09-rust"
# The two parts build the dependencies with different features, so each has its own cache.
key: part-${{ matrix.part }}
# Only main saves caches. The repository can keep 10 GB of caches, and the ones saved by pull
# requests and other branches were mostly copies of main's that pushed other caches out.
save-if: ${{ github.ref == 'refs/heads/main' }}
# End Enable Rust Caching
# Run cargo tests
# First test all features together, afterwards test them separately.
# Part 1 tests several features together, part 2 tests each database backend on its own.
# enable_mimalloc only selects the allocator, so it is tested on and off, not with every combination.
- name: "test features: sqlite,mysql,postgresql,enable_mimalloc,s3"
id: test_sqlite_mysql_postgresql_mimalloc_s3
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 1 }}
run: |
cargo test --profile ci --features sqlite,mysql,postgresql,enable_mimalloc,s3
- name: "test features: sqlite,mysql,postgresql,enable_mimalloc"
id: test_sqlite_mysql_postgresql_mimalloc
if: ${{ !cancelled() }}
run: |
cargo test --profile ci --features sqlite,mysql,postgresql,enable_mimalloc
- name: "test features: sqlite,mysql,postgresql"
id: test_sqlite_mysql_postgresql
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 1 }}
run: |
cargo test --profile ci --features sqlite,mysql,postgresql
- name: "test features: sqlite"
id: test_sqlite
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 2 }}
run: |
cargo test --profile ci --features sqlite
- name: "test features: mysql"
id: test_mysql
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 2 }}
run: |
cargo test --profile ci --features mysql
- name: "test features: postgresql"
id: test_postgresql
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 2 }}
run: |
cargo test --profile ci --features postgresql
# The test seeder only exists with this feature, which no release build has
- name: "test features: sqlite,unsafe_test_mode"
id: test_unsafe_test_mode
if: ${{ !cancelled() }}
if: ${{ !cancelled() && matrix.part == 2 }}
run: |
cargo test --profile ci --features sqlite,unsafe_test_mode
# End Run cargo tests
@@ -171,13 +200,13 @@ jobs:
# Run cargo clippy, and fail on warnings
- name: "clippy features: sqlite,mysql,postgresql,enable_mimalloc,s3"
id: clippy
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' }}
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' && matrix.part == 1 }}
run: |
cargo clippy --profile ci --features sqlite,mysql,postgresql,enable_mimalloc,s3
- name: "clippy features: sqlite,unsafe_test_mode"
id: clippy_unsafe_test_mode
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' }}
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' && matrix.part == 1 }}
run: |
cargo clippy --profile ci --features sqlite,unsafe_test_mode
# End Run cargo clippy
@@ -186,7 +215,7 @@ jobs:
# Run cargo fmt (Only run on rust-toolchain defined version)
- name: "check formatting"
id: formatting
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' }}
if: ${{ !cancelled() && matrix.channel == 'rust-toolchain' && matrix.part == 1 }}
run: |
cargo fmt --all -- --check
# End Run cargo fmt
@@ -198,7 +227,6 @@ jobs:
if: ${{ failure() }}
env:
TEST_DB_M_S3: ${{ steps.test_sqlite_mysql_postgresql_mimalloc_s3.outcome }}
TEST_DB_M: ${{ steps.test_sqlite_mysql_postgresql_mimalloc.outcome }}
TEST_DB: ${{ steps.test_sqlite_mysql_postgresql.outcome }}
TEST_SQLITE: ${{ steps.test_sqlite.outcome }}
TEST_MYSQL: ${{ steps.test_mysql.outcome }}
@@ -208,20 +236,25 @@ jobs:
CLIPPY_UNSAFE_TEST_MODE: ${{ steps.clippy_unsafe_test_mode.outcome }}
FMT: ${{ steps.formatting.outcome }}
run: |
# Only list the checks this part runs
row() {
if [[ "${2}" != "skipped" ]]; then
echo "|${1}|${2}|" >> "${GITHUB_STEP_SUMMARY}"
fi
}
echo "### :x: Checks Failed!" >> "${GITHUB_STEP_SUMMARY}"
echo "" >> "${GITHUB_STEP_SUMMARY}"
echo "|Job|Status|" >> "${GITHUB_STEP_SUMMARY}"
echo "|---|------|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (sqlite,mysql,postgresql,enable_mimalloc,s3)|${TEST_DB_M_S3}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (sqlite,mysql,postgresql,enable_mimalloc)|${TEST_DB_M}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (sqlite,mysql,postgresql)|${TEST_DB}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (sqlite)|${TEST_SQLITE}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (mysql)|${TEST_MYSQL}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (postgresql)|${TEST_POSTGRESQL}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|test (sqlite,unsafe_test_mode)|${TEST_UNSAFE_TEST_MODE}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|clippy (sqlite,mysql,postgresql,enable_mimalloc,s3)|${CLIPPY}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|clippy (sqlite,unsafe_test_mode)|${CLIPPY_UNSAFE_TEST_MODE}|" >> "${GITHUB_STEP_SUMMARY}"
echo "|fmt|${FMT}|" >> "${GITHUB_STEP_SUMMARY}"
row "test (sqlite,mysql,postgresql,enable_mimalloc,s3)" "${TEST_DB_M_S3}"
row "test (sqlite,mysql,postgresql)" "${TEST_DB}"
row "test (sqlite)" "${TEST_SQLITE}"
row "test (mysql)" "${TEST_MYSQL}"
row "test (postgresql)" "${TEST_POSTGRESQL}"
row "test (sqlite,unsafe_test_mode)" "${TEST_UNSAFE_TEST_MODE}"
row "clippy (sqlite,mysql,postgresql,enable_mimalloc,s3)" "${CLIPPY}"
row "clippy (sqlite,unsafe_test_mode)" "${CLIPPY_UNSAFE_TEST_MODE}"
row "fmt" "${FMT}"
echo "" >> "${GITHUB_STEP_SUMMARY}"
echo "Please check the failed jobs and fix where needed." >> "${GITHUB_STEP_SUMMARY}"
echo "" >> "${GITHUB_STEP_SUMMARY}"
@@ -231,7 +264,40 @@ jobs:
# Check for any previous failures, if there are stop, else continue.
# This is useful so all test/clippy/fmt actions are done, and they can all be addressed
- name: "All checks passed"
id: passed
if: ${{ success() }}
env:
PART: ${{ matrix.channel }}-${{ matrix.part }}
run: |
echo "### :tada: Checks Passed!" >> "${GITHUB_STEP_SUMMARY}"
echo "" >> "${GITHUB_STEP_SUMMARY}"
echo "${PART}=success" >> "${GITHUB_OUTPUT}"
# The required status checks are named after these jobs, one per channel, so they stay the same
# however the build job is split. It runs even when a part failed or the run was cancelled, because
# a skipped job counts as passed for a required check.
result:
name: Build and Test ${{ matrix.channel }}
needs: build
if: ${{ always() }}
runs-on: ubuntu-24.04
timeout-minutes: 5
strategy:
fail-fast: false
matrix:
channel:
- "rust-toolchain"
- "msrv"
steps:
- name: "Check that both parts passed"
env:
CHANNEL: ${{ matrix.channel }}
PASSED: ${{ toJSON(needs.build.outputs) }}
run: |
for part in 1 2; do
if [[ "$(jq -r --arg part "${CHANNEL}-${part}" '.[$part] // ""' <<< "${PASSED}")" != "success" ]]; then
echo "::error::Build and Test ${CHANNEL} (part ${part}) did not pass"
exit 1
fi
done