From a3585ae261c4e95a75a78871c52159ce8885f4b9 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 14:01:49 -0700 Subject: [PATCH 1/8] Shard smoketests using nextest --- .../download-build-artifacts/action.yml | 27 ++ .github/workflows/ci.yml | 275 +++++++++++++++--- crates/guard/src/lib.rs | 7 +- crates/smoketests/src/lib.rs | 6 + .../tests/smoketests/default_module_clippy.rs | 11 +- .../smoketests/tests/smoketests/namespaces.rs | 13 +- .../tests/smoketests/permissions.rs | 12 +- tools/ci/src/smoketest.rs | 72 ++++- 8 files changed, 350 insertions(+), 73 deletions(-) create mode 100644 .github/actions/download-build-artifacts/action.yml diff --git a/.github/actions/download-build-artifacts/action.yml b/.github/actions/download-build-artifacts/action.yml new file mode 100644 index 00000000000..9add1d7860c --- /dev/null +++ b/.github/actions/download-build-artifacts/action.yml @@ -0,0 +1,27 @@ +name: Download build artifacts +description: Download the prebuilt CLI and standalone binary shared by smoketest jobs. + +inputs: + artifact-suffix: + description: Platform suffix used by the build artifact. + required: true + +runs: + using: composite + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: build-artifacts-${{ inputs.artifact-suffix }} + path: ${{ runner.temp }}/build-artifacts + + - name: Extract build artifacts + shell: bash + run: | + tar -xzf "${RUNNER_TEMP}/build-artifacts/build-support.tar.gz" + exe_suffix="" + if [[ "${RUNNER_OS}" == "Windows" ]]; then + exe_suffix=".exe" + fi + test -f "${CARGO_TARGET_DIR}/release/spacetimedb-cli${exe_suffix}" + test -f "${CARGO_TARGET_DIR}/release/spacetimedb-standalone${exe_suffix}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c39b4a2458e..595d3778b00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,23 +70,113 @@ jobs: echo "Merge queue commit ${GITHUB_SHA} differs from PR #${pr_number} head ${pr_head_sha}; running CI normally." fi - smoketests: + upload-build-artifacts: needs: [merge_queue_noop, lints] if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} - name: Smoketests (${{ matrix.name }}) + name: Upload build artifacts (${{ matrix.name }}) strategy: + fail-fast: false matrix: include: - name: Linux runner: spacetimedb-new-runner-2 + artifact_suffix: linux - name: Windows runner: spacetimedb-windows-runner + artifact_suffix: windows runs-on: ${{ matrix.runner }} - timeout-minutes: 120 + timeout-minutes: 75 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + steps: + - name: Find Git ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + PR_NUMBER="${{ github.event.inputs.pr_number || null }}" + if test -n "${PR_NUMBER}"; then + GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" + else + GIT_REF="${{ github.ref }}" + fi + echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ env.GIT_REF }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: ${{ github.workspace }} + shared-key: spacetimedb + cache-on-failure: false + cache-all-crates: true + cache-workspace-crates: true + prefix-key: v1 + + - name: Build smoketest binaries + run: | + cargo build --timings --release \ + -p spacetimedb-cli \ + -p spacetimedb-standalone \ + --features spacetimedb-standalone/allow_loopback_http_for_tests + + - name: Package build artifacts + shell: bash + run: | + exe_suffix="" + if [[ "${RUNNER_OS}" == "Windows" ]]; then + exe_suffix=".exe" + fi + tar -czf build-support.tar.gz \ + "target/release/spacetimedb-cli${exe_suffix}" \ + "target/release/spacetimedb-standalone${exe_suffix}" + + - name: Upload Cargo timing reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-artifacts-cargo-timings-${{ matrix.artifact_suffix }} + path: ${{ github.workspace }}/target/cargo-timings/ + if-no-files-found: warn + overwrite: true + retention-days: 14 + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-artifacts-${{ matrix.artifact_suffix }} + path: build-support.tar.gz + if-no-files-found: error + overwrite: true + retention-days: 14 + + smoketest_build: + needs: [upload-build-artifacts] + name: Build smoketests (${{ matrix.name }}) + strategy: + fail-fast: false + matrix: + include: + - name: Linux + runner: spacetimedb-new-runner-2 + artifact_suffix: linux + - name: Windows + runner: spacetimedb-windows-runner + artifact_suffix: windows + runs-on: ${{ matrix.runner }} + timeout-minutes: 75 env: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full - SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp steps: - name: Find Git ref env: @@ -120,6 +210,117 @@ jobs: cache-workspace-crates: true prefix-key: v1 + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + + - name: Build smoketest dependencies and archive test binaries + shell: bash + run: | + cargo ci smoketests archive --archive-file smoketest-nextest.tar.zst + + shopt -s nullglob + precompiled_modules=(target/wasm32-unknown-unknown/release/smoketest_module_*.wasm) + if (( ${#precompiled_modules[@]} == 0 )); then + echo "No precompiled smoketest modules were produced." + exit 1 + fi + + exe_suffix="" + if [[ "${RUNNER_OS}" == "Windows" ]]; then + exe_suffix=".exe" + fi + tar -czf smoketest-support.tar.gz \ + "target/debug/ci${exe_suffix}" \ + "${precompiled_modules[@]}" + + - name: Upload Cargo timing reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: smoketest-build-cargo-timings-${{ matrix.artifact_suffix }} + path: ${{ github.workspace }}/target/cargo-timings/ + if-no-files-found: warn + overwrite: true + retention-days: 14 + + - name: Upload smoketest build + uses: actions/upload-artifact@v4 + with: + name: smoketest-build-${{ matrix.artifact_suffix }} + path: | + smoketest-nextest.tar.zst + smoketest-support.tar.gz + if-no-files-found: error + overwrite: true + retention-days: 14 + + smoketest_partitions: + needs: [smoketest_build] + name: Smoketests (${{ matrix.name }}) + strategy: + fail-fast: false + matrix: + include: + - name: Linux 1/2 + runner: spacetimedb-new-runner-2 + artifact_suffix: linux + partition: 1 + partition_count: 2 + - name: Linux 2/2 + runner: spacetimedb-new-runner-2 + artifact_suffix: linux + partition: 2 + partition_count: 2 + - name: Windows 1/4 + runner: spacetimedb-windows-runner + artifact_suffix: windows + partition: 1 + partition_count: 4 + - name: Windows 2/4 + runner: spacetimedb-windows-runner + artifact_suffix: windows + partition: 2 + partition_count: 4 + - name: Windows 3/4 + runner: spacetimedb-windows-runner + artifact_suffix: windows + partition: 3 + partition_count: 4 + - name: Windows 4/4 + runner: spacetimedb-windows-runner + artifact_suffix: windows + partition: 4 + partition_count: 4 + runs-on: ${{ matrix.runner }} + timeout-minutes: 120 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp + SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} + steps: + - name: Find Git ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + PR_NUMBER="${{ github.event.inputs.pr_number || null }}" + if test -n "${PR_NUMBER}"; then + GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" + else + GIT_REF="${{ github.ref }}" + fi + echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ env.GIT_REF }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + - uses: actions/setup-dotnet@v4 with: global-json-file: global.json @@ -191,53 +392,47 @@ jobs: cd sdks/csharp ./tools~/write-nuget-config.sh ../.. - # This step shouldn't be needed, but somehow we end up with caches that are missing librusty_v8.a. - # ChatGPT suspects that this could be due to different build invocations using the same target dir, - # and this makes sense to me because we only see it in this job where we mix `cargo build -p` with - # `cargo build --manifest-path` (which apparently build different dependency trees). - # However, we've been unable to fix it so... /shrug - - name: Check v8 outputs - shell: bash - run: | - find "${CARGO_TARGET_DIR}"/ -type f | grep '[/_]v8' || true - if ! [ -f "${CARGO_TARGET_DIR}"/release/gn_out/obj/librusty_v8.a ]; then - echo "Could not find v8 output file librusty_v8.a; rebuilding manually." - cargo clean --release -p v8 || true - cargo build --release -p v8 - fi - - name: Install cargo-nextest uses: taiki-e/install-action@nextest - # --test-threads=1 eliminates contention in the C# tests where they fight over bindings - # build artifacts. - # It also seemed to improve performance a fair amount (11m -> 6m) - - name: Run smoketests (Linux) + - uses: ./.github/actions/download-build-artifacts + with: + artifact-suffix: ${{ matrix.artifact_suffix }} + + - name: Download smoketest build + uses: actions/download-artifact@v4 + with: + name: smoketest-build-${{ matrix.artifact_suffix }} + + - name: Extract smoketest support files + shell: bash + run: tar -xzf smoketest-support.tar.gz + + # Serial execution avoids contention between the C# tests over generated bindings. + - name: Run smoketest partition (Linux) if: runner.os == 'Linux' shell: bash run: | if [ -f ~/emsdk/emsdk_env.sh ]; then source ~/emsdk/emsdk_env.sh fi - cargo ci smoketests -- --test-threads=1 + ./target/debug/ci smoketests run-archive \ + --archive-file smoketest-nextest.tar.zst \ + -- \ + --partition hash:${{ matrix.partition }}/${{ matrix.partition_count }} - # Due to Emscripten PATH issues this was separated to make sure OpenSSL still builds correctly - - name: Run smoketests (Windows) + # Due to Emscripten PATH issues this remains separate from the Linux command. + - name: Run smoketest partition (Windows) if: runner.os == 'Windows' shell: pwsh run: | if (Test-Path "$env:USERPROFILE\emsdk\emsdk_env.ps1") { & "$env:USERPROFILE\emsdk\emsdk_env.ps1" | Out-Null } - cargo ci smoketests -- --test-threads=1 - - - name: Upload Cargo timing reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: cargo-timings-smoketests-${{ matrix.name }} - path: ${{ github.workspace }}/target/cargo-timings/ - retention-days: 30 + .\target\debug\ci.exe smoketests run-archive ` + --archive-file smoketest-nextest.tar.zst ` + -- ` + --partition hash:${{ matrix.partition }}/${{ matrix.partition_count }} # this is a no-op version of the above check with a trivially-passing body. # we can't just let the check be entirely skipped because each matrix target is a required check, @@ -249,8 +444,12 @@ jobs: strategy: matrix: include: - - name: Linux - - name: Windows + - name: Linux 1/2 + - name: Linux 2/2 + - name: Windows 1/4 + - name: Windows 2/4 + - name: Windows 3/4 + - name: Windows 4/4 runs-on: ubuntu-latest steps: - name: Skip duplicate merge queue smoketest @@ -451,7 +650,7 @@ jobs: with: name: cargo-timings-public-lints path: ${{ github.workspace }}/target/cargo-timings/ - retention-days: 30 + retention-days: 14 codeowners_check: if: ${{ github.event_name == 'pull_request' }} diff --git a/crates/guard/src/lib.rs b/crates/guard/src/lib.rs index eead324c936..32da928bab4 100644 --- a/crates/guard/src/lib.rs +++ b/crates/guard/src/lib.rs @@ -22,8 +22,13 @@ fn next_spawn_id() -> u64 { } /// Returns the workspace root directory. -// TODO: Should this use something like `git rev-parse --show-toplevel` to avoid being directory-relative? Or perhaps `CARGO_WORKSPACE_DIR` is set? fn workspace_root() -> PathBuf { + // Archived tests run in a different checkout from the build job. CI supplies + // the runtime root; local Cargo runs continue to use the compile-time path. + if let Some(root) = env::var_os("SPACETIMEDB_WORKSPACE_ROOT") { + return root.into(); + } + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest_dir .parent() // crates/ diff --git a/crates/smoketests/src/lib.rs b/crates/smoketests/src/lib.rs index 5b90de38a03..d5fd600c367 100644 --- a/crates/smoketests/src/lib.rs +++ b/crates/smoketests/src/lib.rs @@ -181,6 +181,12 @@ macro_rules! timed { /// Returns the workspace root directory. pub fn workspace_root() -> PathBuf { + // Archived tests run in a different checkout from the build job. CI supplies + // the runtime root; local Cargo runs continue to use the compile-time path. + if let Some(root) = env::var_os("SPACETIMEDB_WORKSPACE_ROOT") { + return root.into(); + } + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest_dir .parent() diff --git a/crates/smoketests/tests/smoketests/default_module_clippy.rs b/crates/smoketests/tests/smoketests/default_module_clippy.rs index f32b0fd94e2..73e76f40357 100644 --- a/crates/smoketests/tests/smoketests/default_module_clippy.rs +++ b/crates/smoketests/tests/smoketests/default_module_clippy.rs @@ -1,17 +1,8 @@ //! These tests verify that the Rust module templates have no clippy warnings. -use std::path::PathBuf; +use spacetimedb_smoketests::workspace_root; use std::process::Command; -fn workspace_root() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .parent() - .unwrap() - .to_path_buf() -} - /// Run clippy on a template's spacetimedb module directory. /// Both templates use workspace dependencies, so they can be checked in place. fn check_template_clippy(template_name: &str) { diff --git a/crates/smoketests/tests/smoketests/namespaces.rs b/crates/smoketests/tests/smoketests/namespaces.rs index 3d632edd7e2..971de3ae027 100644 --- a/crates/smoketests/tests/smoketests/namespaces.rs +++ b/crates/smoketests/tests/smoketests/namespaces.rs @@ -1,15 +1,6 @@ -use spacetimedb_smoketests::Smoketest; +use spacetimedb_smoketests::{workspace_root, Smoketest}; use std::fs; -use std::path::{Path, PathBuf}; - -fn workspace_root() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .parent() - .unwrap() - .to_path_buf() -} +use std::path::Path; /// Count occurrences of a needle string in all .cs files under a directory fn count_matches(dir: &Path, needle: &str) -> usize { diff --git a/crates/smoketests/tests/smoketests/permissions.rs b/crates/smoketests/tests/smoketests/permissions.rs index 35a9f8a7c37..36e0b0dcd1e 100644 --- a/crates/smoketests/tests/smoketests/permissions.rs +++ b/crates/smoketests/tests/smoketests/permissions.rs @@ -1,14 +1,4 @@ -use spacetimedb_smoketests::Smoketest; -use std::path::PathBuf; - -fn workspace_root() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .parent() - .unwrap() - .to_path_buf() -} +use spacetimedb_smoketests::{workspace_root, Smoketest}; /// Ensure that anyone has the permission to call any standard reducer #[test] diff --git a/tools/ci/src/smoketest.rs b/tools/ci/src/smoketest.rs index 2a35b91ca59..ef19e30eba4 100644 --- a/tools/ci/src/smoketest.rs +++ b/tools/ci/src/smoketest.rs @@ -4,7 +4,7 @@ use clap::{Args, Subcommand}; use duct::cmd; use spacetimedb_guard::ensure_binaries_built; use std::ffi::OsStr; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::{env, fs}; use tempfile::TempDir; @@ -46,11 +46,27 @@ pub struct SmoketestsArgs { #[derive(Subcommand)] enum SmoketestCmd { - /// Only build binaries without running tests + /// Local helper: only build binaries without running tests. /// /// Use this before running `cargo test --all` to ensure binaries are built. Prepare, CheckModList, + + /// CI build job: build dependencies and archive the smoketest binaries. + Archive { + #[arg(long)] + archive_file: PathBuf, + }, + + /// CI partition job: run smoketests from an existing nextest archive. + RunArchive { + #[arg(long)] + archive_file: PathBuf, + + /// Additional arguments to pass to nextest. + #[arg(trailing_var_arg = true)] + args: Vec, + }, } pub fn run(args: SmoketestsArgs) -> Result<()> { @@ -66,6 +82,8 @@ pub fn run(args: SmoketestsArgs) -> Result<()> { eprintln!("smoketests/mod.rs is up to date."); Ok(()) } + Some(SmoketestCmd::Archive { archive_file }) => archive_smoketests(&archive_file), + Some(SmoketestCmd::RunArchive { archive_file, args }) => run_smoketest_archive(&archive_file, args), None => run_smoketest(args.server, args.dotnet, args.auth_host.as_deref(), args.args), } } @@ -142,6 +160,56 @@ fn build_precompiled_modules() -> Result<()> { Ok(()) } +fn archive_smoketests(archive_file: &Path) -> Result<()> { + build_precompiled_modules()?; + + let status = Command::new("cargo") + .args([ + "nextest", + "archive", + "--release", + "--timings", + "-p", + "spacetimedb-smoketests", + "--archive-file", + ]) + .arg(archive_file) + .status()?; + ensure!(status.success(), "Failed to archive smoketests"); + Ok(()) +} + +fn run_smoketest_archive(archive_file: &Path, args: Vec) -> Result<()> { + let workspace_root = env::current_dir()?; + let archive_file = if archive_file.is_absolute() { + archive_file.to_path_buf() + } else { + workspace_root.join(archive_file) + }; + + // CI supplies the release CLI and standalone through the shared build artifact. + let cli_path = ensure_binaries_built(); + let base_config_dir = prepare_base_config(&cli_path, None, None)?; + let base_config_path = base_config_dir.path().join("config.toml"); + + let mut cmd = Command::new("cargo"); + set_env(&mut cmd, None, true, false, &base_config_path); + cmd.args(["nextest", "run", "--archive-file"]) + .arg(archive_file) + .args(["--workspace-remap"]) + .arg(&workspace_root) + .args(["--no-fail-fast", "--no-tests", "pass", "-j", "1"]) + .args(args); + + ensure!(cmd.status()?.success(), "Tests failed"); + let diff_status = cmd!("bash", "tools/check-diff.sh", "crates/smoketests").run()?; + ensure!( + diff_status.status.success(), + "There is a diff in the smoketests directory." + ); + Ok(()) +} + /// Default parallelism for smoketests. /// 16 was found to be optimal - higher values cause OS scheduler overhead. const DEFAULT_PARALLELISM: &str = "16"; From d8474d6d9b8c871a7481b7c3d31f8be24a6f8475 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 14:39:13 -0700 Subject: [PATCH 2/8] fixes --- .github/workflows/ci.yml | 12 ++++++++++++ tools/ci/README.md | 29 +++++++++++++++++++++++++++-- tools/ci/src/smoketest.rs | 3 +++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 595d3778b00..783997f4046 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,19 @@ jobs: cache-workspace-crates: true prefix-key: v1 + # Some runner caches contain V8 metadata without the corresponding static library. + - name: Check v8 outputs + shell: bash + run: | + find "${CARGO_TARGET_DIR}"/ -type f | grep '[/_]v8' || true + if ! [ -f "${CARGO_TARGET_DIR}"/release/gn_out/obj/librusty_v8.a ]; then + echo "Could not find v8 output file librusty_v8.a; rebuilding manually." + cargo clean --release -p v8 || true + cargo build --timings --release -p v8 + fi + - name: Build smoketest binaries + shell: bash run: | cargo build --timings --release \ -p spacetimedb-cli \ diff --git a/tools/ci/README.md b/tools/ci/README.md index f0563733ee2..c20d264469d 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -108,13 +108,13 @@ This is required for servers that reject direct server-issued logins for privile Optionally accepts an auth host to pass through to `spacetime login`, for example `--auth-host=https://spacetimedb.com`. -- `--dotnet `: +- `--dotnet `: Run .NET smoketests - `args `: Additional arguments to pass to the test runner - `--help`: Print help (see a summary with '-h') #### `prepare` -Only build binaries without running tests +Local helper: only build binaries without running tests. Use this before running `cargo test --all` to ensure binaries are built. @@ -138,6 +138,31 @@ Usage: check-mod-list - `--help`: Print help +#### `archive` + +**Usage:** +```bash +Usage: archive --archive-file +``` + +**Options:** + +- `--archive-file `: Path to the nextest archive to create +- `--help`: Print help + +#### `run-archive` + +**Usage:** +```bash +Usage: run-archive --archive-file [ARGS]... +``` + +**Options:** + +- `--archive-file `: Path to the nextest archive to run +- `args `: Additional arguments to pass to nextest +- `--help`: Print help + #### `help` **Usage:** diff --git a/tools/ci/src/smoketest.rs b/tools/ci/src/smoketest.rs index ef19e30eba4..90648b69eb0 100644 --- a/tools/ci/src/smoketest.rs +++ b/tools/ci/src/smoketest.rs @@ -36,6 +36,7 @@ pub struct SmoketestsArgs { #[arg(long, num_args = 0..=1, require_equals = true, default_missing_value = "")] auth_host: Option, + /// Run .NET smoketests. #[arg(long, default_value_t = true, action = clap::ArgAction::Set)] dotnet: bool, @@ -54,12 +55,14 @@ enum SmoketestCmd { /// CI build job: build dependencies and archive the smoketest binaries. Archive { + /// Path to the nextest archive to create. #[arg(long)] archive_file: PathBuf, }, /// CI partition job: run smoketests from an existing nextest archive. RunArchive { + /// Path to the nextest archive to run. #[arg(long)] archive_file: PathBuf, From d1fa946a586fb2308759d1e4e5bf31f50e9b9f93 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 15:29:37 -0700 Subject: [PATCH 3/8] Independent linux and windows build chains --- .github/workflows/ci.yml | 377 +++++++++++++++++++++++++++++---------- 1 file changed, 279 insertions(+), 98 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 783997f4046..f9c896a3912 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,21 +70,11 @@ jobs: echo "Merge queue commit ${GITHUB_SHA} differs from PR #${pr_number} head ${pr_head_sha}; running CI normally." fi - upload-build-artifacts: + upload-build-artifacts-linux: needs: [merge_queue_noop, lints] if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} - name: Upload build artifacts (${{ matrix.name }}) - strategy: - fail-fast: false - matrix: - include: - - name: Linux - runner: spacetimedb-new-runner-2 - artifact_suffix: linux - - name: Windows - runner: spacetimedb-windows-runner - artifact_suffix: windows - runs-on: ${{ matrix.runner }} + name: Upload build artifacts (Linux) + runs-on: spacetimedb-new-runner-2 timeout-minutes: 75 env: CARGO_TARGET_DIR: ${{ github.workspace }}/target @@ -144,19 +134,102 @@ jobs: - name: Package build artifacts shell: bash run: | - exe_suffix="" - if [[ "${RUNNER_OS}" == "Windows" ]]; then - exe_suffix=".exe" + tar -czf build-support.tar.gz \ + target/release/spacetimedb-cli \ + target/release/spacetimedb-standalone + + - name: Upload Cargo timing reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: build-artifacts-cargo-timings-linux + path: ${{ github.workspace }}/target/cargo-timings/ + if-no-files-found: warn + overwrite: true + retention-days: 14 + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-artifacts-linux + path: build-support.tar.gz + if-no-files-found: error + overwrite: true + retention-days: 14 + + upload-build-artifacts-windows: + needs: [merge_queue_noop, lints] + if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} + name: Upload build artifacts (Windows) + runs-on: spacetimedb-windows-runner + timeout-minutes: 75 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + steps: + - name: Find Git ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + PR_NUMBER="${{ github.event.inputs.pr_number || null }}" + if test -n "${PR_NUMBER}"; then + GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" + else + GIT_REF="${{ github.ref }}" fi + echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ env.GIT_REF }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: ${{ github.workspace }} + shared-key: spacetimedb + cache-on-failure: false + cache-all-crates: true + cache-workspace-crates: true + prefix-key: v1 + + # Some runner caches contain V8 metadata without the corresponding static library. + - name: Check v8 outputs + shell: bash + run: | + find "${CARGO_TARGET_DIR}"/ -type f | grep '[/_]v8' || true + if ! [ -f "${CARGO_TARGET_DIR}"/release/gn_out/obj/librusty_v8.a ]; then + echo "Could not find v8 output file librusty_v8.a; rebuilding manually." + cargo clean --release -p v8 || true + cargo build --timings --release -p v8 + fi + + - name: Build smoketest binaries + shell: bash + run: | + cargo build --timings --release \ + -p spacetimedb-cli \ + -p spacetimedb-standalone \ + --features spacetimedb-standalone/allow_loopback_http_for_tests + + - name: Package build artifacts + shell: bash + run: | tar -czf build-support.tar.gz \ - "target/release/spacetimedb-cli${exe_suffix}" \ - "target/release/spacetimedb-standalone${exe_suffix}" + target/release/spacetimedb-cli.exe \ + target/release/spacetimedb-standalone.exe - name: Upload Cargo timing reports if: always() uses: actions/upload-artifact@v4 with: - name: build-artifacts-cargo-timings-${{ matrix.artifact_suffix }} + name: build-artifacts-cargo-timings-windows path: ${{ github.workspace }}/target/cargo-timings/ if-no-files-found: warn overwrite: true @@ -165,26 +238,16 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: build-artifacts-${{ matrix.artifact_suffix }} + name: build-artifacts-windows path: build-support.tar.gz if-no-files-found: error overwrite: true retention-days: 14 - smoketest_build: - needs: [upload-build-artifacts] - name: Build smoketests (${{ matrix.name }}) - strategy: - fail-fast: false - matrix: - include: - - name: Linux - runner: spacetimedb-new-runner-2 - artifact_suffix: linux - - name: Windows - runner: spacetimedb-windows-runner - artifact_suffix: windows - runs-on: ${{ matrix.runner }} + smoketest_build_linux: + needs: [upload-build-artifacts-linux] + name: Build smoketests (Linux) + runs-on: spacetimedb-new-runner-2 timeout-minutes: 75 env: CARGO_TARGET_DIR: ${{ github.workspace }}/target @@ -237,19 +300,96 @@ jobs: exit 1 fi - exe_suffix="" - if [[ "${RUNNER_OS}" == "Windows" ]]; then - exe_suffix=".exe" + tar -czf smoketest-support.tar.gz \ + target/debug/ci \ + "${precompiled_modules[@]}" + + - name: Upload Cargo timing reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: smoketest-build-cargo-timings-linux + path: ${{ github.workspace }}/target/cargo-timings/ + if-no-files-found: warn + overwrite: true + retention-days: 14 + + - name: Upload smoketest build + uses: actions/upload-artifact@v4 + with: + name: smoketest-build-linux + path: | + smoketest-nextest.tar.zst + smoketest-support.tar.gz + if-no-files-found: error + overwrite: true + retention-days: 14 + + smoketest_build_windows: + needs: [upload-build-artifacts-windows] + name: Build smoketests (Windows) + runs-on: spacetimedb-windows-runner + timeout-minutes: 75 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + steps: + - name: Find Git ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + PR_NUMBER="${{ github.event.inputs.pr_number || null }}" + if test -n "${PR_NUMBER}"; then + GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" + else + GIT_REF="${{ github.ref }}" + fi + echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ env.GIT_REF }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@v2 + with: + workspaces: ${{ github.workspace }} + shared-key: spacetimedb + cache-on-failure: false + cache-all-crates: true + cache-workspace-crates: true + prefix-key: v1 + + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + + - name: Build smoketest dependencies and archive test binaries + shell: bash + run: | + cargo ci smoketests archive --archive-file smoketest-nextest.tar.zst + + shopt -s nullglob + precompiled_modules=(target/wasm32-unknown-unknown/release/smoketest_module_*.wasm) + if (( ${#precompiled_modules[@]} == 0 )); then + echo "No precompiled smoketest modules were produced." + exit 1 fi + tar -czf smoketest-support.tar.gz \ - "target/debug/ci${exe_suffix}" \ + target/debug/ci.exe \ "${precompiled_modules[@]}" - name: Upload Cargo timing reports if: always() uses: actions/upload-artifact@v4 with: - name: smoketest-build-cargo-timings-${{ matrix.artifact_suffix }} + name: smoketest-build-cargo-timings-windows path: ${{ github.workspace }}/target/cargo-timings/ if-no-files-found: warn overwrite: true @@ -258,7 +398,7 @@ jobs: - name: Upload smoketest build uses: actions/upload-artifact@v4 with: - name: smoketest-build-${{ matrix.artifact_suffix }} + name: smoketest-build-windows path: | smoketest-nextest.tar.zst smoketest-support.tar.gz @@ -266,44 +406,14 @@ jobs: overwrite: true retention-days: 14 - smoketest_partitions: - needs: [smoketest_build] - name: Smoketests (${{ matrix.name }}) + smoketest_partitions_linux: + needs: [smoketest_build_linux] + name: Smoketests (Linux ${{ matrix.partition }}/2) strategy: fail-fast: false matrix: - include: - - name: Linux 1/2 - runner: spacetimedb-new-runner-2 - artifact_suffix: linux - partition: 1 - partition_count: 2 - - name: Linux 2/2 - runner: spacetimedb-new-runner-2 - artifact_suffix: linux - partition: 2 - partition_count: 2 - - name: Windows 1/4 - runner: spacetimedb-windows-runner - artifact_suffix: windows - partition: 1 - partition_count: 4 - - name: Windows 2/4 - runner: spacetimedb-windows-runner - artifact_suffix: windows - partition: 2 - partition_count: 4 - - name: Windows 3/4 - runner: spacetimedb-windows-runner - artifact_suffix: windows - partition: 3 - partition_count: 4 - - name: Windows 4/4 - runner: spacetimedb-windows-runner - artifact_suffix: windows - partition: 4 - partition_count: 4 - runs-on: ${{ matrix.runner }} + partition: [1, 2] + runs-on: spacetimedb-new-runner-2 timeout-minutes: 120 env: CARGO_TARGET_DIR: ${{ github.workspace }}/target @@ -348,8 +458,7 @@ jobs: run_install: true # Install emscripten for C++ module compilation tests. - - name: Install emscripten (Linux) - if: runner.os == 'Linux' + - name: Install emscripten shell: bash run: | git clone https://github.com/emscripten-core/emsdk.git ~/emsdk @@ -357,8 +466,95 @@ jobs: ./emsdk install 4.0.21 ./emsdk activate 4.0.21 + - name: Override NuGet packages + shell: bash + run: | + dotnet pack -c Release crates/bindings-csharp/BSATN.Runtime + dotnet pack -c Release crates/bindings-csharp/Runtime + cd sdks/csharp + ./tools~/write-nuget-config.sh ../.. + + - name: Install cargo-nextest + uses: taiki-e/install-action@nextest + + - uses: ./.github/actions/download-build-artifacts + with: + artifact-suffix: linux + + - name: Download smoketest build + uses: actions/download-artifact@v4 + with: + name: smoketest-build-linux + + - name: Extract smoketest support files + shell: bash + run: tar -xzf smoketest-support.tar.gz + + # Serial execution avoids contention between the C# tests over generated bindings. + - name: Run smoketest partition + shell: bash + run: | + if [ -f ~/emsdk/emsdk_env.sh ]; then + source ~/emsdk/emsdk_env.sh + fi + ./target/debug/ci smoketests run-archive \ + --archive-file smoketest-nextest.tar.zst \ + -- \ + --partition hash:${{ matrix.partition }}/2 + + smoketest_partitions_windows: + needs: [smoketest_build_windows] + name: Smoketests (Windows ${{ matrix.partition }}/4) + strategy: + fail-fast: false + matrix: + partition: [1, 2, 3, 4] + runs-on: spacetimedb-windows-runner + timeout-minutes: 120 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp + SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} + steps: + - name: Find Git ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + PR_NUMBER="${{ github.event.inputs.pr_number || null }}" + if test -n "${PR_NUMBER}"; then + GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" + else + GIT_REF="${{ github.ref }}" + fi + echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + + - name: Checkout sources + uses: actions/checkout@v4 + with: + ref: ${{ env.GIT_REF }} + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + # nodejs and pnpm are required for the typescript quickstart smoketest + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - uses: ./.github/actions/setup-pnpm + with: + run_install: true + + # Install emscripten for C++ module compilation tests. - name: Install emscripten (Windows) - if: runner.os == 'Windows' shell: pwsh run: | git clone https://github.com/emscripten-core/emsdk.git $env:USERPROFILE\emsdk @@ -366,8 +562,7 @@ jobs: .\emsdk install 4.0.21 .\emsdk activate 4.0.21 - - name: Install psql (Windows) - if: runner.os == 'Windows' + - name: Install psql shell: pwsh run: | # Fail properly if any individual command fails @@ -379,7 +574,6 @@ jobs: Get-Command psql - name: Update dotnet workloads - if: runner.os == 'Windows' run: | # Fail properly if any individual command fails $ErrorActionPreference = 'Stop' @@ -409,33 +603,20 @@ jobs: - uses: ./.github/actions/download-build-artifacts with: - artifact-suffix: ${{ matrix.artifact_suffix }} + artifact-suffix: windows - name: Download smoketest build uses: actions/download-artifact@v4 with: - name: smoketest-build-${{ matrix.artifact_suffix }} + name: smoketest-build-windows - name: Extract smoketest support files shell: bash run: tar -xzf smoketest-support.tar.gz # Serial execution avoids contention between the C# tests over generated bindings. - - name: Run smoketest partition (Linux) - if: runner.os == 'Linux' - shell: bash - run: | - if [ -f ~/emsdk/emsdk_env.sh ]; then - source ~/emsdk/emsdk_env.sh - fi - ./target/debug/ci smoketests run-archive \ - --archive-file smoketest-nextest.tar.zst \ - -- \ - --partition hash:${{ matrix.partition }}/${{ matrix.partition_count }} - # Due to Emscripten PATH issues this remains separate from the Linux command. - - name: Run smoketest partition (Windows) - if: runner.os == 'Windows' + - name: Run smoketest partition shell: pwsh run: | if (Test-Path "$env:USERPROFILE\emsdk\emsdk_env.ps1") { @@ -444,7 +625,7 @@ jobs: .\target\debug\ci.exe smoketests run-archive ` --archive-file smoketest-nextest.tar.zst ` -- ` - --partition hash:${{ matrix.partition }}/${{ matrix.partition_count }} + --partition hash:${{ matrix.partition }}/4 # this is a no-op version of the above check with a trivially-passing body. # we can't just let the check be entirely skipped because each matrix target is a required check, From b4f6ff2cd2c157a4f6f220e906e6d19e54964730 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 15:42:42 -0700 Subject: [PATCH 4/8] Anchors --- .github/workflows/ci.yml | 315 ++++++++------------------------------- 1 file changed, 63 insertions(+), 252 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9c896a3912..69c9adc5696 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,9 @@ jobs: env: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full - steps: + ARTIFACT_SUFFIX: linux + EXE_SUFFIX: "" + steps: &upload-build-artifact-steps - name: Find Git ref env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -135,14 +137,14 @@ jobs: shell: bash run: | tar -czf build-support.tar.gz \ - target/release/spacetimedb-cli \ - target/release/spacetimedb-standalone + "target/release/spacetimedb-cli${EXE_SUFFIX}" \ + "target/release/spacetimedb-standalone${EXE_SUFFIX}" - name: Upload Cargo timing reports if: always() uses: actions/upload-artifact@v4 with: - name: build-artifacts-cargo-timings-linux + name: build-artifacts-cargo-timings-${{ env.ARTIFACT_SUFFIX }} path: ${{ github.workspace }}/target/cargo-timings/ if-no-files-found: warn overwrite: true @@ -151,7 +153,7 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: build-artifacts-linux + name: build-artifacts-${{ env.ARTIFACT_SUFFIX }} path: build-support.tar.gz if-no-files-found: error overwrite: true @@ -166,83 +168,9 @@ jobs: env: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full - steps: - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - PR_NUMBER="${{ github.event.inputs.pr_number || null }}" - if test -n "${PR_NUMBER}"; then - GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" - else - GIT_REF="${{ github.ref }}" - fi - echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" - - - name: Checkout sources - uses: actions/checkout@v4 - with: - ref: ${{ env.GIT_REF }} - - - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - cache-on-failure: false - cache-all-crates: true - cache-workspace-crates: true - prefix-key: v1 - - # Some runner caches contain V8 metadata without the corresponding static library. - - name: Check v8 outputs - shell: bash - run: | - find "${CARGO_TARGET_DIR}"/ -type f | grep '[/_]v8' || true - if ! [ -f "${CARGO_TARGET_DIR}"/release/gn_out/obj/librusty_v8.a ]; then - echo "Could not find v8 output file librusty_v8.a; rebuilding manually." - cargo clean --release -p v8 || true - cargo build --timings --release -p v8 - fi - - - name: Build smoketest binaries - shell: bash - run: | - cargo build --timings --release \ - -p spacetimedb-cli \ - -p spacetimedb-standalone \ - --features spacetimedb-standalone/allow_loopback_http_for_tests - - - name: Package build artifacts - shell: bash - run: | - tar -czf build-support.tar.gz \ - target/release/spacetimedb-cli.exe \ - target/release/spacetimedb-standalone.exe - - - name: Upload Cargo timing reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: build-artifacts-cargo-timings-windows - path: ${{ github.workspace }}/target/cargo-timings/ - if-no-files-found: warn - overwrite: true - retention-days: 14 - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-artifacts-windows - path: build-support.tar.gz - if-no-files-found: error - overwrite: true - retention-days: 14 + ARTIFACT_SUFFIX: windows + EXE_SUFFIX: .exe + steps: *upload-build-artifact-steps smoketest_build_linux: needs: [upload-build-artifacts-linux] @@ -252,7 +180,9 @@ jobs: env: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full - steps: + ARTIFACT_SUFFIX: linux + EXE_SUFFIX: "" + steps: &smoketest-build-steps - name: Find Git ref env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -301,14 +231,14 @@ jobs: fi tar -czf smoketest-support.tar.gz \ - target/debug/ci \ + "target/debug/ci${EXE_SUFFIX}" \ "${precompiled_modules[@]}" - name: Upload Cargo timing reports if: always() uses: actions/upload-artifact@v4 with: - name: smoketest-build-cargo-timings-linux + name: smoketest-build-cargo-timings-${{ env.ARTIFACT_SUFFIX }} path: ${{ github.workspace }}/target/cargo-timings/ if-no-files-found: warn overwrite: true @@ -317,7 +247,7 @@ jobs: - name: Upload smoketest build uses: actions/upload-artifact@v4 with: - name: smoketest-build-linux + name: smoketest-build-${{ env.ARTIFACT_SUFFIX }} path: | smoketest-nextest.tar.zst smoketest-support.tar.gz @@ -333,78 +263,9 @@ jobs: env: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full - steps: - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - PR_NUMBER="${{ github.event.inputs.pr_number || null }}" - if test -n "${PR_NUMBER}"; then - GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" - else - GIT_REF="${{ github.ref }}" - fi - echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" - - - name: Checkout sources - uses: actions/checkout@v4 - with: - ref: ${{ env.GIT_REF }} - - - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - cache-on-failure: false - cache-all-crates: true - cache-workspace-crates: true - prefix-key: v1 - - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - - - name: Build smoketest dependencies and archive test binaries - shell: bash - run: | - cargo ci smoketests archive --archive-file smoketest-nextest.tar.zst - - shopt -s nullglob - precompiled_modules=(target/wasm32-unknown-unknown/release/smoketest_module_*.wasm) - if (( ${#precompiled_modules[@]} == 0 )); then - echo "No precompiled smoketest modules were produced." - exit 1 - fi - - tar -czf smoketest-support.tar.gz \ - target/debug/ci.exe \ - "${precompiled_modules[@]}" - - - name: Upload Cargo timing reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: smoketest-build-cargo-timings-windows - path: ${{ github.workspace }}/target/cargo-timings/ - if-no-files-found: warn - overwrite: true - retention-days: 14 - - - name: Upload smoketest build - uses: actions/upload-artifact@v4 - with: - name: smoketest-build-windows - path: | - smoketest-nextest.tar.zst - smoketest-support.tar.gz - if-no-files-found: error - overwrite: true - retention-days: 14 + ARTIFACT_SUFFIX: windows + EXE_SUFFIX: .exe + steps: *smoketest-build-steps smoketest_partitions_linux: needs: [smoketest_build_linux] @@ -420,7 +281,9 @@ jobs: RUST_BACKTRACE: full SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} - steps: + ARTIFACT_SUFFIX: linux + PARTITION_COUNT: 2 + steps: &smoketest-partition-steps - name: Find Git ref env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -458,7 +321,8 @@ jobs: run_install: true # Install emscripten for C++ module compilation tests. - - name: Install emscripten + - name: Install emscripten (Linux) + if: runner.os == 'Linux' shell: bash run: | git clone https://github.com/emscripten-core/emsdk.git ~/emsdk @@ -466,95 +330,8 @@ jobs: ./emsdk install 4.0.21 ./emsdk activate 4.0.21 - - name: Override NuGet packages - shell: bash - run: | - dotnet pack -c Release crates/bindings-csharp/BSATN.Runtime - dotnet pack -c Release crates/bindings-csharp/Runtime - cd sdks/csharp - ./tools~/write-nuget-config.sh ../.. - - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - - - uses: ./.github/actions/download-build-artifacts - with: - artifact-suffix: linux - - - name: Download smoketest build - uses: actions/download-artifact@v4 - with: - name: smoketest-build-linux - - - name: Extract smoketest support files - shell: bash - run: tar -xzf smoketest-support.tar.gz - - # Serial execution avoids contention between the C# tests over generated bindings. - - name: Run smoketest partition - shell: bash - run: | - if [ -f ~/emsdk/emsdk_env.sh ]; then - source ~/emsdk/emsdk_env.sh - fi - ./target/debug/ci smoketests run-archive \ - --archive-file smoketest-nextest.tar.zst \ - -- \ - --partition hash:${{ matrix.partition }}/2 - - smoketest_partitions_windows: - needs: [smoketest_build_windows] - name: Smoketests (Windows ${{ matrix.partition }}/4) - strategy: - fail-fast: false - matrix: - partition: [1, 2, 3, 4] - runs-on: spacetimedb-windows-runner - timeout-minutes: 120 - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - RUST_BACKTRACE: full - SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp - SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} - steps: - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - PR_NUMBER="${{ github.event.inputs.pr_number || null }}" - if test -n "${PR_NUMBER}"; then - GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" - else - GIT_REF="${{ github.ref }}" - fi - echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" - - - name: Checkout sources - uses: actions/checkout@v4 - with: - ref: ${{ env.GIT_REF }} - - - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - - - uses: actions/setup-dotnet@v4 - with: - global-json-file: global.json - - # nodejs and pnpm are required for the typescript quickstart smoketest - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: ./.github/actions/setup-pnpm - with: - run_install: true - - # Install emscripten for C++ module compilation tests. - name: Install emscripten (Windows) + if: runner.os == 'Windows' shell: pwsh run: | git clone https://github.com/emscripten-core/emsdk.git $env:USERPROFILE\emsdk @@ -563,6 +340,7 @@ jobs: .\emsdk activate 4.0.21 - name: Install psql + if: runner.os == 'Windows' shell: pwsh run: | # Fail properly if any individual command fails @@ -574,6 +352,8 @@ jobs: Get-Command psql - name: Update dotnet workloads + if: runner.os == 'Windows' + shell: pwsh run: | # Fail properly if any individual command fails $ErrorActionPreference = 'Stop' @@ -603,20 +383,33 @@ jobs: - uses: ./.github/actions/download-build-artifacts with: - artifact-suffix: windows + artifact-suffix: ${{ env.ARTIFACT_SUFFIX }} - name: Download smoketest build uses: actions/download-artifact@v4 with: - name: smoketest-build-windows + name: smoketest-build-${{ env.ARTIFACT_SUFFIX }} - name: Extract smoketest support files shell: bash run: tar -xzf smoketest-support.tar.gz # Serial execution avoids contention between the C# tests over generated bindings. + - name: Run smoketest partition (Linux) + if: runner.os == 'Linux' + shell: bash + run: | + if [ -f ~/emsdk/emsdk_env.sh ]; then + source ~/emsdk/emsdk_env.sh + fi + ./target/debug/ci smoketests run-archive \ + --archive-file smoketest-nextest.tar.zst \ + -- \ + --partition hash:${{ matrix.partition }}/${{ env.PARTITION_COUNT }} + # Due to Emscripten PATH issues this remains separate from the Linux command. - - name: Run smoketest partition + - name: Run smoketest partition (Windows) + if: runner.os == 'Windows' shell: pwsh run: | if (Test-Path "$env:USERPROFILE\emsdk\emsdk_env.ps1") { @@ -625,7 +418,25 @@ jobs: .\target\debug\ci.exe smoketests run-archive ` --archive-file smoketest-nextest.tar.zst ` -- ` - --partition hash:${{ matrix.partition }}/4 + --partition hash:${{ matrix.partition }}/${{ env.PARTITION_COUNT }} + + smoketest_partitions_windows: + needs: [smoketest_build_windows] + name: Smoketests (Windows ${{ matrix.partition }}/4) + strategy: + fail-fast: false + matrix: + partition: [1, 2, 3, 4] + runs-on: spacetimedb-windows-runner + timeout-minutes: 120 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp + SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} + ARTIFACT_SUFFIX: windows + PARTITION_COUNT: 4 + steps: *smoketest-partition-steps # this is a no-op version of the above check with a trivially-passing body. # we can't just let the check be entirely skipped because each matrix target is a required check, From b0f4cef5db9e5b8d733c8d937f7e2de5084d8668 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 16:25:20 -0700 Subject: [PATCH 5/8] Inline artifact download --- .../download-build-artifacts/action.yml | 27 ------ .github/workflows/ci.yml | 83 +++++++------------ crates/guard/src/lib.rs | 6 -- tools/ci/src/smoketest.rs | 1 + 4 files changed, 31 insertions(+), 86 deletions(-) delete mode 100644 .github/actions/download-build-artifacts/action.yml diff --git a/.github/actions/download-build-artifacts/action.yml b/.github/actions/download-build-artifacts/action.yml deleted file mode 100644 index 9add1d7860c..00000000000 --- a/.github/actions/download-build-artifacts/action.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Download build artifacts -description: Download the prebuilt CLI and standalone binary shared by smoketest jobs. - -inputs: - artifact-suffix: - description: Platform suffix used by the build artifact. - required: true - -runs: - using: composite - steps: - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: build-artifacts-${{ inputs.artifact-suffix }} - path: ${{ runner.temp }}/build-artifacts - - - name: Extract build artifacts - shell: bash - run: | - tar -xzf "${RUNNER_TEMP}/build-artifacts/build-support.tar.gz" - exe_suffix="" - if [[ "${RUNNER_OS}" == "Windows" ]]; then - exe_suffix=".exe" - fi - test -f "${CARGO_TARGET_DIR}/release/spacetimedb-cli${exe_suffix}" - test -f "${CARGO_TARGET_DIR}/release/spacetimedb-standalone${exe_suffix}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69c9adc5696..5f4d40a62c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,7 +82,8 @@ jobs: ARTIFACT_SUFFIX: linux EXE_SUFFIX: "" steps: &upload-build-artifact-steps - - name: Find Git ref + - &find-git-ref + name: Find Git ref env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} shell: bash @@ -95,16 +96,19 @@ jobs: fi echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" - - name: Checkout sources + - &checkout-sources + name: Checkout sources uses: actions/checkout@v4 with: ref: ${{ env.GIT_REF }} - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain + - &set-default-rust-toolchain + name: Set default rust toolchain run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - - name: Cache Rust dependencies + - &cache-rust-dependencies + name: Cache Rust dependencies uses: Swatinem/rust-cache@v2 with: workspaces: ${{ github.workspace }} @@ -183,37 +187,14 @@ jobs: ARTIFACT_SUFFIX: linux EXE_SUFFIX: "" steps: &smoketest-build-steps - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - PR_NUMBER="${{ github.event.inputs.pr_number || null }}" - if test -n "${PR_NUMBER}"; then - GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" - else - GIT_REF="${{ github.ref }}" - fi - echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + - *find-git-ref - - name: Checkout sources - uses: actions/checkout@v4 - with: - ref: ${{ env.GIT_REF }} + - *checkout-sources - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + - *set-default-rust-toolchain - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - cache-on-failure: false - cache-all-crates: true - cache-workspace-crates: true - prefix-key: v1 + - *cache-rust-dependencies - name: Install cargo-nextest uses: taiki-e/install-action@nextest @@ -280,31 +261,15 @@ jobs: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp - SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} ARTIFACT_SUFFIX: linux PARTITION_COUNT: 2 steps: &smoketest-partition-steps - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - PR_NUMBER="${{ github.event.inputs.pr_number || null }}" - if test -n "${PR_NUMBER}"; then - GIT_REF="$( gh pr view --repo clockworklabs/SpacetimeDB $PR_NUMBER --json headRefName --jq .headRefName )" - else - GIT_REF="${{ github.ref }}" - fi - echo "GIT_REF=${GIT_REF}" >>"$GITHUB_ENV" + - *find-git-ref - - name: Checkout sources - uses: actions/checkout@v4 - with: - ref: ${{ env.GIT_REF }} + - *checkout-sources - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + - *set-default-rust-toolchain - uses: actions/setup-dotnet@v4 with: @@ -381,9 +346,22 @@ jobs: - name: Install cargo-nextest uses: taiki-e/install-action@nextest - - uses: ./.github/actions/download-build-artifacts + - name: Download build artifacts + uses: actions/download-artifact@v4 with: - artifact-suffix: ${{ env.ARTIFACT_SUFFIX }} + name: build-artifacts-${{ env.ARTIFACT_SUFFIX }} + path: ${{ runner.temp }}/build-artifacts + + - name: Extract build artifacts + shell: bash + run: | + tar -xzf "${RUNNER_TEMP}/build-artifacts/build-support.tar.gz" + exe_suffix="" + if [[ "${RUNNER_OS}" == "Windows" ]]; then + exe_suffix=".exe" + fi + test -f "${CARGO_TARGET_DIR}/release/spacetimedb-cli${exe_suffix}" + test -f "${CARGO_TARGET_DIR}/release/spacetimedb-standalone${exe_suffix}" - name: Download smoketest build uses: actions/download-artifact@v4 @@ -433,7 +411,6 @@ jobs: CARGO_TARGET_DIR: ${{ github.workspace }}/target RUST_BACKTRACE: full SPACETIMEDB_CPP_DIR: ${{ github.workspace }}/crates/bindings-cpp - SPACETIMEDB_WORKSPACE_ROOT: ${{ github.workspace }} ARTIFACT_SUFFIX: windows PARTITION_COUNT: 4 steps: *smoketest-partition-steps diff --git a/crates/guard/src/lib.rs b/crates/guard/src/lib.rs index 32da928bab4..84d5e589497 100644 --- a/crates/guard/src/lib.rs +++ b/crates/guard/src/lib.rs @@ -23,12 +23,6 @@ fn next_spawn_id() -> u64 { /// Returns the workspace root directory. fn workspace_root() -> PathBuf { - // Archived tests run in a different checkout from the build job. CI supplies - // the runtime root; local Cargo runs continue to use the compile-time path. - if let Some(root) = env::var_os("SPACETIMEDB_WORKSPACE_ROOT") { - return root.into(); - } - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest_dir .parent() // crates/ diff --git a/tools/ci/src/smoketest.rs b/tools/ci/src/smoketest.rs index 90648b69eb0..6f621acb124 100644 --- a/tools/ci/src/smoketest.rs +++ b/tools/ci/src/smoketest.rs @@ -197,6 +197,7 @@ fn run_smoketest_archive(archive_file: &Path, args: Vec) -> Result<()> { let mut cmd = Command::new("cargo"); set_env(&mut cmd, None, true, false, &base_config_path); + cmd.env("SPACETIMEDB_WORKSPACE_ROOT", &workspace_root); cmd.args(["nextest", "run", "--archive-file"]) .arg(archive_file) .args(["--workspace-remap"]) From fc52a270ab0e8388c51450366757f36fc92effd0 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Sun, 9 Aug 2026 21:28:26 -0700 Subject: [PATCH 6/8] fix artifact download path --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f4d40a62c9..ff9ec6242d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -350,12 +350,12 @@ jobs: uses: actions/download-artifact@v4 with: name: build-artifacts-${{ env.ARTIFACT_SUFFIX }} - path: ${{ runner.temp }}/build-artifacts + path: build-artifacts - name: Extract build artifacts shell: bash run: | - tar -xzf "${RUNNER_TEMP}/build-artifacts/build-support.tar.gz" + tar -xzf build-artifacts/build-support.tar.gz exe_suffix="" if [[ "${RUNNER_OS}" == "Windows" ]]; then exe_suffix=".exe" From 7c6f3fecd7c599362c688c542ea2e0e904640047 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Mon, 10 Aug 2026 16:17:21 -0700 Subject: [PATCH 7/8] Remove SPACETIMEDB_WORKSPACE_ROOT --- crates/smoketests/src/lib.rs | 7 ------- tools/ci/src/smoketest.rs | 1 - 2 files changed, 8 deletions(-) diff --git a/crates/smoketests/src/lib.rs b/crates/smoketests/src/lib.rs index d5fd600c367..5c90ae988d2 100644 --- a/crates/smoketests/src/lib.rs +++ b/crates/smoketests/src/lib.rs @@ -56,7 +56,6 @@ pub mod modules; use anyhow::{bail, Context, Result}; use regex::Regex; use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; -use std::env; use std::fs; use std::io::{BufRead, BufReader}; use std::path::{Path, PathBuf}; @@ -181,12 +180,6 @@ macro_rules! timed { /// Returns the workspace root directory. pub fn workspace_root() -> PathBuf { - // Archived tests run in a different checkout from the build job. CI supplies - // the runtime root; local Cargo runs continue to use the compile-time path. - if let Some(root) = env::var_os("SPACETIMEDB_WORKSPACE_ROOT") { - return root.into(); - } - let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest_dir .parent() diff --git a/tools/ci/src/smoketest.rs b/tools/ci/src/smoketest.rs index 6f621acb124..90648b69eb0 100644 --- a/tools/ci/src/smoketest.rs +++ b/tools/ci/src/smoketest.rs @@ -197,7 +197,6 @@ fn run_smoketest_archive(archive_file: &Path, args: Vec) -> Result<()> { let mut cmd = Command::new("cargo"); set_env(&mut cmd, None, true, false, &base_config_path); - cmd.env("SPACETIMEDB_WORKSPACE_ROOT", &workspace_root); cmd.args(["nextest", "run", "--archive-file"]) .arg(archive_file) .args(["--workspace-remap"]) From adaad6d735009e6ef492fae9bc10a9d6876d5bb5 Mon Sep 17 00:00:00 2001 From: joshua-spacetime Date: Mon, 10 Aug 2026 16:34:08 -0700 Subject: [PATCH 8/8] fix comment --- crates/guard/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/guard/src/lib.rs b/crates/guard/src/lib.rs index 84d5e589497..eead324c936 100644 --- a/crates/guard/src/lib.rs +++ b/crates/guard/src/lib.rs @@ -22,6 +22,7 @@ fn next_spawn_id() -> u64 { } /// Returns the workspace root directory. +// TODO: Should this use something like `git rev-parse --show-toplevel` to avoid being directory-relative? Or perhaps `CARGO_WORKSPACE_DIR` is set? fn workspace_root() -> PathBuf { let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); manifest_dir