From 4824f5f1a2676f49234d0a7eded20ea41c515cc8 Mon Sep 17 00:00:00 2001 From: Kasama Date: Mon, 13 Oct 2025 14:16:50 -0300 Subject: [PATCH 001/160] add initial cargo ci --- .cargo/config.toml | 1 + .github/workflows/ci.yml | 85 ++++--------------- Cargo.toml | 1 + tools/ci/Cargo.toml | 12 +++ tools/ci/build.rs | 4 + tools/ci/src/main.rs | 172 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 69 deletions(-) create mode 100644 tools/ci/Cargo.toml create mode 100644 tools/ci/build.rs create mode 100644 tools/ci/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 7a3e270ebbd..fea26873991 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,3 +3,4 @@ rustflags = ["--cfg", "tokio_unstable"] [alias] bump-versions = "run -p upgrade-version --" +ci = "run -p ci --" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aa33bcf152c..949477af421 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: include: - { runner: spacetimedb-runner, smoketest_args: --docker } - { runner: windows-latest, smoketest_args: --no-build-cli } - runner: [ spacetimedb-runner, windows-latest ] + runner: [spacetimedb-runner, windows-latest] runs-on: ${{ matrix.runner }} steps: - name: Find Git ref @@ -76,8 +76,8 @@ jobs: - name: Install psycopg2 run: python -m pip install psycopg2-binary - name: Run smoketests - # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication + run: cargo ci smoketests -- ${{ matrix.smoketest_args }} + - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose down @@ -109,26 +109,9 @@ jobs: with: global-json-file: global.json - - name: Create /stdb dir - run: | - sudo mkdir /stdb - sudo chmod 777 /stdb - - name: Run cargo test #Note: Unreal tests will be run separately - run: cargo test --all -- --skip unreal - - - name: Check that the test outputs are up-to-date - run: bash tools/check-diff.sh - - - name: Ensure C# autogen bindings are up-to-date - run: | - cargo run -p spacetimedb-codegen --example regen-csharp-moduledef - bash tools/check-diff.sh crates/bindings-csharp - - - name: C# bindings tests - working-directory: crates/bindings-csharp - run: dotnet test -warnaserror + run: cargo ci test lints: name: Lints @@ -145,29 +128,7 @@ jobs: global-json-file: global.json - name: Run cargo fmt - run: cargo fmt --all -- --check - - - name: Run cargo clippy - run: cargo clippy --all --tests --benches -- -D warnings - - - name: Run C# formatting check - working-directory: crates/bindings-csharp - run: | - dotnet tool restore - dotnet csharpier --check . - - - name: Run `cargo doc` for bindings crate - # `bindings` is the only crate we care strongly about documenting, - # since we link to its docs.rs from our website. - # We won't pass `--no-deps`, though, - # since we want everything reachable through it to also work. - # This includes `sats` and `lib`. - working-directory: crates/bindings - env: - # Make `cargo doc` exit with error on warnings, most notably broken links - RUSTDOCFLAGS: '--deny warnings' - run: | - cargo doc + run: cargo ci lint wasm_bindings: name: Build and test wasm bindings @@ -179,20 +140,7 @@ jobs: - run: echo ::add-matcher::.github/workflows/rust_matcher.json - name: Run bindgen tests - run: cargo test -p spacetimedb-codegen - - # Make sure the `Cargo.lock` file reflects the latest available versions. - # This is what users would end up with on a fresh module, so we want to - # catch any compile errors arising from a different transitive closure - # of dependencies than what is in the workspace lock file. - # - # For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714 - - name: Update dependencies - run: cargo update - - - name: Build module-test - run: cargo run -p spacetimedb-cli -- build --project-path modules/module-test - + run: cargo ci wasm-bindings publish_checks: name: Check that packages are publishable @@ -245,19 +193,9 @@ jobs: run: sudo apt install -y libssl-dev - name: Build spacetimedb-update - run: cargo build --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update - - - name: Run self-install env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - ROOT_DIR="$(mktemp -d)" - # NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. - # My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it - # happens very frequently on the `macos-runner`, but we haven't seen it on any others). - cargo run --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update -- self-install --root-dir="${ROOT_DIR}" --yes - "${ROOT_DIR}"/spacetime --root-dir="${ROOT_DIR}" help + run: cargo ci update-flow --target=${{ matrix.target }} unreal_engine_tests: name: Unreal Engine Tests @@ -296,6 +234,15 @@ jobs: with: ref: ${{ env.GIT_REF }} - uses: dsherret/rust-toolchain-file@v1 + - name: Install unreal engine test dependencies + working-directory: sdks/unreal + env: + UE_ROOT_PATH: /home/ue4/UnrealEngine + run: | + + apt-get update + apt-get install -y acl curl ca-certificates + - name: Run Unreal Engine tests working-directory: sdks/unreal env: diff --git a/Cargo.toml b/Cargo.toml index fb2f05c8ae0..6618505aac8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ members = [ "sdks/rust/tests/test-client", "sdks/rust/tests/test-counter", "sdks/rust/tests/connect_disconnect_client", + "tools/ci", "tools/upgrade-version", "tools/license-check", "crates/bindings-typescript/test-app/server", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml new file mode 100644 index 00000000000..e907526a42c --- /dev/null +++ b/tools/ci/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ci" +version = "0.1.0" +edition.workspace = true + +[dependencies] +log.workspace = true +anyhow.workspace = true +chrono = { workspace = true, features=["clock"] } +clap.workspace = true +regex.workspace = true +duct.workspace = true diff --git a/tools/ci/build.rs b/tools/ci/build.rs new file mode 100644 index 00000000000..b821cf9a10d --- /dev/null +++ b/tools/ci/build.rs @@ -0,0 +1,4 @@ +#[allow(clippy::disallowed_macros)] +fn main() { + println!("cargo:rustc-env=TARGET={}", std::env::var("TARGET").unwrap_or_default()); +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs new file mode 100644 index 00000000000..ac5c73af01b --- /dev/null +++ b/tools/ci/src/main.rs @@ -0,0 +1,172 @@ +use anyhow::Result; +use clap::{CommandFactory, Parser, Subcommand}; +use duct::cmd; +use std::collections::HashMap; +use std::env; + +#[derive(Parser)] +#[command( + name = "spacetimedb-ci", + about = "SpacetimeDB CI tasks", + subcommand_required = false, + arg_required_else_help = false +)] +struct Cli { + #[command(subcommand)] + cmd: Option, + + #[arg(long)] + skip: Vec, +} + +#[derive(Subcommand)] +enum CiCmd { + Test, + Lints, + WasmBindings, + Smoketests { + #[arg(trailing_var_arg = true)] + args: Vec, + }, + UpdateFlow { + #[arg(long)] + target: Option, + #[arg(long, default_value = "true")] + github_token_auth: bool, + }, + UnrealTests, + CliDocs, +} + +macro_rules! run { + ($cmdline:expr) => { + run_command($cmdline, &Vec::new()) + }; + ($cmdline:expr, $envs:expr) => { + run_command($cmdline, $envs) + }; +} + +fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { + let subcmds = Cli::command() + .get_subcommands() + .map(|sc| sc.get_name().to_string()) + .collect::>(); + + for subcmd in subcmds { + if skips.contains(&subcmd) { + log::info!("skipping {subcmd} as requested"); + continue; + } + log::info!("executing cargo ci {subcmd}"); + run!(&format!("cargo ci {subcmd}"))?; + } + + Ok(()) +} + +fn run_command(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { + let mut env = env::vars().collect::>(); + env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); + log::debug!("$ {cmdline}"); + let status = cmd!("bash", "-lc", cmdline).full_env(env).run()?; + if !status.status.success() { + let e = anyhow::anyhow!("command failed: {cmdline}"); + log::error!("{e}"); + return Err(e); + } + Ok(()) +} + +fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.cmd { + Some(CiCmd::Test) => { + run!("sudo mkdir -p /stdb && sudo chmod 777 /stdb")?; + run!("cargo test --all -- --skip unreal")?; + run!("bash tools/check-diff.sh")?; + run!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; + run!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; + } + + Some(CiCmd::Lints) => { + run!("cargo fmt --all -- --check")?; + run!("cargo clippy --all --tests --benches -- -D warnings")?; + run!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; + run!("cd crates/bindings && cargo doc", &[("RUSTDOCFLAGS", "hey")])?; + } + + Some(CiCmd::WasmBindings) => { + run!("cargo test -p spacetimedb-codegen")?; + run!("cargo update")?; + run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; + } + + Some(CiCmd::Smoketests { args }) => { + // Note: clear_database and replication only work in private + run!(&format!("python -m smoketests {} -x clear_database replication", args.join(" ")))?; + } + + Some(CiCmd::UpdateFlow { + target, + github_token_auth, + }) => { + let target = target.unwrap_or_else(|| env!("TARGET").to_string()); + let github_token_auth_flag = if github_token_auth { + "--features github-token-auth " + } else { + "" + }; + + run!(&format!("echo 'checking update flow for target: {target}'"))?; + run!(&format!( + "cargo build {github_token_auth_flag}--target {target} -p spacetimedb-update" + ))?; + run!(&format!( + r#" +ROOT_DIR="$(mktemp -d)" +# NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. +# My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it +# happens very frequently on the `macos-runner`, but we haven't seen it on any others). +cargo run {github_token_auth_flag}--target {target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes +"${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help + "# + ))?; + } + + Some(CiCmd::UnrealTests) => { + run!("for p in \"$GITHUB_WORKSPACE\" \"${RUNNER_TEMP:-/__t}\" \"${RUNNER_TOOL_CACHE:-/__t}\"; do [ -d \"$p\" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX \"$p\" || true; done")?; + + run!("export CARGO_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/cargo\"")?; + run!("export RUSTUP_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/rustup\"")?; + run!("mkdir -p \"$CARGO_HOME\" \"$RUSTUP_HOME\"")?; + + run!("chmod a+rx \"$UE_ROOT_PATH\" \"$UE_ROOT_PATH/Engine\" \"$UE_ROOT_PATH/Engine/Build\" \"$UE_ROOT_PATH/Engine/Build/BatchFiles/Linux\" || true")?; + run!("chmod a+rx \"$UE_ROOT_PATH/Engine/Build/BatchFiles/Linux/Build.sh\" || true")?; + + run!("sudo -E -H -u ue4 env HOME=/home/ue4 CARGO_HOME=\"$CARGO_HOME\" RUSTUP_HOME=\"$RUSTUP_HOME\" PATH=\"$CARGO_HOME/bin:$PATH\" bash -lc 'set -euxo pipefail; if ! command -v cargo >/dev/null 2>&1; then curl -sSf https://sh.rustup.rs | sh -s -- -y; fi; rustup show >/dev/null; git config --global --add safe.directory \"$GITHUB_WORKSPACE\" || true; cd \"$GITHUB_WORKSPACE/sdks/unreal\"; cargo --version; cargo test'")?; + } + + Some(CiCmd::CliDocs) => { + run!("pnpm install --recursive")?; + run!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; + run!("pnpm format")?; + run!("git status")?; + run!( + r#" +if git diff --exit-code HEAD; then + echo "No docs changes detected" +else + echo "It looks like the CLI docs have changed:" + exit 1 +fi + "# + )?; + } + + None => run_all_clap_subcommands(&cli.skip)?, + } + + Ok(()) +} From 0011f562c7cd32bbde9a88fddb27dc6069a5c8f3 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 14 Oct 2025 08:15:52 -0700 Subject: [PATCH 002/160] [bfops/pnpm-install-instruction]: Add `pnpm install` step to docs CLI reference instructions --- docs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 9c923ff8cbc..4131e1e370c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -38,7 +38,8 @@ git push -u origin a-branch-name-that-describes-my-change #### CLI Reference Section 1. Run `cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md` -2. Run `pnpm format` +2. Run `pnpm install` +3. Run `pnpm format` ### Checking Links From 1c0406d2a724a4f98b8bdbf1cd258652d38dce79 Mon Sep 17 00:00:00 2001 From: Kasama Date: Fri, 24 Oct 2025 14:01:19 -0300 Subject: [PATCH 003/160] update cargo ci --- .github/workflows/ci.yml | 57 +- Cargo.lock | 2443 +++++++++++++++++++------------------- tools/ci/README.md | 164 +++ tools/ci/src/ci_docs.rs | 68 ++ tools/ci/src/main.rs | 115 +- 5 files changed, 1591 insertions(+), 1256 deletions(-) create mode 100644 tools/ci/README.md create mode 100644 tools/ci/src/ci_docs.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 543af73bff6..388fa57f1d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: with: global-json-file: global.json - - name: Run cargo fmt + - name: Run ci lint run: cargo ci lint wasm_bindings: @@ -209,20 +209,20 @@ jobs: # without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle. options: --user 0:0 steps: -# Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back -# commits though. -# - 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" + # Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back + # commits though. + # - 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: @@ -284,6 +284,33 @@ jobs: cargo test ' + ci_command_docs: + name: Check CI command docs + runs-on: ubuntu-latest + 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: Check for docs change + run: cargo ci self-docs --check + cli_docs: name: Check CLI docs permissions: read-all diff --git a/Cargo.lock b/Cargo.lock index fa6cb645241..f56e3b05177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,18 +13,18 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ - "gimli 0.31.1", + "gimli 0.32.3", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aes" @@ -55,7 +55,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.2", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -91,12 +91,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -114,9 +108,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -129,44 +123,44 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "once_cell", - "windows-sys 0.59.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" dependencies = [ "backtrace", ] @@ -188,9 +182,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] @@ -232,46 +226,46 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "async_cache" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b273cbc6887e59eda041e24bc08631c28fb22edf9fdb9c6ed48f77147654e297" +checksum = "5cbc83780e81c84c8763ba7c07d0ab3f42b09cc2215687ccfdb2cb98ebfb3704" dependencies = [ "ahash 0.8.12", "anyhow", "async_singleflight", - "dashmap 5.5.3", + "dashmap 6.1.0", "faststr", "futures", - "hashbrown 0.14.5", - "parking_lot 0.12.3", + "hashbrown 0.15.5", + "parking_lot 0.12.5", "tokio", ] [[package]] name = "async_singleflight" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb139b209b5443652e3d7c8cdf2c1cd02d12009f657c3670191d9fd527eaa6d" +checksum = "421e0a4e6860c81e94d899b0a9169fcfeb28bc022a8ffab0c1f3a0a06d11d5a4" dependencies = [ "futures", - "hashbrown 0.13.2", - "parking_lot 0.12.3", + "hashbrown 0.15.5", + "parking_lot 0.12.5", "pin-project", "tokio", ] @@ -295,15 +289,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.13.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +checksum = "879b6c89592deb404ba4dc0ae6b58ffd1795c78991cbb5b8bc441c48a070440d" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -312,11 +306,11 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.28.2" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1" +checksum = "107a4e9d9cab9963e04e84bb8dee0e25f2a987f9a8bad5ed054abd439caa8f8c" dependencies = [ - "bindgen 0.69.5", + "bindgen", "cc", "cmake", "dunce", @@ -336,7 +330,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "itoa", "matchit", @@ -404,17 +398,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line 0.24.2", + "addr2line 0.25.1", "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.37.3", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -439,9 +433,9 @@ dependencies = [ [[package]] name = "bigdecimal" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a22f228ab7a1b23027ccc6c350b72868017af7ea8356fbdf19f8d991c690013" +checksum = "560f42649de9fa436b73517378a147ec21f6c997a546581df4b4b31677828934" dependencies = [ "autocfg", "libm", @@ -459,56 +453,24 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.9.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.101", - "which 4.4.2", -] - [[package]] name = "bindgen" version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", + "rustc-hash", "shlex", - "syn 2.0.101", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", + "syn 2.0.108", ] [[package]] @@ -517,15 +479,9 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -540,11 +496,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -601,7 +557,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -627,9 +583,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byte-unit" @@ -665,28 +621,28 @@ dependencies = [ [[package]] name = "bytecount" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -703,12 +659,12 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytestring" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" dependencies = [ "bytes", - "serde", + "serde_core", ] [[package]] @@ -742,11 +698,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.9" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ - "serde", + "serde_core", ] [[package]] @@ -780,10 +736,11 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -806,9 +763,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -827,17 +784,28 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1", +] + +[[package]] +name = "ci" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap 4.5.50", + "duct", + "log", + "regex", ] [[package]] @@ -907,12 +875,12 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "0c2cfd7bf8a6017ddaa4e32ffe7403d547790db06bd171c1c53926faab501623" dependencies = [ "clap_builder", - "clap_derive 4.5.32", + "clap_derive 4.5.49", ] [[package]] @@ -921,18 +889,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2a2617956a06d4885b490697b5307ebb09fec10b088afc18c81762d848c2339" dependencies = [ - "clap 4.5.37", + "clap 4.5.50", ] [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "0a4c05b9e80c5ccd3a7ef080ad7b6ba7d6fc00a985b8b157197075677c82c7a0" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.4", + "clap_lex 0.7.6", "strsim 0.11.1", "terminal_size", ] @@ -952,14 +920,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -973,9 +941,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clipboard-win" @@ -999,15 +967,18 @@ dependencies = [ [[package]] name = "cobs" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.17", +] [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" @@ -1047,7 +1018,7 @@ dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] @@ -1084,9 +1055,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ "core-foundation-sys", "libc", @@ -1120,9 +1091,9 @@ dependencies = [ [[package]] name = "cpp_demangle" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" dependencies = [ "cfg-if", ] @@ -1173,7 +1144,7 @@ dependencies = [ "hashbrown 0.14.5", "log", "regalloc2", - "rustc-hash 2.1.1", + "rustc-hash", "smallvec", "target-lexicon", ] @@ -1284,9 +1255,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1300,7 +1271,7 @@ dependencies = [ "anes", "cast", "ciborium", - "clap 4.5.37", + "clap 4.5.50", "criterion-plot", "futures", "is-terminal", @@ -1382,7 +1353,7 @@ dependencies = [ "crossterm_winapi", "libc", "mio 0.8.11", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "signal-hook", "signal-hook-mio", "winapi", @@ -1399,9 +1370,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -1474,9 +1445,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ "darling_core", "darling_macro", @@ -1484,27 +1455,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1517,7 +1488,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.12", ] [[package]] @@ -1531,7 +1502,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.12", ] [[package]] @@ -1552,18 +1523,18 @@ dependencies = [ [[package]] name = "deflate64" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] @@ -1574,18 +1545,18 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1598,7 +1569,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1638,7 +1609,7 @@ dependencies = [ "diplomat_core", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1658,7 +1629,7 @@ dependencies = [ "serde", "smallvec", "strck", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1711,7 +1682,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1732,6 +1703,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "educe" version = "0.4.23" @@ -1801,7 +1778,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1821,7 +1798,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -1834,35 +1811,35 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "enumset" -version = "1.1.6" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a6b7c3d347de0a9f7bfd2f853be43fe32fa6fac30c70f6d6d67a1e936b87ee" +checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.11.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6da3ea9e1d1a3b1593e15781f930120e72aa7501610b2f82e5b6739c72e8eac5" +checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", ] @@ -1898,12 +1875,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1918,9 +1895,9 @@ dependencies = [ [[package]] name = "ethnum" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0939f82868b77ef93ce3c3c3daf2b3c526b456741da5a1a4559e590965b6026b" +checksum = "ca81e6b4777c89fd810c25a4be2b1bd93ea034fbe58e6a75216a34c6b82c539b" dependencies = [ "serde", ] @@ -1945,12 +1922,13 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fancy-regex" -version = "0.11.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +checksum = "998b056554fbe42e03ae0e152895cd1a7e1002aec800fdc6635d20270260c46f" dependencies = [ - "bit-set 0.5.3", - "regex", + "bit-set", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1961,12 +1939,12 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "faststr" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6503af7917fea18ffef8f7e8553fb8dff89e2e6837e94e09dd7fb069c82d62c" +checksum = "baec6a0289d7f1fe5665586ef7340af82e3037207bef60f5785e57569776f0c8" dependencies = [ "bytes", - "rkyv 0.8.10", + "rkyv 0.8.12", "serde", "simdutf8", ] @@ -1984,16 +1962,22 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.25" +version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -2002,9 +1986,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" dependencies = [ "crc32fast", "miniz_oxide", @@ -2039,9 +2023,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -2149,7 +2133,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -2193,10 +2177,11 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.4" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" dependencies = [ + "cc", "cfg-if", "libc", "log", @@ -2206,9 +2191,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -2223,21 +2208,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -2248,21 +2233,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" dependencies = [ "fallible-iterator 0.3.0", - "indexmap 2.9.0", + "indexmap 2.12.0", "stable_deref_trait", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "gzip-header" @@ -2275,9 +2260,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ "bytes", "fnv", @@ -2285,7 +2270,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.9.0", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -2294,9 +2279,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -2304,7 +2289,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.9.0", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -2313,12 +2298,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -2330,15 +2316,6 @@ dependencies = [ "ahash 0.7.8", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.12", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -2352,16 +2329,23 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ + "allocator-api2", "equivalent", "foldhash", "rayon", "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "hashlink" version = "0.8.4" @@ -2373,11 +2357,11 @@ dependencies = [ [[package]] name = "headers" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "headers-core", "http 1.3.1", @@ -2418,15 +2402,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - -[[package]] -name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2533,9 +2511,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -2547,14 +2525,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -2563,20 +2541,22 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", - "h2 0.4.10", + "futures-core", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2584,13 +2564,12 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.5" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", "http 1.3.1", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "rustls", "rustls-pki-types", @@ -2620,7 +2599,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "native-tls", "tokio", @@ -2630,22 +2609,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.7.0", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.6.1", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2666,7 +2651,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -2697,9 +2682,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2707,7 +2692,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.0", + "windows-core 0.62.2", ] [[package]] @@ -2730,10 +2715,10 @@ dependencies = [ "icu_calendar_data", "icu_locale", "icu_locale_core", - "icu_provider 2.0.0", - "tinystr 0.8.1", - "writeable 0.6.1", - "zerovec 0.11.4", + "icu_provider", + "tinystr", + "writeable", + "zerovec", ] [[package]] @@ -2742,18 +2727,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7219c8639ab936713a87b571eed2bc2615aa9137e8af6eb221446ee5644acc18" -[[package]] -name = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke 0.7.5", - "zerofrom", - "zerovec 0.10.4", -] - [[package]] name = "icu_collections" version = "2.0.0" @@ -2762,9 +2735,9 @@ checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", - "yoke 0.8.0", + "yoke", "zerofrom", - "zerovec 0.11.4", + "zerovec", ] [[package]] @@ -2774,13 +2747,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ae5921528335e91da1b6c695dbf1ec37df5ac13faa3f91e5640be93aa2fbefd" dependencies = [ "displaydoc", - "icu_collections 2.0.0", + "icu_collections", "icu_locale_core", "icu_locale_data", - "icu_provider 2.0.0", + "icu_provider", "potential_utf", - "tinystr 0.8.1", - "zerovec 0.11.4", + "tinystr", + "zerovec", ] [[package]] @@ -2790,10 +2763,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", - "litemap 0.8.0", - "tinystr 0.8.1", - "writeable 0.6.1", - "zerovec 0.11.4", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] @@ -2802,100 +2775,48 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fdef0c124749d06a743c69e938350816554eb63ac979166590e2b4ee4252765" -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap 0.7.5", - "tinystr 0.7.6", - "writeable 0.5.5", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider 1.5.0", - "tinystr 0.7.6", - "zerovec 0.10.4", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", - "icu_collections 1.5.0", + "icu_collections", "icu_normalizer_data", "icu_properties", - "icu_provider 1.5.0", + "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec 0.10.4", + "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", - "icu_collections 1.5.0", - "icu_locid_transform", + "icu_collections", + "icu_locale_core", "icu_properties_data", - "icu_provider 1.5.0", - "tinystr 0.7.6", - "zerovec 0.10.4", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" - -[[package]] -name = "icu_provider" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr 0.7.6", - "writeable 0.5.5", - "yoke 0.7.5", - "zerofrom", - "zerovec 0.10.4", -] +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" @@ -2906,23 +2827,12 @@ dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", - "tinystr 0.8.1", - "writeable 0.6.1", - "yoke 0.8.0", + "tinystr", + "writeable", + "yoke", "zerofrom", "zerotrie", - "zerovec 0.11.4", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "zerovec", ] [[package]] @@ -2939,9 +2849,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2950,9 +2860,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -2964,7 +2874,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.5", ] [[package]] @@ -2980,13 +2890,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -2998,7 +2909,7 @@ dependencies = [ "console", "number_prefix", "portable-atomic", - "unicode-width 0.2.0", + "unicode-width 0.2.2", "web-time", ] @@ -3024,17 +2935,17 @@ dependencies = [ [[package]] name = "inferno" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2094aecddc672e902cd773bad7071542f63641e01e9187c3bba4b43005e837e9" +checksum = "e96d2465363ed2d81857759fc864cf6bb7997f79327aec028d65bd7989393685" dependencies = [ "ahash 0.8.12", - "clap 4.5.37", + "clap 4.5.50", "crossbeam-channel", "crossbeam-utils", "dashmap 6.1.0", "env_logger 0.11.8", - "indexmap 2.9.0", + "indexmap 2.12.0", "itoa", "log", "num-format", @@ -3055,9 +2966,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.43.1" +version = "1.43.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "154934ea70c58054b556dd430b99a98c2a7ff5309ac9891597e339b5c28f4371" +checksum = "46fdb647ebde000f43b5b53f773c30cf9b0cb4300453208713fa38b2c70935a0" dependencies = [ "console", "once_cell", @@ -3076,39 +2987,38 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-uring" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.5.1", + "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -3137,6 +3047,24 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -3199,19 +3127,19 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -3219,12 +3147,12 @@ dependencies = [ [[package]] name = "junction" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72bbdfd737a243da3dfc1f99ee8d6e166480f17ab4ac84d7c34aacd73fc7bd16" +checksum = "c52f6e1bf39a7894f618c9d378904a11dbd7e10fe3ec20d1173600e79b1408d8" dependencies = [ "scopeguard", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -3264,7 +3192,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -3273,12 +3201,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "leb128" version = "0.2.5" @@ -3287,18 +3209,18 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-link 0.2.1", ] [[package]] @@ -3309,9 +3231,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libmimalloc-sys" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" dependencies = [ "cc", "libc", @@ -3319,13 +3241,13 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.18", ] [[package]] @@ -3345,7 +3267,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d8de370f98a6cb8a4606618e53e802f93b094ddec0f96988eaec2c27e6e9ce7" dependencies = [ - "clap 4.5.37", + "clap 4.5.50", "termcolor", "threadpool", ] @@ -3364,15 +3286,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" - -[[package]] -name = "litemap" -version = "0.7.5" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -3382,19 +3298,18 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -3432,9 +3347,9 @@ dependencies = [ [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -3460,11 +3375,11 @@ checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -3491,33 +3406,33 @@ checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memfd" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.2", ] [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] [[package]] name = "mimalloc" -version = "0.1.46" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" dependencies = [ "libmimalloc-sys", ] @@ -3536,11 +3451,12 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -3551,19 +3467,19 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -3594,22 +3510,22 @@ dependencies = [ [[package]] name = "munge" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e22e7961c873e8b305b176d2a4e1d41ce7ba31bc1c52d2a107a89568ec74c55" +checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" dependencies = [ "munge_macro", ] [[package]] name = "munge_macro" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ac7d860b767c6398e88fe93db73ce53eb496057aa6895ffa4d60cb02e1d1c6b" +checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -3661,7 +3577,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "cfg_aliases", "libc", @@ -3685,12 +3601,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -3784,11 +3699,11 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ - "hermit-abi 0.3.9", + "hermit-abi 0.5.2", "libc", ] @@ -3809,9 +3724,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] @@ -3824,11 +3739,11 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "objc2", ] @@ -3839,8 +3754,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "crc32fast", - "hashbrown 0.15.3", - "indexmap 2.9.0", + "hashbrown 0.15.5", + "indexmap 2.12.0", + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ "memchr", ] @@ -3850,6 +3774,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "oorandom" version = "11.1.5" @@ -3858,11 +3788,11 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl" -version = "0.10.72" +version = "0.10.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" +checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -3879,7 +3809,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -3890,18 +3820,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.0+3.5.0" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ce546f549326b0e6052b649198487d91320875da901e7bd11a06d1ee3f9c2f" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.108" +version = "0.9.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" +checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" dependencies = [ "cc", "libc", @@ -3918,12 +3848,12 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "os_pipe" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3932,12 +3862,6 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owning_ref" version = "0.4.1" @@ -3977,12 +3901,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", + "parking_lot_core 0.9.12", ] [[package]] @@ -4001,15 +3925,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -4036,19 +3960,19 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perf-test-module" @@ -4065,7 +3989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.9.0", + "indexmap 2.12.0", ] [[package]] @@ -4095,10 +4019,10 @@ dependencies = [ "lazy-regex", "md5", "postgres-types", - "rand 0.9.1", + "rand 0.9.2", "rust_decimal", "rustls-pki-types", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-rustls", "tokio-util", @@ -4106,18 +4030,19 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_shared", + "serde", ] [[package]] name = "phf_shared" -version = "0.11.3" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -4139,7 +4064,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -4162,13 +4087,13 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", - "indexmap 2.9.0", - "quick-xml 0.32.0", + "indexmap 2.12.0", + "quick-xml 0.38.3", "serde", "time", ] @@ -4203,15 +4128,15 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "postcard" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ "cobs", "embedded-io 0.4.0", @@ -4221,21 +4146,21 @@ dependencies = [ [[package]] name = "postgres-derive" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69700ea4603c5ef32d447708e6a19cd3e8ac197a000842e97f527daea5e4175f" +checksum = "56df96f5394370d1b20e49de146f9e6c25aa9ae750f449c9d665eafecb3ccae6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "postgres-protocol" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" +checksum = "fbef655056b916eb868048276cfd5d6a7dea4f81560dfd047f97c8c6fe3fcfd4" dependencies = [ "base64 0.22.1", "byteorder", @@ -4244,16 +4169,16 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.9.1", + "rand 0.9.2", "sha2", "stringprep", ] [[package]] name = "postgres-types" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" +checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095" dependencies = [ "array-init", "bytes", @@ -4270,7 +4195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "serde", - "zerovec 0.11.4", + "zerovec", ] [[package]] @@ -4288,7 +4213,7 @@ dependencies = [ "anyhow", "backtrace", "flate2", - "inferno 0.12.2", + "inferno 0.12.3", "num", "paste", "prost", @@ -4315,21 +4240,21 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.34" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit", + "toml_edit 0.23.7", ] [[package]] @@ -4358,9 +4283,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "8e0f6df8eaa422d97d72edcd152e1451618fed47fabbdbd5a8864167b1d4aff7" dependencies = [ "unicode-ident", ] @@ -4375,26 +4300,26 @@ dependencies = [ "fnv", "lazy_static", "memchr", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "protobuf", "thiserror 1.0.69", ] [[package]] name = "proptest" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" +checksum = "2bb0be07becd10686a0bb407298fb425360a5c44a663774406340c59a22de4ce" dependencies = [ - "bit-set 0.8.0", - "bit-vec 0.8.0", - "bitflags 2.9.0", + "bit-set", + "bit-vec", + "bitflags 2.10.0", "lazy_static", "num-traits", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.2", + "rand_chacha 0.9.0", "rand_xorshift", - "regex-syntax 0.8.5", + "regex-syntax", "rusty-fork", "tempfile", "unarray", @@ -4408,7 +4333,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -4428,10 +4353,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -4442,9 +4367,9 @@ checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" [[package]] name = "psm" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +checksum = "e66fcd288453b748497d8fb18bccc83a16b0518e3906d4b8df0a8d42d93dbb1c" dependencies = [ "cc", ] @@ -4460,11 +4385,11 @@ dependencies = [ [[package]] name = "ptr_meta" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" dependencies = [ - "ptr_meta_derive 0.3.0", + "ptr_meta_derive 0.3.1", ] [[package]] @@ -4480,13 +4405,13 @@ dependencies = [ [[package]] name = "ptr_meta_derive" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -4502,7 +4427,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1a341ae463320e9f8f34adda49c8a85d81d4e8f34cce4397fb0350481552224" dependencies = [ "chrono", - "indexmap 2.9.0", + "indexmap 2.12.0", "quick-xml 0.31.0", "strip-ansi-escapes", "thiserror 1.0.69", @@ -4529,18 +4454,18 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.32.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -4555,18 +4480,18 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -4586,11 +4511,11 @@ dependencies = [ [[package]] name = "rancor" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" dependencies = [ - "ptr_meta 0.3.0", + "ptr_meta 0.3.1", ] [[package]] @@ -4619,9 +4544,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4677,23 +4602,23 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", ] [[package]] name = "rand_xorshift" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.6.4", + "rand_core 0.9.3", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -4701,9 +4626,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4729,11 +4654,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", ] [[package]] @@ -4747,6 +4672,26 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "regalloc2" version = "0.10.2" @@ -4755,60 +4700,45 @@ checksum = "12908dbeb234370af84d0579b9f68258a0f67e201412dd9a2814e6f45b2fc0f0" dependencies = [ "hashbrown 0.14.5", "log", - "rustc-hash 2.1.1", + "rustc-hash", "slice-group-by", "smallvec", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-lite" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" - -[[package]] -name = "regex-syntax" -version = "0.6.29" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "remove_dir_all" @@ -4830,9 +4760,9 @@ dependencies = [ [[package]] name = "rend" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" [[package]] name = "reqwest" @@ -4845,7 +4775,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.26", + "h2 0.3.27", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.32", @@ -4858,7 +4788,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 1.0.4", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -4876,55 +4806,52 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.4.10", + "h2 0.4.12", "http 1.3.1", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.2.0", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper 1.0.2", - "system-configuration 0.6.1", "tokio", "tokio-native-tls", "tokio-util", "tower", + "tower-http 0.6.6", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", ] [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" dependencies = [ "bytemuck", ] @@ -4963,18 +4890,18 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65" +checksum = "35a640b26f007713818e9a9b65d34da1cf58538207b052916a83d80e43f3ffa4" dependencies = [ "bytes", - "hashbrown 0.15.3", - "indexmap 2.9.0", + "hashbrown 0.15.5", + "indexmap 2.12.0", "munge", - "ptr_meta 0.3.0", + "ptr_meta 0.3.1", "rancor", - "rend 0.5.2", - "rkyv_derive 0.8.10", + "rend 0.5.3", + "rkyv_derive 0.8.12", "tinyvec", "uuid", ] @@ -4992,13 +4919,13 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" +checksum = "bd83f5f173ff41e00337d97f6572e416d022ef8a19f371817259ae960324c482" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -5008,7 +4935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.9.0", + "bitflags 2.10.0", "serde", "serde_derive", ] @@ -5019,7 +4946,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "fallible-iterator 0.2.0", "fallible-streaming-iterator", "hashlink", @@ -5029,9 +4956,9 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.37.1" +version = "1.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa7de2ba56ac291bd90c6b9bece784a52ae1411f9506544b3eae36dd2356d50" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" dependencies = [ "arrayvec", "borsh", @@ -5046,15 +4973,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -5077,7 +4998,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys 0.4.15", @@ -5086,22 +5007,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "6a9586e9ee2b4f8fab52a0048ca7334d7024eef48e2cb9407e3497bb7cab7fa7" dependencies = [ "aws-lc-rs", "log", @@ -5121,15 +5042,6 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -5141,9 +5053,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.2" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7149975849f1abb3832b246010ef62ccc80d3a76169517ada7188252b9cfb437" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "aws-lc-rs", "ring", @@ -5153,15 +5065,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -5175,7 +5087,7 @@ version = "12.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "994eca4bca05c87e86e15d90fc7a91d1be64b4482b38cb2d27474568fe7c9db9" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "cfg-if", "clipboard-win", "fd-lock", @@ -5204,7 +5116,7 @@ dependencies = [ "icu_locale", "num-traits", "temporal_rs", - "writeable 0.6.1", + "writeable", ] [[package]] @@ -5234,11 +5146,35 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", ] [[package]] @@ -5289,7 +5225,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -5298,9 +5234,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -5319,65 +5255,87 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.12.0", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_path_to_error" -version = "0.1.17" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" dependencies = [ "itoa", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5392,17 +5350,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.9.0", - "serde", - "serde_derive", + "indexmap 2.12.0", + "schemars 0.9.0", + "schemars 1.0.4", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -5410,14 +5369,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -5430,7 +5389,7 @@ dependencies = [ "futures", "lazy_static", "log", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "serial_test_derive", ] @@ -5442,7 +5401,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -5488,12 +5447,13 @@ dependencies = [ [[package]] name = "shared_child" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e297bd52991bbe0686c086957bee142f13df85d1e79b0b21630a99d374ae9dc" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" dependencies = [ "libc", - "windows-sys 0.59.0", + "sigchld", + "windows-sys 0.60.2", ] [[package]] @@ -5508,11 +5468,22 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -5531,9 +5502,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -5564,7 +5535,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", ] @@ -5576,12 +5547,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "sled" @@ -5607,18 +5575,18 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5626,12 +5594,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5644,38 +5612,38 @@ dependencies = [ [[package]] name = "spacetimedb" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cd00b22d3170c92858f448db222a135c6302e336f1729c17e77f634c7e9e849" +version = "1.5.0" dependencies = [ "bytemuck", "derive_more", "getrandom 0.2.16", + "insta", "log", "rand 0.8.5", "scoped-tls", - "spacetimedb-bindings-macro 1.3.0", - "spacetimedb-bindings-sys 1.3.0", - "spacetimedb-lib 1.3.0", - "spacetimedb-primitives 1.3.0", + "spacetimedb-bindings-macro 1.5.0", + "spacetimedb-bindings-sys 1.5.0", + "spacetimedb-lib 1.5.0", + "spacetimedb-primitives 1.5.0", + "trybuild", ] [[package]] name = "spacetimedb" -version = "1.5.0" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8679cf54a7a653e6bc612bef28c219f98b9274308d8aae1e86046ed685db48b1" dependencies = [ "bytemuck", "derive_more", "getrandom 0.2.16", - "insta", "log", "rand 0.8.5", "scoped-tls", - "spacetimedb-bindings-macro 1.5.0", - "spacetimedb-bindings-sys 1.5.0", - "spacetimedb-lib 1.5.0", - "spacetimedb-primitives 1.5.0", - "trybuild", + "spacetimedb-bindings-macro 1.6.0", + "spacetimedb-bindings-sys 1.6.0", + "spacetimedb-lib 1.6.0", + "spacetimedb-primitives 1.6.0", ] [[package]] @@ -5697,7 +5665,7 @@ dependencies = [ "anyhow", "anymap", "byte-unit", - "clap 4.5.37", + "clap 4.5.50", "criterion", "foldhash", "futures", @@ -5708,7 +5676,7 @@ dependencies = [ "lazy_static", "log", "mimalloc", - "rand 0.9.1", + "rand 0.9.2", "regex", "rusqlite", "serde", @@ -5739,44 +5707,44 @@ dependencies = [ [[package]] name = "spacetimedb-bindings-macro" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dcccbad947dbecdb49f6c241762a78451344f04c1bdad023a3b1b5942238b2" +version = "1.5.0" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.3.0", - "syn 2.0.101", + "spacetimedb-primitives 1.5.0", + "syn 2.0.108", ] [[package]] name = "spacetimedb-bindings-macro" -version = "1.5.0" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a930242493f5c875ab96903eb40fb6a90c4d3ae99597fd51da569ff22769a03" dependencies = [ "heck 0.4.1", "humantime", "proc-macro2", "quote", - "spacetimedb-primitives 1.5.0", - "syn 2.0.101", + "spacetimedb-primitives 1.6.0", + "syn 2.0.108", ] [[package]] name = "spacetimedb-bindings-sys" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969e77710b67e7db3e86bb4e195ba325f5f288efd1bfd6b1997ca9c389d22b04" +version = "1.5.0" dependencies = [ - "spacetimedb-primitives 1.3.0", + "spacetimedb-primitives 1.5.0", ] [[package]] name = "spacetimedb-bindings-sys" -version = "1.5.0" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e565dfcdd2dc3f58e0178052ec1c8ce710b013482d4fa50b511ba786a2c3bc68" dependencies = [ - "spacetimedb-primitives 1.5.0", + "spacetimedb-primitives 1.6.0", ] [[package]] @@ -5788,7 +5756,7 @@ dependencies = [ "bytes", "cargo_metadata", "chrono", - "clap 4.5.37", + "clap 4.5.50", "clap-markdown", "colored", "convert_case 0.6.0", @@ -5805,7 +5773,7 @@ dependencies = [ "mimalloc", "percent-encoding", "regex", - "reqwest 0.12.15", + "reqwest 0.12.24", "rustyline", "serde", "serde_json", @@ -5831,8 +5799,8 @@ dependencies = [ "tikv-jemallocator", "tokio", "tokio-tungstenite", - "toml 0.8.22", - "toml_edit", + "toml 0.8.23", + "toml_edit 0.22.27", "tracing", "walkdir", "wasmbin", @@ -5859,7 +5827,7 @@ dependencies = [ "headers", "http 1.3.1", "humantime", - "hyper 1.6.0", + "hyper 1.7.0", "hyper-util", "itoa", "jemalloc_pprof", @@ -5868,7 +5836,7 @@ dependencies = [ "mime", "pretty_assertions", "prometheus", - "rand 0.9.1", + "rand 0.9.2", "regex", "scopeguard", "serde", @@ -5886,8 +5854,8 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite", - "toml 0.8.22", - "tower-http", + "toml 0.8.23", + "tower-http 0.5.2", "tower-layer", "tower-service", "tracing", @@ -5939,7 +5907,7 @@ name = "spacetimedb-commitlog" version = "1.5.0" dependencies = [ "async-stream", - "bitflags 2.9.0", + "bitflags 2.10.0", "bytes", "crc32c", "env_logger 0.10.2", @@ -5951,7 +5919,7 @@ dependencies = [ "pretty_assertions", "proptest", "proptest-derive", - "rand 0.9.1", + "rand 0.9.2", "serde", "spacetimedb-commitlog", "spacetimedb-fs-utils", @@ -5998,12 +5966,12 @@ dependencies = [ "fs_extra", "futures", "futures-util", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "hex", "hostname", - "hyper 1.6.0", + "hyper 1.7.0", "imara-diff", - "indexmap 2.9.0", + "indexmap 2.12.0", "itertools 0.12.1", "lazy_static", "log", @@ -6012,20 +5980,20 @@ dependencies = [ "nohash-hasher", "once_cell", "openssl", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "paste", "pin-project-lite", "pretty_assertions", "prometheus", "proptest", "proptest-derive", - "rand 0.9.1", + "rand 0.9.2", "rayon", "rayon-core", "regex", - "reqwest 0.12.15", + "reqwest 0.12.24", "rustc-demangle", - "rustc-hash 2.1.1", + "rustc-hash", "scopeguard", "semver", "serde", @@ -6073,7 +6041,7 @@ dependencies = [ "tokio-metrics", "tokio-stream", "tokio-util", - "toml 0.8.22", + "toml 0.8.23", "tracing", "tracing-appender", "tracing-core", @@ -6094,7 +6062,7 @@ version = "1.5.0" dependencies = [ "ahash 0.8.12", "crossbeam-queue", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "nohash-hasher", "serde", "smallvec", @@ -6115,7 +6083,7 @@ dependencies = [ "lazy_static", "log", "once_cell", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "pretty_assertions", "prometheus", "proptest", @@ -6191,7 +6159,7 @@ version = "1.5.0" dependencies = [ "anyhow", "hex", - "rand 0.9.1", + "rand 0.9.2", "tempdir", "thiserror 1.0.69", "tokio", @@ -6229,38 +6197,18 @@ dependencies = [ [[package]] name = "spacetimedb-lib" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9b8d04af6da14e3b7cc79b4b88c1c309c994eeba6a97f298fd0cdc7f897209" +version = "1.5.0" dependencies = [ "anyhow", - "bitflags 2.9.0", + "bitflags 2.10.0", "blake3", + "bytes", "chrono", "derive_more", "enum-as-inner", + "enum-map", "hex", - "itertools 0.12.1", - "spacetimedb-bindings-macro 1.3.0", - "spacetimedb-primitives 1.3.0", - "spacetimedb-sats 1.3.0", - "thiserror 1.0.69", -] - -[[package]] -name = "spacetimedb-lib" -version = "1.5.0" -dependencies = [ - "anyhow", - "bitflags 2.9.0", - "blake3", - "bytes", - "chrono", - "derive_more", - "enum-as-inner", - "enum-map", - "hex", - "insta", + "insta", "itertools 0.12.1", "proptest", "proptest-derive", @@ -6275,13 +6223,33 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "spacetimedb-lib" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ba57c8f1983bb144ee7e1ff28aa5882ca437f67c14a14daad4bf61d31f7040d" +dependencies = [ + "anyhow", + "bitflags 2.10.0", + "blake3", + "chrono", + "derive_more", + "enum-as-inner", + "hex", + "itertools 0.12.1", + "spacetimedb-bindings-macro 1.6.0", + "spacetimedb-primitives 1.6.0", + "spacetimedb-sats 1.6.0", + "thiserror 1.0.69", +] + [[package]] name = "spacetimedb-memory-usage" version = "1.5.0" dependencies = [ "decorum", "ethnum", - "hashbrown 0.15.3", + "hashbrown 0.15.5", "smallvec", ] @@ -6347,26 +6315,26 @@ dependencies = [ [[package]] name = "spacetimedb-primitives" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0181dc495c66138755705e573f0e0a9a7024660f207ebd1e35d0a1f839813e84" +version = "1.5.0" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "either", "itertools 0.12.1", "nohash-hasher", + "proptest", + "spacetimedb-memory-usage", ] [[package]] name = "spacetimedb-primitives" -version = "1.5.0" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dcd64c6970ca59e7b71e51952cf1a71bae2652b1eb736c19a7e528f3874894a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "either", "itertools 0.12.1", "nohash-hasher", - "proptest", - "spacetimedb-memory-usage", ] [[package]] @@ -6388,15 +6356,16 @@ dependencies = [ [[package]] name = "spacetimedb-sats" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9700ea5d62401c05633ce86d369dbc4a73ec978ac7e347ab8563626ad01fbc" +version = "1.5.0" dependencies = [ + "ahash 0.8.12", "anyhow", "arrayvec", - "bitflags 2.9.0", + "bitflags 2.10.0", + "blake3", "bytemuck", "bytes", + "bytestring", "chrono", "decorum", "derive_more", @@ -6404,26 +6373,32 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", + "proptest", + "proptest-derive", + "rand 0.9.2", "second-stack", + "serde", + "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.3.0", - "spacetimedb-primitives 1.3.0", + "spacetimedb-bindings-macro 1.5.0", + "spacetimedb-memory-usage", + "spacetimedb-metrics", + "spacetimedb-primitives 1.5.0", "thiserror 1.0.69", ] [[package]] name = "spacetimedb-sats" -version = "1.5.0" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae61d8f88bda21f56c143bc9b4ddc20100ff08ae4fed0b9444509f7c3ca4339" dependencies = [ - "ahash 0.8.12", "anyhow", "arrayvec", - "bitflags 2.9.0", - "blake3", + "bitflags 2.10.0", "bytemuck", "bytes", - "bytestring", "chrono", "decorum", "derive_more", @@ -6431,18 +6406,11 @@ dependencies = [ "ethnum", "hex", "itertools 0.12.1", - "proptest", - "proptest-derive", - "rand 0.9.1", "second-stack", - "serde", - "serde_json", "sha3", "smallvec", - "spacetimedb-bindings-macro 1.5.0", - "spacetimedb-memory-usage", - "spacetimedb-metrics", - "spacetimedb-primitives 1.5.0", + "spacetimedb-bindings-macro 1.6.0", + "spacetimedb-primitives 1.6.0", "thiserror 1.0.69", ] @@ -6454,8 +6422,8 @@ dependencies = [ "derive_more", "enum-as-inner", "enum-map", - "hashbrown 0.15.3", - "indexmap 2.9.0", + "hashbrown 0.15.5", + "indexmap 2.12.0", "insta", "itertools 0.12.1", "lazy_static", @@ -6494,7 +6462,7 @@ dependencies = [ "log", "once_cell", "prometheus", - "rand 0.9.1", + "rand 0.9.2", "spacetimedb-client-api-messages", "spacetimedb-data-structures", "spacetimedb-lib 1.5.0", @@ -6519,7 +6487,7 @@ dependencies = [ "hex", "log", "pretty_assertions", - "rand 0.9.1", + "rand 0.9.2", "scopeguard", "spacetimedb-core", "spacetimedb-datastore", @@ -6556,7 +6524,7 @@ dependencies = [ "anyhow", "async-trait", "axum", - "clap 4.5.37", + "clap 4.5.50", "dirs", "futures", "hostname", @@ -6570,7 +6538,7 @@ dependencies = [ "serde", "serde_json", "sled", - "socket2 0.5.9", + "socket2 0.5.10", "spacetimedb-client-api", "spacetimedb-client-api-messages", "spacetimedb-core", @@ -6585,8 +6553,8 @@ dependencies = [ "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", - "toml 0.8.22", - "tower-http", + "toml 0.8.23", + "tower-http 0.5.2", "tracing", ] @@ -6618,7 +6586,7 @@ dependencies = [ "itertools 0.12.1", "proptest", "proptest-derive", - "rand 0.9.1", + "rand 0.9.2", "smallvec", "spacetimedb-data-structures", "spacetimedb-lib 1.5.0", @@ -6634,12 +6602,12 @@ name = "spacetimedb-testing" version = "1.5.0" dependencies = [ "anyhow", - "clap 4.5.37", + "clap 4.5.50", "duct", "env_logger 0.10.2", "lazy_static", "log", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde_json", "serial_test", @@ -6662,12 +6630,12 @@ version = "1.5.0" dependencies = [ "anyhow", "bytes", - "clap 4.5.37", + "clap 4.5.50", "dialoguer", "flate2", "http-body-util", "indicatif", - "reqwest 0.12.15", + "reqwest 0.12.24", "self-replace", "semver", "serde", @@ -6675,7 +6643,7 @@ dependencies = [ "tar", "tempfile", "tokio", - "toml 0.8.22", + "toml 0.8.23", "tracing", "windows-sys 0.59.0", "zip", @@ -6779,14 +6747,14 @@ dependencies = [ "anyhow", "async-trait", "chrono", - "clap 4.5.37", + "clap 4.5.50", "console", "derive_more", "fs-err", "futures", "glob", "itertools 0.12.1", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "postgres-types", "quick-junit", "rusqlite", @@ -6805,9 +6773,9 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "str-buf" @@ -6881,14 +6849,14 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "subst" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e7942675ea19db01ef8cf15a1e6443007208e6c74568bd64162da26d40160d" +checksum = "0a9a86e5144f63c2d18334698269a8bfae6eece345c70b64821ea5b35054ec99" dependencies = [ "memchr", "unicode-width 0.1.14", @@ -6913,9 +6881,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" dependencies = [ "proc-macro2", "quote", @@ -6957,27 +6925,26 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "syntect" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +checksum = "656b45c05d95a5704399aeef6bd0ddec7b2b3531b7c9e900abbf7c4d2190c925" dependencies = [ "bincode", - "bitflags 1.3.2", "fancy-regex", "flate2", "fnv", "once_cell", "plist", - "regex-syntax 0.8.5", + "regex-syntax", "serde", "serde_derive", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.17", "walkdir", "yaml-rust", ] @@ -6999,7 +6966,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -7089,15 +7056,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -7113,9 +7080,9 @@ dependencies = [ "jiff-tzdb", "num-traits", "timezone_provider", - "tinystr 0.8.1", + "tinystr", "tzif", - "writeable 0.6.1", + "writeable", ] [[package]] @@ -7129,12 +7096,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.0.7", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.60.2", ] [[package]] @@ -7143,7 +7110,7 @@ version = "1.5.0" dependencies = [ "anyhow", "env_logger 0.10.2", - "rand 0.9.1", + "rand 0.9.2", "spacetimedb-sdk", "test-counter", "tokio", @@ -7180,11 +7147,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -7195,28 +7162,27 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -7230,9 +7196,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-ctl" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21f216790c8df74ce3ab25b534e0718da5a1916719771d3fec23315c99e468b" +checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" dependencies = [ "libc", "paste", @@ -7241,9 +7207,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" +checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" dependencies = [ "cc", "libc", @@ -7251,9 +7217,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" +checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" dependencies = [ "libc", "tikv-jemalloc-sys", @@ -7261,9 +7227,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -7278,15 +7244,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -7297,19 +7263,9 @@ name = "timezone_provider" version = "0.0.11" source = "git+https://github.com/boa-dev/temporal?tag=v0.0.11#1980ece2d4ebf796d0fe62645392599f64d7b130" dependencies = [ - "tinystr 0.8.1", + "tinystr", "zerotrie", - "zerovec 0.11.4", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec 0.10.4", + "zerovec", ] [[package]] @@ -7319,7 +7275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec 0.11.4", + "zerovec", ] [[package]] @@ -7334,9 +7290,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -7349,40 +7305,37 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", - "mio 1.0.3", - "parking_lot 0.12.3", + "mio 1.1.0", + "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "tokio-metrics" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7817b32d36c9b94744d7aa3f8fc13526aa0f5112009d7045f3c659413a6e44ac" +checksum = "a01bbf7db0b3f5eee8930a119fe99bfa1438de1095fe3d26b25e652a5933ef3f" dependencies = [ "futures-util", "pin-project-lite", @@ -7402,9 +7355,9 @@ dependencies = [ [[package]] name = "tokio-postgres" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" +checksum = "2b40d66d9b2cfe04b628173409368e58247e8eddbbd3b0e6c6ba1d09f20f6c9e" dependencies = [ "async-trait", "byteorder", @@ -7413,14 +7366,14 @@ dependencies = [ "futures-channel", "futures-util", "log", - "parking_lot 0.12.3", + "parking_lot 0.12.5", "percent-encoding", "phf", "pin-project-lite", "postgres-protocol", "postgres-types", - "rand 0.9.1", - "socket2 0.5.9", + "rand 0.9.2", + "socket2 0.6.1", "tokio", "tokio-util", "whoami", @@ -7428,9 +7381,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -7464,9 +7417,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -7487,44 +7440,95 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap 2.12.0", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.9.0", + "indexmap 2.12.0", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap 2.12.0", + "toml_datetime 0.7.3", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "toml_write" -version = "0.1.1" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "toml_writer" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "tower" @@ -7548,7 +7552,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.10.0", "bytes", "http 1.3.1", "http-body 1.0.1", @@ -7558,6 +7562,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -7596,20 +7618,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -7650,14 +7672,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -7705,9 +7727,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "trybuild" -version = "1.0.104" +version = "1.0.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ae08be68c056db96f0e6c6dd820727cca756ced9e1f4cc7fdd20e2a55e23898" +checksum = "4d66678374d835fe847e0dc8348fde2ceb5be4a7ec204437d8367f0d8df266a5" dependencies = [ "glob", "serde", @@ -7715,7 +7737,7 @@ dependencies = [ "serde_json", "target-triple", "termcolor", - "toml 0.8.22", + "toml 0.9.8", ] [[package]] @@ -7730,9 +7752,9 @@ dependencies = [ "httparse", "log", "native-tls", - "rand 0.9.1", + "rand 0.9.2", "sha1", - "thiserror 2.0.12", + "thiserror 2.0.17", "url", "utf-8", ] @@ -7745,9 +7767,9 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "typescript-test-app" @@ -7755,7 +7777,7 @@ version = "0.1.0" dependencies = [ "anyhow", "log", - "spacetimedb 1.3.0", + "spacetimedb 1.6.0", ] [[package]] @@ -7781,9 +7803,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06" [[package]] name = "unicode-normalization" @@ -7814,9 +7836,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -7842,22 +7864,23 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "clap 4.5.37", + "clap 4.5.50", "duct", "regex", "semver", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -7872,12 +7895,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8-width" version = "0.1.7" @@ -7898,11 +7915,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.4", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -7911,8 +7930,8 @@ version = "140.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8827809a2884fb68530d678a8ef15b1ed1344bbf844879194d68c140c6f844f9" dependencies = [ - "bindgen 0.72.1", - "bitflags 2.9.0", + "bindgen", + "bitflags 2.10.0", "fslock", "gzip-header", "home", @@ -7985,17 +8004,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] @@ -8006,35 +8025,36 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -8045,9 +8065,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8055,22 +8075,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] @@ -8118,7 +8138,7 @@ checksum = "3961bf864c790b5a06939f8f36d2a1a6be5bf0f926ddc25fb159b1766f2874db" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "synstructure 0.13.2", "thiserror 1.0.69", ] @@ -8130,9 +8150,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65a5a0689975b9fd93c02f5400cfd9669858b99607e54e7b892c6080cba598bb" dependencies = [ "ahash 0.8.12", - "bitflags 2.9.0", + "bitflags 2.10.0", "hashbrown 0.14.5", - "indexmap 2.9.0", + "indexmap 2.12.0", "semver", "serde", ] @@ -8157,19 +8177,19 @@ dependencies = [ "addr2line 0.22.0", "anyhow", "async-trait", - "bitflags 2.9.0", + "bitflags 2.10.0", "bumpalo", "cc", "cfg-if", "gimli 0.29.0", "hashbrown 0.14.5", - "indexmap 2.9.0", + "indexmap 2.12.0", "libc", "libm", "log", "mach2", "memfd", - "object", + "object 0.36.7", "once_cell", "paste", "postcard", @@ -8218,7 +8238,7 @@ dependencies = [ "serde", "serde_derive", "sha2", - "toml 0.8.22", + "toml 0.8.23", "windows-sys 0.52.0", "zstd", ] @@ -8232,7 +8252,7 @@ dependencies = [ "anyhow", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "wasmtime-component-util", "wasmtime-wit-bindgen", "wit-parser", @@ -8260,7 +8280,7 @@ dependencies = [ "cranelift-wasm", "gimli 0.29.0", "log", - "object", + "object 0.36.7", "smallvec", "target-lexicon", "thiserror 1.0.69", @@ -8280,9 +8300,9 @@ dependencies = [ "cranelift-bitset", "cranelift-entity", "gimli 0.29.0", - "indexmap 2.9.0", + "indexmap 2.12.0", "log", - "object", + "object 0.36.7", "postcard", "rustc-demangle", "serde", @@ -8349,7 +8369,7 @@ checksum = "6879a8e168aef3fe07335343b7fbede12fa494215e83322e173d4018e124a846" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -8360,15 +8380,15 @@ checksum = "3f571f63ac1d532e986eb3973bbef3a45e4ae83de521a8d573b0fe0594dc9608" dependencies = [ "anyhow", "heck 0.4.1", - "indexmap 2.9.0", + "indexmap 2.12.0", "wit-parser", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -8386,12 +8406,11 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5df295f8451142f1856b1bd86a606dfe9587d439bc036e319c827700dbd555e" +checksum = "00f1243ef785213e3a32fa0396093424a3a6ea566f9948497e5a2309261a4c97" dependencies = [ - "core-foundation 0.10.0", - "home", + "core-foundation 0.10.1", "jni", "log", "ndk-context", @@ -8427,11 +8446,11 @@ dependencies = [ [[package]] name = "whoami" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ - "redox_syscall 0.5.12", + "libredox", "wasite", "web-sys", ] @@ -8454,11 +8473,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8469,145 +8488,152 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.58.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", ] [[package]] -name = "windows-core" -version = "0.58.0" +name = "windows-collections" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core 0.61.2", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] -name = "windows-implement" -version = "0.58.0" +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] -name = "windows-implement" -version = "0.60.0" +name = "windows-future" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", ] [[package]] -name = "windows-interface" -version = "0.58.0" +name = "windows-implement" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-registry" -version = "0.4.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-result 0.3.2", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-core 0.61.2", + "windows-link 0.1.3", ] [[package]] -name = "windows-result" -version = "0.2.0" +name = "windows-registry" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ - "windows-targets 0.52.6", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] -name = "windows-strings" -version = "0.1.0" +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -8646,6 +8672,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -8694,18 +8738,28 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", ] [[package]] @@ -8728,9 +8782,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -8752,9 +8806,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -8776,9 +8830,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -8788,9 +8842,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -8812,9 +8866,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -8836,9 +8890,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -8860,9 +8914,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -8884,15 +8938,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -8914,13 +8968,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.0", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "wit-parser" @@ -8930,7 +8981,7 @@ checksum = "e5aaf02882453eaeec4fe30f1e4263cfd8b8ea36dd00e1fe7d902d9cb498bccd" dependencies = [ "anyhow", "id-arena", - "indexmap 2.9.0", + "indexmap 2.12.0", "log", "semver", "serde", @@ -8940,18 +8991,6 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - [[package]] name = "writeable" version = "0.6.1" @@ -8969,12 +9008,12 @@ dependencies = [ [[package]] name = "xattr" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.0.7", + "rustix 1.1.2", ] [[package]] @@ -9013,18 +9052,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive 0.7.5", - "zerofrom", -] - [[package]] name = "yoke" version = "0.8.0" @@ -9033,22 +9060,10 @@ checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive 0.8.0", + "yoke-derive", "zerofrom", ] -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", - "synstructure 0.13.2", -] - [[package]] name = "yoke-derive" version = "0.8.0" @@ -9057,28 +9072,28 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "synstructure 0.13.2", ] [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -9098,15 +9113,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", "synstructure 0.13.2", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -9119,7 +9134,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] @@ -9129,17 +9144,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke 0.7.5", + "yoke", "zerofrom", - "zerovec-derive 0.10.3", ] [[package]] @@ -9148,20 +9154,9 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ - "yoke 0.8.0", + "yoke", "zerofrom", - "zerovec-derive 0.11.1", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.101", + "zerovec-derive", ] [[package]] @@ -9172,14 +9167,14 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.108", ] [[package]] name = "zip" -version = "2.6.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ "aes", "arbitrary", @@ -9188,14 +9183,16 @@ dependencies = [ "crc32fast", "crossbeam-utils", "deflate64", + "displaydoc", "flate2", - "getrandom 0.3.2", + "getrandom 0.3.4", "hmac", - "indexmap 2.9.0", + "indexmap 2.12.0", "lzma-rs", "memchr", "pbkdf2", "sha1", + "thiserror 2.0.17", "time", "xz2", "zeroize", @@ -9247,9 +9244,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.15+zstd.1.5.7" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/tools/ci/README.md b/tools/ci/README.md new file mode 100644 index 00000000000..d32c9dcf82d --- /dev/null +++ b/tools/ci/README.md @@ -0,0 +1,164 @@ +# SpacetimeDB's cargo ci + +## Overview + +This document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options. + +## `cargo ci` + +SpacetimeDB CI tasks + +This tool provides several subcommands for automating CI workflows in SpacetimeDB. + +It may be invoked via `cargo ci `, or simply `cargo ci` to run all subcommands in sequence. It is mostly designed to be run in CI environments via the github workflows, but can also be run locally + +**Usage:** +```bash +Usage: cargo ci [OPTIONS] [COMMAND] +``` + +**Options:** + +- `--skip`: Skip specified subcommands when running all + +When no subcommand is specified, all subcommands are run in sequence. This option allows specifying subcommands to skip when running all. For example, to skip the `unreal-tests` subcommand, use `--skip unreal-tests`. + +- `--help`: Print help (see a summary with '-h') + +### `test` + +Runs tests + +Runs rust tests, codegens csharp sdk and runs csharp tests. This does not include Unreal tests. This expects to run in a clean git state. + +**Usage:** +```bash +Usage: test +``` + +**Options:** + +- `--help`: Print help (see a summary with '-h') + +### `lint` + +Lints the codebase + +Runs rustfmt, clippy, csharpier and generates rust docs to ensure there are no warnings. + +**Usage:** +```bash +Usage: lint +``` + +**Options:** + +- `--help`: Print help (see a summary with '-h') + +### `wasm-bindings` + +Tests Wasm bindings + +Runs tests for the codegen crate and builds a test module with the wasm bindings. + +**Usage:** +```bash +Usage: wasm-bindings +``` + +**Options:** + +- `--help`: Print help (see a summary with '-h') + +### `smoketests` + +Runs smoketests + +Executes the smoketests suite with some default exclusions. + +**Usage:** +```bash +Usage: smoketests [ARGS]... +``` + +**Options:** + +- `args`: Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker` +- `--help`: Print help (see a summary with '-h') + +### `update-flow` + +Tests the update flow + +Tests the self-update flow by building the spacetimedb-update binary for the specified target, by default the current target, and performing a self-install into a temporary directory. + +**Usage:** +```bash +Usage: update-flow [OPTIONS] +``` + +**Options:** + +- `--target`: Target triple to build for, by default the current target. Used by github workflows to check the update flow on multiple platforms. +- `--github-token-auth`: Whether to enable github token authentication feature when building the update binary. By default this is enabled. +- `--help`: Print help (see a summary with '-h') + +### `unreal-tests` + +Run Unreal Engine related tests + +This assumes the UE4 environment is already set up + +This is designed to run in the github actions environment, but should work locally if the Unreal environment is set up correctly. + +**Usage:** +```bash +Usage: unreal-tests +``` + +**Options:** + +- `--help`: Print help (see a summary with '-h') + +### `cli-docs` + +**Usage:** +```bash +Usage: cli-docs +``` + +**Options:** + +- `--help`: + +### `self-docs` + +**Usage:** +```bash +Usage: self-docs [OPTIONS] +``` + +**Options:** + +- `--check`: Only check for changes, do not generate the docs +- `--help`: Print help (see a summary with '-h') + +### `help` + +**Usage:** +```bash +Usage: help [COMMAND]... +``` + +**Options:** + +- `subcommand`: + + +--- + +This document is auto-generated by running: + +```bash +cargo ci self-docs +``` \ No newline at end of file diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs new file mode 100644 index 00000000000..f6beb2eb8ef --- /dev/null +++ b/tools/ci/src/ci_docs.rs @@ -0,0 +1,68 @@ +use clap::{Command, CommandFactory}; + +use crate::Cli; + +pub fn generate_cli_docs() -> String { + let mut cli = Cli::command(); + let usage = generate_markdown(&mut cli, 2); + + format!( + "\ +# SpacetimeDB's cargo ci + +## Overview + +This document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options. + +{} +--- + +This document is auto-generated by running: + +```bash +cargo ci self-docs +```", + usage + ) +} + +fn generate_markdown(cmd: &mut Command, heading_level: usize) -> String { + let mut out = String::new(); + + let heading = "#".repeat(heading_level); + out.push_str(&format!("{} `{}`\n\n", heading, cmd.get_name())); + + if let Some(long_about) = cmd.get_long_about() { + out.push_str(&format!("{}\n\n", long_about)); + } + + out.push_str(&format!("**Usage:**\n```bash\n{}\n```\n\n", cmd.render_usage())); + + let mut options = String::new(); + for arg in cmd.get_arguments() { + let names = arg + .get_long() + .map(|l| format!("--{}", l)) + .or_else(|| arg.get_short().map(|s| format!("-{}", s))) + .unwrap_or_else(|| arg.get_id().to_string()); + let help = arg.get_long_help().unwrap_or_default(); + options.push_str(&format!( + "- `{}`: {}\n{}", + names, + help, + if help.to_string().lines().count() > 1 { "\n" } else { "" } + )); + } + + if !options.is_empty() { + out.push_str("**Options:**\n\n"); + out.push_str(&options); + out.push('\n'); + } + + for sub in cmd.get_subcommands_mut() { + out.push_str(&generate_markdown(sub, heading_level + 1)); + } + + out +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index ac5c73af01b..439035d15bf 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,41 +1,97 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use std::collections::HashMap; -use std::env; +use std::{env, fs}; +use std::path::Path; +const README_PATH: &str = "tools/ci/README.md"; + +mod ci_docs; + +/// SpacetimeDB CI tasks +/// +/// This tool provides several subcommands for automating CI workflows in SpacetimeDB. +/// +/// It may be invoked via `cargo ci `, or simply `cargo ci` to run all subcommands in +/// sequence. It is mostly designed to be run in CI environments via the github workflows, but can +/// also be run locally #[derive(Parser)] -#[command( - name = "spacetimedb-ci", - about = "SpacetimeDB CI tasks", - subcommand_required = false, - arg_required_else_help = false -)] +#[command(name = "cargo ci", subcommand_required = false, arg_required_else_help = false)] struct Cli { #[command(subcommand)] cmd: Option, + /// Skip specified subcommands when running all + /// + /// When no subcommand is specified, all subcommands are run in sequence. This option allows + /// specifying subcommands to skip when running all. For example, to skip the `unreal-tests` + /// subcommand, use `--skip unreal-tests`. #[arg(long)] skip: Vec, } #[derive(Subcommand)] enum CiCmd { + /// Runs tests + /// + /// Runs rust tests, codegens csharp sdk and runs csharp tests. + /// This does not include Unreal tests. + /// This expects to run in a clean git state. Test, - Lints, + /// Lints the codebase + /// + /// Runs rustfmt, clippy, csharpier and generates rust docs to ensure there are no warnings. + Lint, + /// Tests Wasm bindings + /// + /// Runs tests for the codegen crate and builds a test module with the wasm bindings. WasmBindings, + /// Runs smoketests + /// + /// Executes the smoketests suite with some default exclusions. Smoketests { - #[arg(trailing_var_arg = true)] + #[arg( + trailing_var_arg = true, + long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" + )] args: Vec, }, + /// Tests the update flow + /// + /// Tests the self-update flow by building the spacetimedb-update binary for the specified + /// target, by default the current target, and performing a self-install into a temporary + /// directory. UpdateFlow { - #[arg(long)] + #[arg( + long, + long_help = "Target triple to build for, by default the current target. Used by github workflows to check the update flow on multiple platforms." + )] target: Option, - #[arg(long, default_value = "true")] + #[arg( + long, + default_value = "true", + long_help = "Whether to enable github token authentication feature when building the update binary. By default this is enabled." + )] github_token_auth: bool, }, + /// Run Unreal Engine related tests + /// + /// This assumes the UE4 environment is already set up + /// + /// This is designed to run in the github actions environment, but should work locally if the + /// Unreal environment is set up correctly. UnrealTests, + /// Generates CLI documentation and checks for changes CliDocs, + SelfDocs { + #[arg( + long, + default_value_t = false, + long_help = "Only check for changes, do not generate the docs" + )] + check: bool, + }, } macro_rules! run { @@ -90,11 +146,14 @@ fn main() -> Result<()> { run!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; } - Some(CiCmd::Lints) => { + Some(CiCmd::Lint) => { run!("cargo fmt --all -- --check")?; run!("cargo clippy --all --tests --benches -- -D warnings")?; run!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; - run!("cd crates/bindings && cargo doc", &[("RUSTDOCFLAGS", "hey")])?; + run!( + "cd crates/bindings && cargo doc", + &[("RUSTDOCFLAGS", "--deny warnings")] + )?; } Some(CiCmd::WasmBindings) => { @@ -105,7 +164,10 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { args }) => { // Note: clear_database and replication only work in private - run!(&format!("python -m smoketests {} -x clear_database replication", args.join(" ")))?; + run!(&format!( + "python -m smoketests {} -x clear_database replication", + args.join(" ") + ))?; } Some(CiCmd::UpdateFlow { @@ -123,12 +185,12 @@ fn main() -> Result<()> { run!(&format!( "cargo build {github_token_auth_flag}--target {target} -p spacetimedb-update" ))?; + // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. + // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it + // happens very frequently on the `macos-runner`, but we haven't seen it on any others). run!(&format!( r#" ROOT_DIR="$(mktemp -d)" -# NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. -# My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it -# happens very frequently on the `macos-runner`, but we haven't seen it on any others). cargo run {github_token_auth_flag}--target {target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes "${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help "# @@ -165,6 +227,23 @@ fi )?; } + Some(CiCmd::SelfDocs { check }) => { + let readme_content = ci_docs::generate_cli_docs(); + let path = Path::new(README_PATH); + + if check { + let existing = fs::read_to_string(path).unwrap_or_default(); + if existing != readme_content { + bail!("README.md is out of date. Please run `cargo ci self-docs` to update it."); + } else { + log::info!("README.md is up to date."); + } + } else { + fs::write(path, readme_content)?; + log::info!("Wrote CLI docs to {}", path.display()); + } + } + None => run_all_clap_subcommands(&cli.skip)?, } From 852c13cefc756f61f830ce290348393162266f7d Mon Sep 17 00:00:00 2001 From: Kasama Date: Wed, 29 Oct 2025 12:06:42 -0300 Subject: [PATCH 004/160] fix: PR comments --- .github/workflows/ci.yml | 18 ++++++------------ tools/ci/src/main.rs | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80b0cf5f108..337b4ff452e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,8 +25,10 @@ jobs: strategy: matrix: include: - - { runner: spacetimedb-runner, smoketest_args: --docker } - - { runner: windows-latest, smoketest_args: --no-build-cli } + - runner: spacetimedb-runner + smoketest_args: "--docker -x clear_database replication" + - runner: windows-latest + smoketest_args: "--no-build-cli -x clear_database replication" runner: [spacetimedb-runner, windows-latest] runs-on: ${{ matrix.runner }} steps: @@ -208,7 +210,7 @@ jobs: - name: Build spacetimedb-update env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: cargo ci update-flow --target=${{ matrix.target }} + run: cargo ci update-flow --target=${{ matrix.target }} --github-token-auth unreal_engine_tests: name: Unreal Engine Tests @@ -381,12 +383,4 @@ jobs: - name: Check for docs change run: | - cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md - pnpm format - git status - if git diff --exit-code HEAD; then - echo "No docs changes detected" - else - echo "It looks like the CLI docs have changed:" - exit 1 - fi + cargo ci cli-docs diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 439035d15bf..b939b94e5d8 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -2,8 +2,8 @@ use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use std::collections::HashMap; -use std::{env, fs}; use std::path::Path; +use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -70,8 +70,8 @@ enum CiCmd { target: Option, #[arg( long, - default_value = "true", - long_help = "Whether to enable github token authentication feature when building the update binary. By default this is enabled." + default_value = "false", + long_help = "Whether to enable github token authentication feature when building the update binary. By default this is disabled." )] github_token_auth: bool, }, @@ -139,7 +139,6 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { - run!("sudo mkdir -p /stdb && sudo chmod 777 /stdb")?; run!("cargo test --all -- --skip unreal")?; run!("bash tools/check-diff.sh")?; run!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; @@ -162,19 +161,20 @@ fn main() -> Result<()> { run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } - Some(CiCmd::Smoketests { args }) => { + Some(CiCmd::Smoketests { mut args }) => { + let default_args = ["-x", "clear_database", "replication"]; + if args.is_empty() { + args = default_args.iter().map(ToString::to_string).collect(); + } // Note: clear_database and replication only work in private - run!(&format!( - "python -m smoketests {} -x clear_database replication", - args.join(" ") - ))?; + run!(&format!("python -m smoketests {}", args.join(" ")))?; } Some(CiCmd::UpdateFlow { target, github_token_auth, }) => { - let target = target.unwrap_or_else(|| env!("TARGET").to_string()); + let target = target.map(|t| format!("--target {t}")).unwrap_or_default(); let github_token_auth_flag = if github_token_auth { "--features github-token-auth " } else { @@ -183,7 +183,7 @@ fn main() -> Result<()> { run!(&format!("echo 'checking update flow for target: {target}'"))?; run!(&format!( - "cargo build {github_token_auth_flag}--target {target} -p spacetimedb-update" + "cargo build {github_token_auth_flag}{target} -p spacetimedb-update" ))?; // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it From 2b2250a6ba78c68dda83ca00369d908b8b13266d Mon Sep 17 00:00:00 2001 From: Kasama Date: Wed, 29 Oct 2025 12:20:56 -0300 Subject: [PATCH 005/160] update ci to call cargo for unreal test --- .github/workflows/ci.yml | 46 +++------------------------------------- 1 file changed, 3 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 337b4ff452e..a576f9e1971 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,9 @@ jobs: matrix: include: - runner: spacetimedb-runner - smoketest_args: "--docker -x clear_database replication" + smoketest_args: '--docker -x clear_database replication' - runner: windows-latest - smoketest_args: "--no-build-cli -x clear_database replication" + smoketest_args: '--no-build-cli -x clear_database replication' runner: [spacetimedb-runner, windows-latest] runs-on: ${{ matrix.runner }} steps: @@ -265,47 +265,7 @@ jobs: env: UE_ROOT_PATH: /home/ue4/UnrealEngine run: | - - apt-get update - apt-get install -y acl curl ca-certificates - - REPO="$GITHUB_WORKSPACE" - # Let ue4 read/write the workspace & tool caches without changing ownership - for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do - [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - curl -sSf https://sh.rustup.rs | sh -s -- -y - fi - rustup show >/dev/null - git config --global --add safe.directory "$GITHUB_WORKSPACE" || true - - cd "$GITHUB_WORKSPACE/sdks/unreal" - cargo --version - cargo test - ' + cargo ci unreal-tests ci_command_docs: name: Check CI command docs From a9b46b276ed32d90692c24a5af71870f623a9abb Mon Sep 17 00:00:00 2001 From: Kasama Date: Wed, 29 Oct 2025 12:27:13 -0300 Subject: [PATCH 006/160] update cargo ci docs --- tools/ci/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/README.md b/tools/ci/README.md index d32c9dcf82d..0a4a83b1264 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -100,7 +100,7 @@ Usage: update-flow [OPTIONS] **Options:** - `--target`: Target triple to build for, by default the current target. Used by github workflows to check the update flow on multiple platforms. -- `--github-token-auth`: Whether to enable github token authentication feature when building the update binary. By default this is enabled. +- `--github-token-auth`: Whether to enable github token authentication feature when building the update binary. By default this is disabled. - `--help`: Print help (see a summary with '-h') ### `unreal-tests` From fe350f5e2333d851f2097548f46147a328dfa7b1 Mon Sep 17 00:00:00 2001 From: Kasama Date: Thu, 6 Nov 2025 11:37:20 -0300 Subject: [PATCH 007/160] fix: temp disable unreal engine tests running from `cargo ci` --- .github/workflows/ci.yml | 77 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c6ece0b54e..25319592ddd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,7 +135,82 @@ jobs: - name: Run cargo test #Note: Unreal tests will be run separately - run: cargo ci test + run: | + apt-get update + apt-get install -y acl curl ca-certificates + + REPO="$GITHUB_WORKSPACE" + # Let ue4 read/write the workspace & tool caches without changing ownership + for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do + [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true + done + + # Rust tool caches live under the runner tool cache so they persist + export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" + export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" + mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" + chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" + + # Make sure the UE build script is executable (and parents traversable) + UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" + chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true + chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true + + + + # Make sure the UE build script is executable (and parents traversable) + UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" + chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true + chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true + + # Run the build & tests as ue4 (who owns the UE tree) + sudo -E -H -u ue4 env \ + HOME=/home/ue4 \ + XDG_CONFIG_HOME=/home/ue4/.config \ + CARGO_HOME="$CARGO_HOME" \ + RUSTUP_HOME="$RUSTUP_HOME" \ + mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" + done + done + + # Rust tool caches live under the runner tool cache so they persist + export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" + export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" + mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" + chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" + + # Make sure the UE build script is executable (and parents traversable) + UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" + chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true + chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true + + # Run the build & tests as ue4 (who owns the UE tree) + sudo -E -H -u ue4 env \ + HOME=/home/ue4 \ + XDG_CONFIG_HOME=/home/ue4/.config \ + CARGO_HOME="$CARGO_HOME" \ + RUSTUP_HOME="$RUSTUP_HOME" \ + PATH="$CARGO_HOME/bin:$PATH" \ + bash -lc ' + set -euxo pipefail + # Install rustup for ue4 if needed (uses the shared caches) + if ! command -v cargo >/dev/null 2>&1; then + CARGO_HOME="$CARGO_HOME" \ + RUSTUP_HOME="$RUSTUP_HOME" \ + PATH="$CARGO_HOME/bin:$PATH" \ + bash -lc ' + set -euxo pipefail + # Install rustup for ue4 if needed (uses the shared caches) + if ! command -v cargo >/dev/null 2>&1; then + curl -sSf https://sh.rustup.rs | sh -s -- -y + fi + rustup show >/dev/null + git config --global --add safe.directory "$GITHUB_WORKSPACE" || true + + cd "$GITHUB_WORKSPACE/sdks/unreal" + cargo --version + cargo test + ' lints: name: Lints From ac25ba896acda1bc75712a856b30a5a4d379c19e Mon Sep 17 00:00:00 2001 From: Kasama Date: Thu, 6 Nov 2025 17:30:01 -0300 Subject: [PATCH 008/160] fix cargo lock --- Cargo.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c2d03fb6b56..d45a8684825 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -854,6 +854,18 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "ci" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap 4.5.50", + "duct", + "log", + "regex", +] + [[package]] name = "ciborium" version = "0.2.2" From 93e12b7d0b379353c02d4df66a476b760b5e7c0c Mon Sep 17 00:00:00 2001 From: Kasama Date: Thu, 6 Nov 2025 18:09:27 -0300 Subject: [PATCH 009/160] update smoketests call --- .github/workflows/ci.yml | 6 +++--- tools/ci/build.rs | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 tools/ci/build.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25319592ddd..a1e6d7902b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,9 @@ jobs: matrix: include: - runner: spacetimedb-runner - smoketest_args: '--docker -x clear_database replication' + smoketest_args: '--docker' - runner: windows-latest - smoketest_args: '--no-build-cli -x clear_database replication' + smoketest_args: '--no-build-cli' runner: [spacetimedb-runner, windows-latest] runs-on: ${{ matrix.runner }} steps: @@ -87,7 +87,7 @@ jobs: - name: Install python deps run: python -m pip install psycopg2-binary xmltodict - name: Run smoketests - run: cargo ci smoketests -- ${{ matrix.smoketest_args }} + run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication - name: Stop containers (Linux) if: always() && runner.os == 'Linux' diff --git a/tools/ci/build.rs b/tools/ci/build.rs deleted file mode 100644 index b821cf9a10d..00000000000 --- a/tools/ci/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[allow(clippy::disallowed_macros)] -fn main() { - println!("cargo:rustc-env=TARGET={}", std::env::var("TARGET").unwrap_or_default()); -} From 8bf73674a55f42dcf71d0867a508ced5353c1e8d Mon Sep 17 00:00:00 2001 From: Kasama Date: Thu, 6 Nov 2025 19:21:52 -0300 Subject: [PATCH 010/160] add directory check for cli-docs --- tools/ci/src/main.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index b939b94e5d8..2c081a198ea 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -83,7 +83,13 @@ enum CiCmd { /// Unreal environment is set up correctly. UnrealTests, /// Generates CLI documentation and checks for changes - CliDocs, + CliDocs { + #[arg( + long, + long_help = "specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is located)" + )] + spacetime_path: Option, + }, SelfDocs { #[arg( long, @@ -161,11 +167,7 @@ fn main() -> Result<()> { run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } - Some(CiCmd::Smoketests { mut args }) => { - let default_args = ["-x", "clear_database", "replication"]; - if args.is_empty() { - args = default_args.iter().map(ToString::to_string).collect(); - } + Some(CiCmd::Smoketests { args }) => { // Note: clear_database and replication only work in private run!(&format!("python -m smoketests {}", args.join(" ")))?; } @@ -210,7 +212,18 @@ cargo run {github_token_auth_flag}--target {target} -p spacetimedb-update -- sel run!("sudo -E -H -u ue4 env HOME=/home/ue4 CARGO_HOME=\"$CARGO_HOME\" RUSTUP_HOME=\"$RUSTUP_HOME\" PATH=\"$CARGO_HOME/bin:$PATH\" bash -lc 'set -euxo pipefail; if ! command -v cargo >/dev/null 2>&1; then curl -sSf https://sh.rustup.rs | sh -s -- -y; fi; rustup show >/dev/null; git config --global --add safe.directory \"$GITHUB_WORKSPACE\" || true; cd \"$GITHUB_WORKSPACE/sdks/unreal\"; cargo --version; cargo test'")?; } - Some(CiCmd::CliDocs) => { + Some(CiCmd::CliDocs { spacetime_path }) => { + if let Some(path) = spacetime_path { + env::set_current_dir(path).ok(); + } + let current_dir = env::current_dir().expect("No current directory!"); + let dir_name = current_dir.file_name().expect("No current directory!"); + if dir_name != "SpacetimeDB" && dir_name != "public" { + anyhow::bail!( + "You must execute this binary from inside of the SpacetimeDB directory, or use --spacetime-path" + ); + } + run!("pnpm install --recursive")?; run!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; run!("pnpm format")?; From a733f123d85fafd858a626bd37348b9ae8719822 Mon Sep 17 00:00:00 2001 From: Kasama Date: Thu, 6 Nov 2025 19:25:12 -0300 Subject: [PATCH 011/160] update cargo ci docs --- tools/ci/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/ci/README.md b/tools/ci/README.md index 0a4a83b1264..c90622e3b73 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -124,12 +124,13 @@ Usage: unreal-tests **Usage:** ```bash -Usage: cli-docs +Usage: cli-docs [OPTIONS] ``` **Options:** -- `--help`: +- `--spacetime-path`: specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is located) +- `--help`: Print help (see a summary with '-h') ### `self-docs` From 44eafca0d0c517b81a64fd0c9e4198bc70147731 Mon Sep 17 00:00:00 2001 From: Kasama Date: Wed, 12 Nov 2025 15:14:18 -0300 Subject: [PATCH 012/160] remove unreal tests from cargo ci initially --- tools/ci/src/main.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 2c081a198ea..d405ec4ff31 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -75,13 +75,6 @@ enum CiCmd { )] github_token_auth: bool, }, - /// Run Unreal Engine related tests - /// - /// This assumes the UE4 environment is already set up - /// - /// This is designed to run in the github actions environment, but should work locally if the - /// Unreal environment is set up correctly. - UnrealTests, /// Generates CLI documentation and checks for changes CliDocs { #[arg( @@ -199,19 +192,6 @@ cargo run {github_token_auth_flag}--target {target} -p spacetimedb-update -- sel ))?; } - Some(CiCmd::UnrealTests) => { - run!("for p in \"$GITHUB_WORKSPACE\" \"${RUNNER_TEMP:-/__t}\" \"${RUNNER_TOOL_CACHE:-/__t}\"; do [ -d \"$p\" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX \"$p\" || true; done")?; - - run!("export CARGO_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/cargo\"")?; - run!("export RUSTUP_HOME=\"${RUNNER_TOOL_CACHE:-/__t}/rustup\"")?; - run!("mkdir -p \"$CARGO_HOME\" \"$RUSTUP_HOME\"")?; - - run!("chmod a+rx \"$UE_ROOT_PATH\" \"$UE_ROOT_PATH/Engine\" \"$UE_ROOT_PATH/Engine/Build\" \"$UE_ROOT_PATH/Engine/Build/BatchFiles/Linux\" || true")?; - run!("chmod a+rx \"$UE_ROOT_PATH/Engine/Build/BatchFiles/Linux/Build.sh\" || true")?; - - run!("sudo -E -H -u ue4 env HOME=/home/ue4 CARGO_HOME=\"$CARGO_HOME\" RUSTUP_HOME=\"$RUSTUP_HOME\" PATH=\"$CARGO_HOME/bin:$PATH\" bash -lc 'set -euxo pipefail; if ! command -v cargo >/dev/null 2>&1; then curl -sSf https://sh.rustup.rs | sh -s -- -y; fi; rustup show >/dev/null; git config --global --add safe.directory \"$GITHUB_WORKSPACE\" || true; cd \"$GITHUB_WORKSPACE/sdks/unreal\"; cargo --version; cargo test'")?; - } - Some(CiCmd::CliDocs { spacetime_path }) => { if let Some(path) = spacetime_path { env::set_current_dir(path).ok(); From 7789f2f41cc180b36b1d5dbea3ee09ae620fa985 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:04:59 -0800 Subject: [PATCH 013/160] [bfops/fix-list-tests]: Fix running smoketests --list --- smoketests/__main__.py | 67 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index 6161dc5a7aa..8f19238adc8 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -79,6 +79,39 @@ def main(): parser.add_argument("--spacetime-login", action="store_true", help="Use `spacetime login` for these tests (and disable tests that don't work with that)") args = parser.parse_args() + if args.docker: + # have docker logs print concurrently with the test output + if args.compose_file: + smoketests.COMPOSE_FILE = args.compose_file + if not args.no_docker_logs: + if args.compose_file: + subprocess.Popen(["docker", "compose", "-f", args.compose_file, "logs", "-f"]) + else: + docker_container = check_docker() + subprocess.Popen(["docker", "logs", "-f", docker_container]) + smoketests.HAVE_DOCKER = True + + if not args.skip_dotnet: + smoketests.HAVE_DOTNET = check_dotnet() + if not smoketests.HAVE_DOTNET: + print("no suitable dotnet installation found") + exit(1) + + add_prefix = lambda testlist: [TESTPREFIX + test for test in testlist] + import fnmatch + excludelist = add_prefix(args.exclude) + testlist = add_prefix(args.test) + + loader = ExclusionaryTestLoader(excludelist) + loader.testNamePatterns = args.testNamePatterns + + tests = loader.loadTestsFromNames(testlist) + if args.list: + print("Selected tests:\n") + for test in itertools.chain(*itertools.chain(*tests)): + print(f"{test}") + exit(0) + if not args.no_build_cli: logging.info("Compiling spacetime cli...") smoketests.run_cmd("cargo", "build", cwd=TEST_DIR.parent, capture_stderr=False) @@ -100,18 +133,6 @@ def main(): os.environ["SPACETIME_SKIP_CLIPPY"] = "1" - if args.docker: - # have docker logs print concurrently with the test output - if args.compose_file: - smoketests.COMPOSE_FILE = args.compose_file - if not args.no_docker_logs: - if args.compose_file: - subprocess.Popen(["docker", "compose", "-f", args.compose_file, "logs", "-f"]) - else: - docker_container = check_docker() - subprocess.Popen(["docker", "logs", "-f", docker_container]) - smoketests.HAVE_DOCKER = True - with tempfile.NamedTemporaryFile(mode="w+b", suffix=".toml", buffering=0, delete_on_close=False) as config_file: with BASE_STDB_CONFIG_PATH.open("rb") as src, config_file.file as dst: shutil.copyfileobj(src, dst) @@ -130,28 +151,6 @@ def main(): smoketests.STDB_CONFIG = Path(config_file.name).read_text() build_template_target() - - if not args.skip_dotnet: - smoketests.HAVE_DOTNET = check_dotnet() - if not smoketests.HAVE_DOTNET: - print("no suitable dotnet installation found") - exit(1) - - add_prefix = lambda testlist: [TESTPREFIX + test for test in testlist] - import fnmatch - excludelist = add_prefix(args.exclude) - testlist = add_prefix(args.test) - - loader = ExclusionaryTestLoader(excludelist) - loader.testNamePatterns = args.testNamePatterns - - tests = loader.loadTestsFromNames(testlist) - if args.list: - print("Selected tests:\n") - for test in itertools.chain(*itertools.chain(*tests)): - print(f"{test}") - exit(0) - buffer = not args.show_all_output verbosity = 2 From 8bd2b44974d9290378fce97730936039b0329da6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:24:58 -0800 Subject: [PATCH 014/160] [bfops/share-python-deps]: Add smoketests/requirements.txt --- .github/workflows/ci.yml | 6 ++++-- smoketests/requirements.txt | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 smoketests/requirements.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f18f1209e8..1d4409bb2c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,10 +109,12 @@ jobs: with: { python-version: '3.12' } if: runner.os == 'Windows' - name: Install python deps - run: python -m pip install psycopg2-binary xmltodict + run: | + python -m venv venv + venv/pip install -r smoketests/requirements.txt - name: Run smoketests # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams + run: venv/bin/python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down diff --git a/smoketests/requirements.txt b/smoketests/requirements.txt new file mode 100644 index 00000000000..697856f84f6 --- /dev/null +++ b/smoketests/requirements.txt @@ -0,0 +1,3 @@ +psycopg2-binary +toml +xmltodict From a4c3ca8fa19e5c4993094daf8f5da5787fb2aa29 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:36:34 -0800 Subject: [PATCH 015/160] [bfops/share-python-deps]: fix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d4409bb2c4..b0c2497c258 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,7 @@ jobs: - name: Install python deps run: | python -m venv venv - venv/pip install -r smoketests/requirements.txt + venv/bin/pip install -r smoketests/requirements.txt - name: Run smoketests # Note: clear_database and replication only work in private run: venv/bin/python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams From db51dc7c14de10b47c15c0465cc281e952f63e37 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:38:45 -0800 Subject: [PATCH 016/160] [bfops/fix-list-tests]: restore --- smoketests/__main__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index 8f19238adc8..53b7ed845c5 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -57,6 +57,15 @@ def _convert_select_pattern(pattern): TESTPREFIX = "smoketests.tests." + +def _iter_all_tests(suite_or_case): + """Yield all individual tests from possibly nested TestSuite structures.""" + if isinstance(suite_or_case, unittest.TestSuite): + for t in suite_or_case: + yield from _iter_all_tests(t) + else: + yield suite_or_case + def main(): tests = [fname.removesuffix(".py") for fname in os.listdir(TEST_DIR / "tests") if fname.endswith(".py") and fname != "__init__.py"] @@ -108,8 +117,9 @@ def main(): tests = loader.loadTestsFromNames(testlist) if args.list: print("Selected tests:\n") - for test in itertools.chain(*itertools.chain(*tests)): - print(f"{test}") + for test in _iter_all_tests(tests): + name = test.id() + print(f"{name}") exit(0) if not args.no_build_cli: From fae85d5902e6198d0c2703d86b4ac6fa6971e313 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:51:15 -0800 Subject: [PATCH 017/160] [bfops/fix-list-tests]: print any failed --- smoketests/__main__.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index 53b7ed845c5..cc3b0d004b6 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -15,6 +15,7 @@ import tempfile from pathlib import Path import shutil +import traceback def check_docker(): docker_ps = smoketests.run_cmd("docker", "ps", "--format=json") @@ -116,11 +117,22 @@ def main(): tests = loader.loadTestsFromNames(testlist) if args.list: - print("Selected tests:\n") + failed_cls = getattr(unittest.loader, "_FailedTest", None) + any_failed = False for test in _iter_all_tests(tests): name = test.id() - print(f"{name}") - exit(0) + if isinstance(test, failed_cls): + any_failed = True + print('') + print("Failed to construct %s:" % test.id()) + exc = getattr(test, "_exception", None) + if exc is not None: + tb = ''.join(traceback.format_exception(exc)) + print(tb.rstrip()) + print('') + else: + print(f"{name}") + exit(1 if any_failed else 0) if not args.no_build_cli: logging.info("Compiling spacetime cli...") From e7f9f9f95897d24d8ee128b9aa333928d030fe80 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:55:03 -0800 Subject: [PATCH 018/160] [bfops/share-python-deps]: review --- .gitignore | 3 +++ smoketests/README.md | 11 +++++++++++ 2 files changed, 14 insertions(+) create mode 100644 smoketests/README.md diff --git a/.gitignore b/.gitignore index 32bf1df4385..5189e22e2c1 100644 --- a/.gitignore +++ b/.gitignore @@ -226,3 +226,6 @@ new.json # Symlinked output from `nix build` result + +# Python venv directories +venv/ diff --git a/smoketests/README.md b/smoketests/README.md new file mode 100644 index 00000000000..ec53333efad --- /dev/null +++ b/smoketests/README.md @@ -0,0 +1,11 @@ +To use the smoketests, you first need to install the dependencies: + +``` +python -m venv smoketests/venv +smoketests/venv/bin/pip install -r smoketests/requirements.txt +``` + +Then, run the smoketests like so: +``` +smoketests/venv/bin/python -m smoketests +``` From 9aca3b7477a5fc36d7e5f1dad70ac9df2cafcaba Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 13:57:13 -0800 Subject: [PATCH 019/160] [bfops/share-python-deps]: review --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0c2497c258..0e01f47e6a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,12 +109,10 @@ jobs: with: { python-version: '3.12' } if: runner.os == 'Windows' - name: Install python deps - run: | - python -m venv venv - venv/bin/pip install -r smoketests/requirements.txt + run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests # Note: clear_database and replication only work in private - run: venv/bin/python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams + run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down From 5f1c35dc6fc2181912b875faefa03c00384605ac Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 19 Nov 2025 14:08:53 -0800 Subject: [PATCH 020/160] [bfops/parallel-smoketests]: empty From 661af745ef958843dc6024c8aeb880ffcf6a09cb Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 10:49:08 -0800 Subject: [PATCH 021/160] [add/cargo-ci]: fixes --- .github/workflows/ci.yml | 160 +++++++++++++-------------------------- tools/ci/src/main.rs | 2 + 2 files changed, 56 insertions(+), 106 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00258665d6a..703c7656be2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -170,91 +170,8 @@ jobs: working-directory: crates/bindings-typescript run: pnpm build - - name: Run cargo test - #Note: Unreal tests will be run separately - run: cargo test --all -- --skip unreal - - # The fallocate tests have been flakely when running in parallel - - name: Run fallocate tests - run: cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1 - - - name: Ensure C# autogen bindings are up-to-date - run: | - apt-get update - apt-get install -y acl curl ca-certificates - - REPO="$GITHUB_WORKSPACE" - # Let ue4 read/write the workspace & tool caches without changing ownership - for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do - [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - done - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - curl -sSf https://sh.rustup.rs | sh -s -- -y - fi - rustup show >/dev/null - git config --global --add safe.directory "$GITHUB_WORKSPACE" || true - - cd "$GITHUB_WORKSPACE/sdks/unreal" - cargo --version - cargo test -- --test-threads=1 - ' + - name: Run tests + run: cargo ci test lints: name: Lints @@ -393,26 +310,26 @@ jobs: # without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle. options: --user 0:0 steps: - # Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back - # commits though. - # - 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" +# Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back +# commits though. +# - 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: Install unreal engine test dependencies + - name: Run Unreal Engine tests working-directory: sdks/unreal env: UE_ROOT_PATH: /home/ue4/UnrealEngine @@ -421,12 +338,43 @@ jobs: apt-get update apt-get install -y acl curl ca-certificates - - name: Run Unreal Engine tests - working-directory: sdks/unreal - env: - UE_ROOT_PATH: /home/ue4/UnrealEngine - run: | - cargo ci unreal-tests + REPO="$GITHUB_WORKSPACE" + # Let ue4 read/write the workspace & tool caches without changing ownership + for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do + [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true + done + + # Rust tool caches live under the runner tool cache so they persist + export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" + export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" + mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" + chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" + + # Make sure the UE build script is executable (and parents traversable) + UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" + chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true + chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true + + # Run the build & tests as ue4 (who owns the UE tree) + sudo -E -H -u ue4 env \ + HOME=/home/ue4 \ + XDG_CONFIG_HOME=/home/ue4/.config \ + CARGO_HOME="$CARGO_HOME" \ + RUSTUP_HOME="$RUSTUP_HOME" \ + PATH="$CARGO_HOME/bin:$PATH" \ + bash -lc ' + set -euxo pipefail + # Install rustup for ue4 if needed (uses the shared caches) + if ! command -v cargo >/dev/null 2>&1; then + curl -sSf https://sh.rustup.rs | sh -s -- -y + fi + rustup show >/dev/null + git config --global --add safe.directory "$GITHUB_WORKSPACE" || true + + cd "$GITHUB_WORKSPACE/sdks/unreal" + cargo --version + cargo test -- --test-threads=1 + ' ci_command_docs: name: Check CI command docs diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d405ec4ff31..2674f5cc551 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -139,6 +139,8 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { run!("cargo test --all -- --skip unreal")?; + // The fallocate tests have been flakely when running in parallel + run!("cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1")?; run!("bash tools/check-diff.sh")?; run!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; run!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; From b3e115f828869a8dae85044902482ee4bc182938 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 10:56:18 -0800 Subject: [PATCH 022/160] [add/cargo-ci]: review --- .github/workflows/ci.yml | 2 +- tools/ci/src/main.rs | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 703c7656be2..dee88453906 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,7 +112,7 @@ jobs: run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams + run: cargo ci smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 2674f5cc551..92f9212f3c0 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -150,8 +150,14 @@ fn main() -> Result<()> { run!("cargo fmt --all -- --check")?; run!("cargo clippy --all --tests --benches -- -D warnings")?; run!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; + // `bindings` is the only crate we care strongly about documenting, + // since we link to its docs.rs from our website. + // We won't pass `--no-deps`, though, + // since we want everything reachable through it to also work. + // This includes `sats` and `lib`. run!( "cd crates/bindings && cargo doc", + // Make `cargo doc` exit with error on warnings, most notably broken links &[("RUSTDOCFLAGS", "--deny warnings")] )?; } @@ -188,7 +194,7 @@ fn main() -> Result<()> { run!(&format!( r#" ROOT_DIR="$(mktemp -d)" -cargo run {github_token_auth_flag}--target {target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes +cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes "${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help "# ))?; From e0517e01a119b2cb47231017477779b94278738d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 10:58:51 -0800 Subject: [PATCH 023/160] [add/cargo-ci]: rm --- .github/workflows/ci.yml.orig | 457 ---------------------------------- 1 file changed, 457 deletions(-) delete mode 100644 .github/workflows/ci.yml.orig diff --git a/.github/workflows/ci.yml.orig b/.github/workflows/ci.yml.orig deleted file mode 100644 index 6c99702dcda..00000000000 --- a/.github/workflows/ci.yml.orig +++ /dev/null @@ -1,457 +0,0 @@ -on: - pull_request: - push: - branches: - - master - merge_group: - workflow_dispatch: - inputs: - pr_number: - description: 'Pull Request Number' - required: false - default: '' - -name: CI - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.event.inputs.pr_number || format('sha-{0}', github.sha) }} - cancel-in-progress: true - -jobs: - docker_smoketests: - name: Smoketests - strategy: - matrix: - include: - - runner: spacetimedb-runner - smoketest_args: '--docker' - - runner: windows-latest - smoketest_args: '--no-build-cli' - runner: [spacetimedb-runner, windows-latest] - runs-on: ${{ matrix.runner }} - 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 - - 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: pnpm/action-setup@v4 - with: - run_install: true - - - name: Install psql (Windows) - if: runner.os == 'Windows' - run: choco install psql -y --no-progress - shell: powershell - - name: Build and start database (Linux) - if: runner.os == 'Linux' - run: docker compose up -d - - name: Build and start database (Windows) - if: runner.os == 'Windows' - run: | - # Fail properly if any individual command fails - $ErrorActionPreference = 'Stop' - $PSNativeCommandUseErrorActionPreference = $true - - cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update - Start-Process target/debug/spacetimedb-cli.exe -ArgumentList 'start --pg-port 5432' - cd modules - # the sdk-manifests on windows-latest are messed up, so we need to update them - dotnet workload config --update-mode manifests - dotnet workload update - - uses: actions/setup-python@v5 - with: { python-version: '3.12' } - if: runner.os == 'Windows' - - name: Install python deps - run: python -m pip install psycopg2-binary xmltodict - - name: Run smoketests -<<<<<<< HEAD - run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication - -||||||| 004c5bb10 - # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication -======= - # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams ->>>>>>> 4143c15b08a58cdb382497d9d9e55ffa2ce92ec2 - - name: Stop containers (Linux) - if: always() && runner.os == 'Linux' - run: docker compose down - - test: - name: Test Suite - runs-on: spacetimedb-runner - steps: - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - 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 - - - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: pnpm/action-setup@v4 - with: - run_install: true - - - name: Build typescript module sdk - working-directory: crates/bindings-typescript - run: pnpm build - - - name: Run cargo test - #Note: Unreal tests will be run separately -<<<<<<< HEAD -||||||| 004c5bb10 - run: cargo test --all -- --skip unreal - - - name: Check that the test outputs are up-to-date - run: bash tools/check-diff.sh - - - name: Ensure C# autogen bindings are up-to-date -======= - run: cargo test --all -- --skip unreal - - - name: Run fallocate tests - run: cargo test -p spacetimedb-durability --features fallocate - - - name: Check that the test outputs are up-to-date - run: bash tools/check-diff.sh - - - name: Ensure C# autogen bindings are up-to-date ->>>>>>> 4143c15b08a58cdb382497d9d9e55ffa2ce92ec2 - run: | - apt-get update - apt-get install -y acl curl ca-certificates - - REPO="$GITHUB_WORKSPACE" - # Let ue4 read/write the workspace & tool caches without changing ownership - for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do - [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - done - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - curl -sSf https://sh.rustup.rs | sh -s -- -y - fi - rustup show >/dev/null - git config --global --add safe.directory "$GITHUB_WORKSPACE" || true - - cd "$GITHUB_WORKSPACE/sdks/unreal" - cargo --version - cargo test - ' - - lints: - name: Lints - runs-on: spacetimedb-runner - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - uses: dsherret/rust-toolchain-file@v1 - - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Run ci lint - run: cargo ci lint - - wasm_bindings: - name: Build and test wasm bindings - runs-on: spacetimedb-runner - steps: - - uses: actions/checkout@v3 - - - uses: dsherret/rust-toolchain-file@v1 - - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - - name: Run bindgen tests - run: cargo ci wasm-bindings - - publish_checks: - name: Check that packages are publishable - runs-on: ubuntu-latest - permissions: read-all - steps: - - uses: actions/checkout@v3 - - name: Set up Python env - run: | - test -d venv || python3 -m venv venv - venv/bin/pip3 install argparse toml - - name: Run checks - run: | - FAILED=0 - # This definition of ROOTS and invocation of find-publish-list.py is copied from publish-crates.sh - ROOTS=(bindings sdk cli standalone) - for crate in $(venv/bin/python3 tools/find-publish-list.py --recursive --quiet "${ROOTS[@]}"); do - if ! venv/bin/python3 tools/crate-publish-checks.py "crates/$crate"; then - FAILED=$(( $FAILED + 1 )) - fi - done - if [ $FAILED -gt 0 ]; then - exit 1 - fi - - update: - name: Test spacetimedb-update flow - permissions: read-all - strategy: - matrix: - include: - - { target: x86_64-unknown-linux-gnu, runner: spacetimedb-runner } - - { target: aarch64-unknown-linux-gnu, runner: arm-runner } - - { target: aarch64-apple-darwin, runner: macos-latest } - - { target: x86_64-pc-windows-msvc, runner: windows-latest } - runs-on: ${{ matrix.runner }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Rust - uses: dsherret/rust-toolchain-file@v1 - - - name: Install rust target - run: rustup target add ${{ matrix.target }} - - - name: Install packages - if: ${{ matrix.runner == 'arm-runner' }} - shell: bash - run: sudo apt install -y libssl-dev - - - name: Build spacetimedb-update - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: cargo ci update-flow --target=${{ matrix.target }} --github-token-auth - - unreal_engine_tests: - name: Unreal Engine Tests - # This can't go on e.g. ubuntu-latest because that runner runs out of disk space. ChatGPT suggested that the general solution tends to be to use - # a custom runner. - runs-on: spacetimedb-runner - # Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway. - if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }} - container: - image: ghcr.io/epicgames/unreal-engine:dev-5.6 - credentials: - # Note(bfops): I don't think that `github.actor` needs to match the user that the token is for, because I'm using a token for my account and - # it seems to be totally happy. - # However, the token needs to be for a user that has access to the EpicGames org (see - # https://dev.epicgames.com/documentation/en-us/unreal-engine/downloading-source-code-in-unreal-engine?application_version=5.6) - username: ${{ github.actor }} - password: ${{ secrets.GHCR_TOKEN }} - # Run as root because otherwise we get permission denied for various directories inside the container. I tried doing dances to allow it to run - # without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle. - options: --user 0:0 - steps: - # Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back - # commits though. - # - 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: Install unreal engine test dependencies - working-directory: sdks/unreal - env: - UE_ROOT_PATH: /home/ue4/UnrealEngine - run: | - - apt-get update - apt-get install -y acl curl ca-certificates - - - name: Run Unreal Engine tests - working-directory: sdks/unreal - env: - UE_ROOT_PATH: /home/ue4/UnrealEngine - run: | - cargo ci unreal-tests - - ci_command_docs: - name: Check CI command docs - runs-on: ubuntu-latest - 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: Check for docs change - run: cargo ci self-docs --check - - cli_docs: - name: Check CLI docs - permissions: read-all - runs-on: ubuntu-latest - 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 }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: pnpm/action-setup@v4 - with: - run_install: true - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - uses: dsherret/rust-toolchain-file@v1 - - - name: Check for docs change - run: | - cargo ci cli-docs From 8a479f523860d8e12ed6a03c8060854433f7e755 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 10:59:33 -0800 Subject: [PATCH 024/160] [add/cargo-ci]: revert --- Cargo.toml | 59 +++++++++++++++++++----------------------------------- 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ac39176bd4..6694ca9fd89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -163,16 +163,10 @@ colored = "2.0.0" console = { version = "0.15.6" } convert_case = "0.6.0" crc32c = "0.6.4" -criterion = { version = "0.5.1", features = [ - "async", - "async_tokio", - "html_reports", -] } +criterion = { version = "0.5.1", features = ["async", "async_tokio", "html_reports"] } crossbeam-channel = "0.5" crossbeam-queue = "0.3.12" -cursive = { version = "0.20", default-features = false, features = [ - "crossterm-backend", -] } +cursive = { version = "0.20", default-features = false, features = ["crossterm-backend"] } decorum = { version = "0.3.1", default-features = false, features = ["std"] } derive_more = "0.99" dialoguer = { version = "0.11", default-features = false } @@ -196,10 +190,7 @@ futures-util = "0.3" getrandom02 = { package = "getrandom", version = "0.2" } git2 = "0.19" glob = "0.3.1" -hashbrown = { version = "0.15", default-features = false, features = [ - "equivalent", - "inline-more", -] } +hashbrown = { version = "0.15", default-features = false, features = ["equivalent", "inline-more"] } headers = "0.4" heck = "0.4" hex = "0.4.3" @@ -234,10 +225,7 @@ paste = "1.0" percent-encoding = "2.3" petgraph = { version = "0.6.5", default-features = false } pin-project-lite = "0.2.9" -pgwire = { version = "0.34.2", default-features = false, features = [ - "server-api", - "pg-ext-types", -] } +pgwire = { version = "0.34.2", default-features = false, features = ["server-api", "pg-ext-types"] } postgres-types = "0.2.5" pretty_assertions = { version = "1.4", features = ["unstable"] } proc-macro2 = "1.0" @@ -267,10 +255,7 @@ second-stack = "0.3" self-replace = "1.5" semver = "1" serde = { version = "1.0.136", features = ["derive"] } -serde_json = { version = "1.0.128", features = [ - "raw_value", - "arbitrary_precision", -] } +serde_json = { version = "1.0.128", features = ["raw_value", "arbitrary_precision"] } serde_path_to_error = "0.1.9" serde_with = { version = "3.3.0", features = ["base64", "hex"] } serial_test = "2.0.0" @@ -286,9 +271,7 @@ sqllogictest-engines = "0.17" sqlparser = "0.38.0" strum = { version = "0.25.0", features = ["derive"] } syn = { version = "2", features = ["full", "extra-traits"] } -syntect = { version = "5.0.0", default-features = false, features = [ - "default-fancy", -] } +syntect = { version = "5.0.0", default-features = false, features = ["default-fancy"] } tabled = "0.14.0" tar = "0.4" tempdir = "0.3.7" @@ -341,14 +324,14 @@ openssl = { version = "0.10", features = ["vendored"] } version = "25" default-features = false features = [ - "addr2line", - "async", - "cache", - "cranelift", - "demangle", - "parallel-compilation", - "runtime", - "std", + "addr2line", + "async", + "cache", + "cranelift", + "demangle", + "parallel-compilation", + "runtime", + "std", ] [workspace.dependencies.tracing-tracy] @@ -357,13 +340,13 @@ version = "0.10.4" # and reconnecting, from the tracy client to the database. # TODO(George): Need to be able to remove "broadcast" in some build configurations. features = [ - "enable", - "system-tracing", - "context-switch-tracing", - "sampling", - "code-transfer", - "broadcast", - "ondemand", + "enable", + "system-tracing", + "context-switch-tracing", + "sampling", + "code-transfer", + "broadcast", + "ondemand", ] [workspace.lints.rust] From dabe11fd15f5b4b86853f31cb60d378bfc520922 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 11:04:59 -0800 Subject: [PATCH 025/160] [add/cargo-ci]: update self-docs --- tools/ci/README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tools/ci/README.md b/tools/ci/README.md index c90622e3b73..080dc354f33 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -103,23 +103,6 @@ Usage: update-flow [OPTIONS] - `--github-token-auth`: Whether to enable github token authentication feature when building the update binary. By default this is disabled. - `--help`: Print help (see a summary with '-h') -### `unreal-tests` - -Run Unreal Engine related tests - -This assumes the UE4 environment is already set up - -This is designed to run in the github actions environment, but should work locally if the Unreal environment is set up correctly. - -**Usage:** -```bash -Usage: unreal-tests -``` - -**Options:** - -- `--help`: Print help (see a summary with '-h') - ### `cli-docs` **Usage:** From f67e95d3d4cf3e3dba71be045ceea9dc8030ab12 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 11:05:25 -0800 Subject: [PATCH 026/160] [add/cargo-ci]: fix? --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dee88453906..870d61b9613 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,7 +112,7 @@ jobs: run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests # Note: clear_database and replication only work in private - run: cargo ci smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams + run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down From 2dc780c4f1395f18a0d0d4ef6fc6f6128dc55203 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 11:11:12 -0800 Subject: [PATCH 027/160] [bfops/parallel-smoketests]: remove python parallel flags --- smoketests/__main__.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index cc3b0d004b6..7d435fc9f89 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -77,8 +77,6 @@ def main(): parser.add_argument("--no-docker-logs", action="store_true") parser.add_argument("--skip-dotnet", action="store_true", help="ignore tests which require dotnet") parser.add_argument("--show-all-output", action="store_true", help="show all stdout/stderr from the tests as they're running") - parser.add_argument("--parallel", action="store_true", help="run test classes in parallel") - parser.add_argument("-j", dest='jobs', help="Set number of jobs for parallel test runs. Default is `nproc`", type=int, default=0) parser.add_argument('-k', dest='testNamePatterns', action='append', type=_convert_select_pattern, help='Only run tests which match the given substring') @@ -176,14 +174,9 @@ def main(): buffer = not args.show_all_output verbosity = 2 - if args.parallel: - print("parallel test running is under construction, this will probably not work correctly") - from . import unittest_parallel - unittest_parallel.main(buffer=buffer, verbose=verbosity, level="class", discovered_tests=tests, jobs=args.jobs) - else: - result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) - if not result.wasSuccessful(): - parser.exit(status=1) + result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) + if not result.wasSuccessful(): + parser.exit(status=1) if __name__ == '__main__': From 7fe19dda37eda7f871190ad193da049af055d115 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 12:54:14 -0800 Subject: [PATCH 028/160] [bfops/parallel-smoketests]: wip --- tools/ci/src/main.rs | 60 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 92f9212f3c0..36a2b5aeeb3 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -51,6 +51,12 @@ enum CiCmd { /// /// Executes the smoketests suite with some default exclusions. Smoketests { + #[arg( + long, + default_value_t = false, + long_help = "Start SpacetimeDB in docker (Linux) instead of bare. Also forwarded to the smoketests runner." + )] + docker: bool, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -168,9 +174,59 @@ fn main() -> Result<()> { run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } - Some(CiCmd::Smoketests { args }) => { + Some(CiCmd::Smoketests { docker, args }) => { + // Prepare arguments for the smoketests runner + let mut st_args = args.clone(); + if docker { + st_args.push("--docker".to_string()); + } + + // Start SpacetimeDB depending on platform and docker flag + if docker { + // Linux docker compose flow + if cfg!(target_os = "linux") { + // Our .dockerignore omits `target`, which our CI Dockerfile needs. + run!("rm -f .dockerignore")?; + run!("docker compose -f .github/docker-compose.yml up -d")?; + } + } else { + // Bare mode + if cfg!(target_os = "windows") { + // Start spacetimedb-cli in the background with a fixed pg port + // Use Git Bash compatible backgrounding + run!(r#"target/debug/spacetimedb-cli.exe start --pg-port 5432 &"#)?; + // Ensure dotnet workloads are present for module builds on Windows + run!( + r#"(cd modules && dotnet workload config --update-mode manifests && dotnet workload update)"# + )?; + } else { + run!("target/debug/spacetimedb-cli start --pg-port 5432 &")?; + } + } + + // Always exclude some tests by default (mirrors CI workflow) // Note: clear_database and replication only work in private - run!(&format!("python -m smoketests {}", args.join(" ")))?; + let default_excludes = "-x clear_database replication teams"; + let joined = st_args.join(" "); + let cmdline = if joined.is_empty() { + format!("python -m smoketests {}", default_excludes) + } else { + format!("python -m smoketests {} {}", joined, default_excludes) + }; + + let mut test_err: Option = None; + if let Err(e) = run!(&cmdline) { + test_err = Some(e); + } + + // Teardown if we started docker + if docker && cfg!(target_os = "linux") { + let _ = run!("docker compose -f .github/docker-compose.yml down"); + } + + if let Some(e) = test_err { + return Err(e); + } } Some(CiCmd::UpdateFlow { From 4498429c01007d56fa84e47ffbdc86350a98296b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 12:54:33 -0800 Subject: [PATCH 029/160] [add/cargo-ci]: remove stale comment --- tools/ci/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 92f9212f3c0..fc2cf306bc7 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -169,7 +169,6 @@ fn main() -> Result<()> { } Some(CiCmd::Smoketests { args }) => { - // Note: clear_database and replication only work in private run!(&format!("python -m smoketests {}", args.join(" ")))?; } From deb123a3bb0fc74f9e3df2e90ec7a8df51e67f69 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 13:02:09 -0800 Subject: [PATCH 030/160] [bfops/parallel-smoketests]: review --- tools/ci/src/main.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 36a2b5aeeb3..76a8d1cdd3f 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -54,7 +54,7 @@ enum CiCmd { #[arg( long, default_value_t = false, - long_help = "Start SpacetimeDB in docker (Linux) instead of bare. Also forwarded to the smoketests runner." + long_help = "Start SpacetimeDB in docker (Linux) instead of bare." )] docker: bool, #[arg( @@ -175,20 +175,11 @@ fn main() -> Result<()> { } Some(CiCmd::Smoketests { docker, args }) => { - // Prepare arguments for the smoketests runner - let mut st_args = args.clone(); - if docker { - st_args.push("--docker".to_string()); - } - // Start SpacetimeDB depending on platform and docker flag if docker { - // Linux docker compose flow - if cfg!(target_os = "linux") { - // Our .dockerignore omits `target`, which our CI Dockerfile needs. - run!("rm -f .dockerignore")?; - run!("docker compose -f .github/docker-compose.yml up -d")?; - } + // Our .dockerignore omits `target`, which our CI Dockerfile needs. + run!("rm -f .dockerignore")?; + run!("docker compose -f .github/docker-compose.yml up -d")?; } else { // Bare mode if cfg!(target_os = "windows") { From 282ece6ff597ae4a3ecc5c75de759a80dcccb48f Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 13:15:46 -0800 Subject: [PATCH 031/160] [bfops/parallel-smoketests]: Start server as part of running smoketests --- .github/workflows/ci.yml | 30 +++++++++++++----------------- tools/ci/src/main.rs | 13 +------------ 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 870d61b9613..d15587c37ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,8 @@ jobs: container: ${{ matrix.container }} env: CARGO_TARGET_DIR: ${{ github.workspace }}/target + # Note: clear_database and replication only work in private + SMOKETEST_ARGS: ${{ matrix.smoketest_args }} -x clear_database replication teams steps: - name: Find Git ref env: @@ -87,22 +89,13 @@ jobs: if: runner.os == 'Linux' run: /usr/local/bin/start-docker.sh - - name: Build and start database (Linux) - if: runner.os == 'Linux' - run: | - # Our .dockerignore omits `target`, which our CI Dockerfile needs. - rm .dockerignore - docker compose -f .github/docker-compose.yml up -d - - name: Build and start database (Windows) + # the sdk-manifests on windows-latest are messed up, so we need to update them + - name: Fix sdk-manifests if: runner.os == 'Windows' + working-directory: modules + # Powershell doesn't early-exit properly from a multi-line command if one fails + shell: bash run: | - # Fail properly if any individual command fails - $ErrorActionPreference = 'Stop' - $PSNativeCommandUseErrorActionPreference = $true - - Start-Process target/debug/spacetimedb-cli.exe -ArgumentList 'start --pg-port 5432' - cd modules - # the sdk-manifests on windows-latest are messed up, so we need to update them dotnet workload config --update-mode manifests dotnet workload update - uses: actions/setup-python@v5 @@ -110,9 +103,12 @@ jobs: if: runner.os == 'Windows' - name: Install python deps run: python -m pip install -r smoketests/requirements.txt - - name: Run smoketests - # Note: clear_database and replication only work in private - run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams + - name: Run smoketests (Linux) + if: runner.os == 'Linux' + run: cargo ci smoketests --docker -- ${SMOKETEST_ARGS} + - name: Run smoketests (Windows) + if: runner.os == 'Windows' + run: cargo ci smoketests -- ${SMOKETEST_ARGS} - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 76a8d1cdd3f..2263fdf5d38 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -181,18 +181,7 @@ fn main() -> Result<()> { run!("rm -f .dockerignore")?; run!("docker compose -f .github/docker-compose.yml up -d")?; } else { - // Bare mode - if cfg!(target_os = "windows") { - // Start spacetimedb-cli in the background with a fixed pg port - // Use Git Bash compatible backgrounding - run!(r#"target/debug/spacetimedb-cli.exe start --pg-port 5432 &"#)?; - // Ensure dotnet workloads are present for module builds on Windows - run!( - r#"(cd modules && dotnet workload config --update-mode manifests && dotnet workload update)"# - )?; - } else { - run!("target/debug/spacetimedb-cli start --pg-port 5432 &")?; - } + run!(r#"cargo run -p spacetimedb-cli -- start --pg-port 5432 &"#)?; } // Always exclude some tests by default (mirrors CI workflow) From f095a1dfa643bf8fad744d34ff7105ab6c5ffcff Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 13:50:07 -0800 Subject: [PATCH 032/160] [bfops/parallel-smoketests]: WIP --- .github/workflows/ci.yml | 3 -- tools/ci/src/main.rs | 108 +++++++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d15587c37ce..e9b7897f863 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,9 +109,6 @@ jobs: - name: Run smoketests (Windows) if: runner.os == 'Windows' run: cargo ci smoketests -- ${SMOKETEST_ARGS} - - name: Stop containers (Linux) - if: always() && runner.os == 'Linux' - run: docker compose -f .github/docker-compose.yml down test: name: Test Suite diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 2263fdf5d38..b245c9b88fa 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,5 +1,5 @@ use anyhow::{bail, Result}; -use clap::{CommandFactory, Parser, Subcommand}; +use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; use duct::cmd; use std::collections::HashMap; use std::path::Path; @@ -31,6 +31,16 @@ struct Cli { skip: Vec, } +#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)] +enum StartServer { + /// Do not start a server; assume one is already running or remote + No, + /// Start a local server using the spacetimedb CLI + Bare, + /// Start services using docker compose + Docker, +} + #[derive(Subcommand)] enum CiCmd { /// Runs tests @@ -52,11 +62,12 @@ enum CiCmd { /// Executes the smoketests suite with some default exclusions. Smoketests { #[arg( - long, - default_value_t = false, - long_help = "Start SpacetimeDB in docker (Linux) instead of bare." + long = "start-server", + value_enum, + default_value_t = StartServer::Bare, + long_help = "How to start SpacetimeDB before running smoketests: no | bare | docker" )] - docker: bool, + start_server: StartServer, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -174,39 +185,72 @@ fn main() -> Result<()> { run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } - Some(CiCmd::Smoketests { docker, args }) => { - // Start SpacetimeDB depending on platform and docker flag - if docker { - // Our .dockerignore omits `target`, which our CI Dockerfile needs. - run!("rm -f .dockerignore")?; - run!("docker compose -f .github/docker-compose.yml up -d")?; - } else { - run!(r#"cargo run -p spacetimedb-cli -- start --pg-port 5432 &"#)?; + Some(CiCmd::Smoketests { start_server, args }) => { + let mut started_pid: Option = None; + match start_server { + StartServer::Docker => { + // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. + run!("cd .github && docker compose -f docker-compose.yml up -d")?; + } + StartServer::Bare => { + let pid_str; + if cfg!(target_os = "windows") { + pid_str = cmd!( + "powershell", + "-NoProfile", + "-Command", + "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --pg-port 5432' -PassThru; $p.Id" + ) + .read() + .unwrap_or_default(); + } else { + pid_str = cmd!( + "bash", + "-lc", + "cargo run -p spacetimedb-cli -- start --pg-port 5432 & echo $!" + ) + .read() + .unwrap_or_default(); + } + started_pid = Some( + pid_str + .trim() + .parse::() + .expect("Failed to get PID of started process"), + ) + } + StartServer::No => {} } - // Always exclude some tests by default (mirrors CI workflow) - // Note: clear_database and replication only work in private - let default_excludes = "-x clear_database replication teams"; - let joined = st_args.join(" "); - let cmdline = if joined.is_empty() { - format!("python -m smoketests {}", default_excludes) - } else { - format!("python -m smoketests {} {}", joined, default_excludes) - }; + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + let python = if py3_available { "python3" } else { "python" }; - let mut test_err: Option = None; - if let Err(e) = run!(&cmdline) { - test_err = Some(e); - } + let test_result = run!(&format!("{python} -m smoketests {}", args.join(" "))); - // Teardown if we started docker - if docker && cfg!(target_os = "linux") { - let _ = run!("docker compose -f .github/docker-compose.yml down"); + match start_server { + StartServer::Docker => { + let _ = run!("docker compose -f .github/docker-compose.yml down"); + } + StartServer::Bare => { + let pid = if let Some(pid) = started_pid { + pid + } else { + // We constructed a `Some` above if `StartServer::Bare`. + unreachable!(); + }; + if cfg!(target_os = "windows") { + let _ = run!(&format!("powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", pid)); + } else { + let _ = run!(&format!("bash -lc 'kill {} 2>/dev/null || true'", pid)); + } + } + StartServer::No => {} } - if let Some(e) = test_err { - return Err(e); - } + test_result?; } Some(CiCmd::UpdateFlow { From 336337acd70206e3df5ad51172b25329af887f9d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 14:25:00 -0800 Subject: [PATCH 033/160] [bfops/parallel-smoketests]: review --- tools/ci/src/main.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index b245c9b88fa..2895925e33f 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -187,12 +187,17 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { start_server, args }) => { let mut started_pid: Option = None; + println!("Starting server.."); match start_server { StartServer::Docker => { // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. run!("cd .github && docker compose -f docker-compose.yml up -d")?; } StartServer::Bare => { + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests + // before the server is up. + run!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + let pid_str; if cfg!(target_os = "windows") { pid_str = cmd!( @@ -207,7 +212,7 @@ fn main() -> Result<()> { pid_str = cmd!( "bash", "-lc", - "cargo run -p spacetimedb-cli -- start --pg-port 5432 & echo $!" + "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!" ) .read() .unwrap_or_default(); @@ -228,8 +233,11 @@ fn main() -> Result<()> { .unwrap_or(false); let python = if py3_available { "python3" } else { "python" }; + println!("Running smoketests.."); let test_result = run!(&format!("{python} -m smoketests {}", args.join(" "))); + println!("Shutting down server.."); + // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process match start_server { StartServer::Docker => { let _ = run!("docker compose -f .github/docker-compose.yml down"); @@ -244,7 +252,7 @@ fn main() -> Result<()> { if cfg!(target_os = "windows") { let _ = run!(&format!("powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", pid)); } else { - let _ = run!(&format!("bash -lc 'kill {} 2>/dev/null || true'", pid)); + let _ = run!(&format!("bash -lc 'kill {} 2>/dev/null'", pid)); } } StartServer::No => {} From 38859ab0feca9dfda6e60d39a83213b8f05f11f7 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 14:26:46 -0800 Subject: [PATCH 034/160] [bfops/parallel-smoketests]: review --- tools/ci/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 2895925e33f..4af9e0db4ed 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -252,7 +252,7 @@ fn main() -> Result<()> { if cfg!(target_os = "windows") { let _ = run!(&format!("powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", pid)); } else { - let _ = run!(&format!("bash -lc 'kill {} 2>/dev/null'", pid)); + let _ = run!(&format!("kill {}", pid)); } } StartServer::No => {} From a7202ef1997ecc230a4df18855a622cb7aa304fc Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 14:28:49 -0800 Subject: [PATCH 035/160] [add/cargo-ci]: review --- tools/ci/src/main.rs | 58 ++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index fc2cf306bc7..d4750da29df 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -93,12 +93,12 @@ enum CiCmd { }, } -macro_rules! run { +macro_rules! bash { ($cmdline:expr) => { - run_command($cmdline, &Vec::new()) + run_bash($cmdline, &Vec::new()) }; ($cmdline:expr, $envs:expr) => { - run_command($cmdline, $envs) + run_bash($cmdline, $envs) }; } @@ -114,13 +114,13 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { continue; } log::info!("executing cargo ci {subcmd}"); - run!(&format!("cargo ci {subcmd}"))?; + bash!(&format!("cargo ci {subcmd}"))?; } Ok(()) } -fn run_command(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { +fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { let mut env = env::vars().collect::>(); env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); log::debug!("$ {cmdline}"); @@ -138,24 +138,24 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { - run!("cargo test --all -- --skip unreal")?; + bash!("cargo test --all -- --skip unreal")?; // The fallocate tests have been flakely when running in parallel - run!("cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1")?; - run!("bash tools/check-diff.sh")?; - run!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; - run!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; + bash!("cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1")?; + bash!("bash tools/check-diff.sh")?; + bash!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; + bash!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; } Some(CiCmd::Lint) => { - run!("cargo fmt --all -- --check")?; - run!("cargo clippy --all --tests --benches -- -D warnings")?; - run!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; + bash!("cargo fmt --all -- --check")?; + bash!("cargo clippy --all --tests --benches -- -D warnings")?; + bash!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; // `bindings` is the only crate we care strongly about documenting, // since we link to its docs.rs from our website. // We won't pass `--no-deps`, though, // since we want everything reachable through it to also work. // This includes `sats` and `lib`. - run!( + bash!( "cd crates/bindings && cargo doc", // Make `cargo doc` exit with error on warnings, most notably broken links &[("RUSTDOCFLAGS", "--deny warnings")] @@ -163,13 +163,19 @@ fn main() -> Result<()> { } Some(CiCmd::WasmBindings) => { - run!("cargo test -p spacetimedb-codegen")?; - run!("cargo update")?; - run!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; + bash!("cargo test -p spacetimedb-codegen")?; + bash!("cargo update")?; + bash!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } Some(CiCmd::Smoketests { args }) => { - run!(&format!("python -m smoketests {}", args.join(" ")))?; + // On some systems, there is no `python`, but there is `python3`. + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + let python = if py3_available { "python3" } else { "python" }; + bash!(&format!("{python} -m smoketests {}", args.join(" ")))?; } Some(CiCmd::UpdateFlow { @@ -183,14 +189,14 @@ fn main() -> Result<()> { "" }; - run!(&format!("echo 'checking update flow for target: {target}'"))?; - run!(&format!( + bash!(&format!("echo 'checking update flow for target: {target}'"))?; + bash!(&format!( "cargo build {github_token_auth_flag}{target} -p spacetimedb-update" ))?; // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it // happens very frequently on the `macos-runner`, but we haven't seen it on any others). - run!(&format!( + bash!(&format!( r#" ROOT_DIR="$(mktemp -d)" cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes @@ -211,11 +217,11 @@ cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install ); } - run!("pnpm install --recursive")?; - run!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; - run!("pnpm format")?; - run!("git status")?; - run!( + bash!("pnpm install --recursive")?; + bash!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; + bash!("pnpm format")?; + bash!("git status")?; + bash!( r#" if git diff --exit-code HEAD; then echo "No docs changes detected" From 8ae19715439dfa91b07d42c6c32d86faafa9a921 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 14:41:21 -0800 Subject: [PATCH 036/160] [bfops/parallel-smoketests]: fix workflow file --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9b7897f863..bc11d0d6f5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,7 +105,7 @@ jobs: run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests (Linux) if: runner.os == 'Linux' - run: cargo ci smoketests --docker -- ${SMOKETEST_ARGS} + run: cargo ci smoketests --start-server docker -- ${SMOKETEST_ARGS} - name: Run smoketests (Windows) if: runner.os == 'Windows' run: cargo ci smoketests -- ${SMOKETEST_ARGS} From 9bbe781bd7ff18f7359ac0bad36504dd0f952231 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 14:48:07 -0800 Subject: [PATCH 037/160] [bfops/parallel-smoketests]: maybe fix? --- .github/Dockerfile | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/Dockerfile b/.github/Dockerfile index 00d20a86386..40ad7c86d8b 100644 --- a/.github/Dockerfile +++ b/.github/Dockerfile @@ -1,6 +1,11 @@ # Minimal Dockerfile that just wraps pre-built binaries, so we can test the server inside docker + +FROM rust:1.90.0 as not-ignored +COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli +COPY ./crates/standalone/config.toml config.toml + FROM rust:1.90.0 RUN mkdir -p /stdb/data -COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli /usr/local/bin/ -COPY ./crates/standalone/config.toml /stdb/data/config.toml +COPY --from=not-ignored ./spacetimedb-standalone ./spacetimedb-cli /usr/local/bin/ +COPY --from=not-ignored ./config.toml /stdb/data/config.toml RUN ln -s /usr/local/bin/spacetimedb-cli /usr/local/bin/spacetime From 393776811062cc59ae2b70868042e2fced0ff201 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 15:17:04 -0800 Subject: [PATCH 038/160] [bfops/parallel-smoketests]: fix copy --- .github/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/Dockerfile b/.github/Dockerfile index 40ad7c86d8b..56dc4cda77e 100644 --- a/.github/Dockerfile +++ b/.github/Dockerfile @@ -1,8 +1,8 @@ # Minimal Dockerfile that just wraps pre-built binaries, so we can test the server inside docker FROM rust:1.90.0 as not-ignored -COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli -COPY ./crates/standalone/config.toml config.toml +COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli . +COPY ./crates/standalone/config.toml . FROM rust:1.90.0 RUN mkdir -p /stdb/data From f7e7893fa8fed87f5ec18371540a1d3fa175585c Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 20 Nov 2025 15:18:13 -0800 Subject: [PATCH 039/160] [bfops/parallel-smoketests]: fix --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc11d0d6f5b..e5e1faf1b22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,7 +105,10 @@ jobs: run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests (Linux) if: runner.os == 'Linux' - run: cargo ci smoketests --start-server docker -- ${SMOKETEST_ARGS} + run: | + # .dockerignore ignores target, which our CI dockerfile uses + rm .dockerignore + cargo ci smoketests --docker .github/docker-compose.yml -- ${SMOKETEST_ARGS} - name: Run smoketests (Windows) if: runner.os == 'Windows' run: cargo ci smoketests -- ${SMOKETEST_ARGS} From 680042e3e1001366322a3560335d24ffc09fd2b3 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 11:51:36 -0800 Subject: [PATCH 040/160] [bfops/parallel-smoketests]: review --- tools/ci/src/main.rs | 75 ++++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c4d5414dd22..20f9f35f45b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,6 +1,7 @@ use anyhow::{bail, Result}; -use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; +use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; +use log::warn; use std::collections::HashMap; use std::path::Path; use std::{env, fs}; @@ -31,16 +32,6 @@ struct Cli { skip: Vec, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, ValueEnum)] -enum StartServer { - /// Do not start a server; assume one is already running or remote - No, - /// Start a local server using the spacetimedb CLI - Bare, - /// Start services using docker compose - Docker, -} - #[derive(Subcommand)] enum CiCmd { /// Runs tests @@ -63,11 +54,18 @@ enum CiCmd { Smoketests { #[arg( long = "start-server", - value_enum, - default_value_t = StartServer::Bare, - long_help = "How to start SpacetimeDB before running smoketests: no | bare | docker" + default_value_t = true, + long_help = "Whether to start a local SpacetimeDB server before running smoketests" + )] + start_server: bool, + #[arg( + long = "docker", + value_name = "COMPOSE_FILE", + num_args(0..=1), + default_missing_value = "docker-compose.yml", + long_help = "Use docker for smoketests, specifying a docker compose file. If no value is provided, docker-compose.yml is used by default. This cannot be combined with --start-server." )] - start_server: StartServer, + docker: Option, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -185,19 +183,27 @@ fn main() -> Result<()> { bash!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } - Some(CiCmd::Smoketests { start_server, args }) => { + Some(CiCmd::Smoketests { + start_server, + docker, + args, + }) => { let mut started_pid: Option = None; - println!("Starting server.."); - match start_server { - StartServer::Docker => { + match (start_server, docker.as_ref()) { + (start_server, Some(compose_file)) => { + if !start_server { + warn!("--docker implies --start-server=true"); + } + println!("Starting server.."); // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. - bash!("cd .github && docker compose -f docker-compose.yml up -d")?; + bash!(&format!("docker compose -f {compose_file} up -d"))?; } - StartServer::Bare => { + (true, None) => { // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests // before the server is up. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + println!("Starting server.."); let pid_str; if cfg!(target_os = "windows") { pid_str = cmd!( @@ -224,9 +230,10 @@ fn main() -> Result<()> { .expect("Failed to get PID of started process"), ) } - StartServer::No => {} + (false, None) => {} } + // TODO: does this work on windows? let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") .run() .map(|s| s.status.success()) @@ -234,19 +241,27 @@ fn main() -> Result<()> { let python = if py3_available { "python3" } else { "python" }; println!("Running smoketests.."); - let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); + let mut smoketests_args = args.clone(); + if let Some(compose_file) = docker.as_ref() { + // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to + // run the server in docker while still retaining full control over what tests they want. + smoketests_args.push("--compose-file".to_string()); + smoketests_args.push(compose_file.to_string()); + } + let test_result = bash!(&format!("{python} -m smoketests {}", smoketests_args.join(" "))); - println!("Shutting down server.."); // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process - match start_server { - StartServer::Docker => { - let _ = bash!("docker compose -f .github/docker-compose.yml down"); + match (start_server, docker.as_ref()) { + (_, Some(compose_file)) => { + println!("Shutting down server.."); + let _ = bash!(&format!("docker compose -f {compose_file} down")); } - StartServer::Bare => { + (true, None) => { + println!("Shutting down server.."); let pid = if let Some(pid) = started_pid { pid } else { - // We constructed a `Some` above if `StartServer::Bare`. + // We constructed a `Some` above in this case unreachable!(); }; if cfg!(target_os = "windows") { @@ -255,7 +270,7 @@ fn main() -> Result<()> { let _ = bash!(&format!("kill {}", pid)); } } - StartServer::No => {} + (false, None) => {} } test_result?; From 60de57d99c0cbd5901520a35c137b7bd08e8b76d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 11:54:02 -0800 Subject: [PATCH 041/160] [bfops/ci-start-server]: revert --- smoketests/__main__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index 7d435fc9f89..cc3b0d004b6 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -77,6 +77,8 @@ def main(): parser.add_argument("--no-docker-logs", action="store_true") parser.add_argument("--skip-dotnet", action="store_true", help="ignore tests which require dotnet") parser.add_argument("--show-all-output", action="store_true", help="show all stdout/stderr from the tests as they're running") + parser.add_argument("--parallel", action="store_true", help="run test classes in parallel") + parser.add_argument("-j", dest='jobs', help="Set number of jobs for parallel test runs. Default is `nproc`", type=int, default=0) parser.add_argument('-k', dest='testNamePatterns', action='append', type=_convert_select_pattern, help='Only run tests which match the given substring') @@ -174,9 +176,14 @@ def main(): buffer = not args.show_all_output verbosity = 2 - result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) - if not result.wasSuccessful(): - parser.exit(status=1) + if args.parallel: + print("parallel test running is under construction, this will probably not work correctly") + from . import unittest_parallel + unittest_parallel.main(buffer=buffer, verbose=verbosity, level="class", discovered_tests=tests, jobs=args.jobs) + else: + result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) + if not result.wasSuccessful(): + parser.exit(status=1) if __name__ == '__main__': From e7443f4ad1bdb6156d52d99e997449b7e2859b0e Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 11:55:37 -0800 Subject: [PATCH 042/160] [bfops/ci-start-server]: review --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5e1faf1b22..b1928b0a600 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,7 +106,7 @@ jobs: - name: Run smoketests (Linux) if: runner.os == 'Linux' run: | - # .dockerignore ignores target, which our CI dockerfile uses + # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml -- ${SMOKETEST_ARGS} - name: Run smoketests (Windows) From 53279560841ddded27c07d1d56afa96f5c21e74d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 11:56:42 -0800 Subject: [PATCH 043/160] [bfops/ci-start-server]: revert --- .github/Dockerfile | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/Dockerfile b/.github/Dockerfile index 56dc4cda77e..00d20a86386 100644 --- a/.github/Dockerfile +++ b/.github/Dockerfile @@ -1,11 +1,6 @@ # Minimal Dockerfile that just wraps pre-built binaries, so we can test the server inside docker - -FROM rust:1.90.0 as not-ignored -COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli . -COPY ./crates/standalone/config.toml . - FROM rust:1.90.0 RUN mkdir -p /stdb/data -COPY --from=not-ignored ./spacetimedb-standalone ./spacetimedb-cli /usr/local/bin/ -COPY --from=not-ignored ./config.toml /stdb/data/config.toml +COPY ./target/debug/spacetimedb-standalone ./target/debug/spacetimedb-cli /usr/local/bin/ +COPY ./crates/standalone/config.toml /stdb/data/config.toml RUN ln -s /usr/local/bin/spacetimedb-cli /usr/local/bin/spacetime From 9afb403f3e54b9f3f6709b113a4526b6e268a17a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 12:26:55 -0800 Subject: [PATCH 044/160] [bfops/parallel-smoketests]: Revert "[bfops/ci-start-server]: revert" This reverts commit 60de57d99c0cbd5901520a35c137b7bd08e8b76d. --- smoketests/__main__.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index cc3b0d004b6..7d435fc9f89 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -77,8 +77,6 @@ def main(): parser.add_argument("--no-docker-logs", action="store_true") parser.add_argument("--skip-dotnet", action="store_true", help="ignore tests which require dotnet") parser.add_argument("--show-all-output", action="store_true", help="show all stdout/stderr from the tests as they're running") - parser.add_argument("--parallel", action="store_true", help="run test classes in parallel") - parser.add_argument("-j", dest='jobs', help="Set number of jobs for parallel test runs. Default is `nproc`", type=int, default=0) parser.add_argument('-k', dest='testNamePatterns', action='append', type=_convert_select_pattern, help='Only run tests which match the given substring') @@ -176,14 +174,9 @@ def main(): buffer = not args.show_all_output verbosity = 2 - if args.parallel: - print("parallel test running is under construction, this will probably not work correctly") - from . import unittest_parallel - unittest_parallel.main(buffer=buffer, verbose=verbosity, level="class", discovered_tests=tests, jobs=args.jobs) - else: - result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) - if not result.wasSuccessful(): - parser.exit(status=1) + result = unittest.TextTestRunner(buffer=buffer, verbosity=verbosity).run(tests) + if not result.wasSuccessful(): + parser.exit(status=1) if __name__ == '__main__': From 961d8ba04e1a68f9a5727ce4086b91f0f15dd909 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 13:58:21 -0800 Subject: [PATCH 045/160] [bfops/parallel-smoketests]: WIP --- tools/ci/src/main.rs | 300 ++++++++++++++++++++++++++++++++----------- 1 file changed, 225 insertions(+), 75 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 20f9f35f45b..2480e248f0a 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,9 +1,11 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use log::warn; use std::collections::HashMap; +use std::net::TcpListener; use std::path::Path; +use std::thread; use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -66,6 +68,12 @@ enum CiCmd { long_help = "Use docker for smoketests, specifying a docker compose file. If no value is provided, docker-compose.yml is used by default. This cannot be combined with --start-server." )] docker: Option, + #[arg( + long = "parallel", + default_value_t = false, + long_help = "Run smoketest suites in parallel, one process per top-level suite" + )] + parallel: bool, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -135,6 +143,168 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { Ok(()) } +fn prebuild_bare_server() -> Result<()> { + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests + // before the server is up. + bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone") +} + +fn run_smoketests_inner( + python: &str, + suite_name: Option<&str>, + start_server: bool, + docker: &Option, + per_suite: bool, + args: &[String], +) -> (String, bool) { + let mut output = String::new(); + let mut ok = true; + + // Server setup + let mut pid: Option = None; + let mut port: Option = None; + if let Some(compose_file) = docker.as_ref() { + output.push_str("Starting server..\n"); + // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. + let cmdline = format!("docker compose -f {} up -d", compose_file); + let res = cmd!("bash", "-lc", &cmdline).stderr_to_stdout().run(); + if let Err(e) = res { + output.push_str(&format!("Failed to start docker: {}\n", e)); + return (output, false); + } + } else if start_server { + // Bare server: shared (port 5432) in serial mode, or per-suite port + --remote-server in parallel mode. + if !per_suite { + if let Err(e) = prebuild_bare_server() { + output.push_str(&format!("Failed to prebuild server: {}\n", e)); + return (output, false); + } + output.push_str("Starting server..\n"); + let cmdline = "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!"; + let pid_str = cmd!("bash", "-lc", cmdline).read().unwrap_or_default(); + match pid_str.trim().parse::() { + Ok(p) => pid = Some(p), + Err(e) => { + output.push_str(&format!("Failed to parse server PID from '{}': {}\n", pid_str, e)); + return (output, false); + } + } + } else { + let free_port = match find_free_port() { + Ok(p) => p, + Err(e) => { + output.push_str(&format!("Failed to find free port: {}\n", e)); + return (output, false); + } + }; + port = Some(free_port); + output.push_str(&format!("Starting local server on port {}..\n", free_port)); + let cmdline = format!( + "nohup cargo run -p spacetimedb-cli -- start --pg-port {} >/dev/null 2>&1 & echo $!", + free_port + ); + let pid_str = cmd!("bash", "-lc", &cmdline).read().unwrap_or_default(); + match pid_str.trim().parse::() { + Ok(p) => pid = Some(p), + Err(e) => { + output.push_str(&format!("Failed to parse server PID from '{}': {}\n", pid_str, e)); + return (output, false); + } + } + } + } + + // Build smoketests args + let mut smoketests_args = Vec::new(); + if let Some(name) = suite_name { + smoketests_args.push(name.to_string()); + } + smoketests_args.extend(args.iter().cloned()); + + if let Some(compose_file) = docker.as_ref() { + // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to + // run the server in docker while still retaining full control over what tests they want. + smoketests_args.push("--compose-file".to_string()); + smoketests_args.push(compose_file.to_string()); + } + + if start_server && docker.is_none() && per_suite { + if let Some(p) = port { + smoketests_args.push("--remote-server".to_string()); + smoketests_args.push(format!("http://127.0.0.1:{}", p)); + } + } + + output.push_str("Running smoketests..\n"); + let cmdline = format!("{} -m smoketests {}", python, smoketests_args.join(" ")); + let res = cmd!("bash", "-lc", &cmdline).stderr_to_stdout().read(); + match res { + Ok(out) => { + output.push_str(&out); + } + Err(e) => { + output.push_str(&format!("smoketests failed: {}\n", e)); + ok = false; + } + } + + // Shutdown + if let Some(compose_file) = docker.as_ref() { + output.push_str("Shutting down server..\n"); + let down_cmd = format!("docker compose -f {} down", compose_file); + let _ = cmd!("bash", "-lc", &down_cmd).run(); + } + + if let Some(p) = pid { + output.push_str("Shutting down server..\n"); + let kill_cmd = format!("kill {}", p); + let _ = cmd!("bash", "-lc", &kill_cmd).run(); + } + + (output, ok) +} + +fn run_smoketests_serial(python: &str, start_server: bool, docker: &Option, args: &[String]) -> Result<()> { + let (output, ok) = run_smoketests_inner(python, None, start_server, docker, false, args); + print!("{}", output); + if !ok { + bail!("smoketests failed"); + } + Ok(()) +} + +fn find_free_port() -> Result { + let listener = TcpListener::bind("127.0.0.1:0").context("failed to bind to an ephemeral port")?; + let port = listener + .local_addr() + .context("failed to read local address for ephemeral port")? + .port(); + drop(listener); + Ok(port) +} + +fn list_smoketest_suites() -> Result> { + let mut suites = Vec::new(); + for entry in fs::read_dir("smoketests/tests").context("failed to read smoketests/tests directory")? { + let entry = entry?; + let path = entry.path(); + if let Some(name) = path.file_name().and_then(|n| n.to_str()) { + if name == "__init__.py" { + continue; + } + if let Some(ext) = path.extension().and_then(|e| e.to_str()) { + if ext == "py" { + if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) { + suites.push(stem.to_string()); + } + } + } + } + } + suites.sort(); + Ok(suites) +} + fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { let mut env = env::vars().collect::>(); env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); @@ -186,53 +356,9 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { start_server, docker, + parallel, args, }) => { - let mut started_pid: Option = None; - match (start_server, docker.as_ref()) { - (start_server, Some(compose_file)) => { - if !start_server { - warn!("--docker implies --start-server=true"); - } - println!("Starting server.."); - // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. - bash!(&format!("docker compose -f {compose_file} up -d"))?; - } - (true, None) => { - // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests - // before the server is up. - bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; - - println!("Starting server.."); - let pid_str; - if cfg!(target_os = "windows") { - pid_str = cmd!( - "powershell", - "-NoProfile", - "-Command", - "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --pg-port 5432' -PassThru; $p.Id" - ) - .read() - .unwrap_or_default(); - } else { - pid_str = cmd!( - "bash", - "-lc", - "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!" - ) - .read() - .unwrap_or_default(); - } - started_pid = Some( - pid_str - .trim() - .parse::() - .expect("Failed to get PID of started process"), - ) - } - (false, None) => {} - } - // TODO: does this work on windows? let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") .run() @@ -240,40 +366,64 @@ fn main() -> Result<()> { .unwrap_or(false); let python = if py3_available { "python3" } else { "python" }; - println!("Running smoketests.."); - let mut smoketests_args = args.clone(); - if let Some(compose_file) = docker.as_ref() { - // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to - // run the server in docker while still retaining full control over what tests they want. - smoketests_args.push("--compose-file".to_string()); - smoketests_args.push(compose_file.to_string()); - } - let test_result = bash!(&format!("{python} -m smoketests {}", smoketests_args.join(" "))); + if !parallel { + run_smoketests_serial(python, start_server, &docker, &args)?; + } else { + // Parallel mode: run each top-level smoketest suite in its own process. + let suites = list_smoketest_suites()?; + if suites.is_empty() { + bail!("No smoketest suites found in smoketests/tests"); + } + + let mut handles = Vec::new(); + for suite in suites { + let suite_name = suite.clone(); + let docker = docker.clone(); + let args = args.clone(); + let python = python.to_string(); + let start_server = start_server; - // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process - match (start_server, docker.as_ref()) { - (_, Some(compose_file)) => { - println!("Shutting down server.."); - let _ = bash!(&format!("docker compose -f {compose_file} down")); + let handle = thread::spawn(move || { + let (output, ok) = + run_smoketests_inner(&python, Some(&suite_name), start_server, &docker, true, &args); + (suite_name, output, ok) + }); + + handles.push(handle); } - (true, None) => { - println!("Shutting down server.."); - let pid = if let Some(pid) = started_pid { - pid - } else { - // We constructed a `Some` above in this case - unreachable!(); - }; - if cfg!(target_os = "windows") { - let _ = bash!(&format!("powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", pid)); - } else { - let _ = bash!(&format!("kill {}", pid)); + + let mut all_ok = true; + let mut results = Vec::new(); + for handle in handles { + match handle.join() { + Ok((suite, output, ok)) => { + results.push((suite, output, ok)); + } + Err(_) => { + results.push(("".to_string(), "thread panicked".to_string(), false)); + } + } + } + + // Print outputs in a stable order. + results.sort_by(|a, b| a.0.cmp(&b.0)); + for (suite, output, ok) in &results { + println!("===== smoketests suite: {} =====", suite); + print!("{}", output); + println!( + "===== end suite: {} (status: {}) =====", + suite, + if *ok { "ok" } else { "FAILED" } + ); + if !ok { + all_ok = false; } } - (false, None) => {} - } - test_result?; + if !all_ok { + bail!("One or more smoketest suites failed"); + } + } } Some(CiCmd::UpdateFlow { From 3516f6908f9804d27a5d8a5ace4eec1af68d73a4 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 14:02:00 -0800 Subject: [PATCH 046/160] [bfops/parallel-smoketests]: WIP --- tools/ci/src/main.rs | 313 ++++++++++++------------------------------- 1 file changed, 85 insertions(+), 228 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 2480e248f0a..a9d2c171c1c 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,11 +1,9 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use log::warn; use std::collections::HashMap; -use std::net::TcpListener; use std::path::Path; -use std::thread; use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -68,12 +66,6 @@ enum CiCmd { long_help = "Use docker for smoketests, specifying a docker compose file. If no value is provided, docker-compose.yml is used by default. This cannot be combined with --start-server." )] docker: Option, - #[arg( - long = "parallel", - default_value_t = false, - long_help = "Run smoketest suites in parallel, one process per top-level suite" - )] - parallel: bool, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -143,179 +135,109 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { Ok(()) } -fn prebuild_bare_server() -> Result<()> { - // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests - // before the server is up. - bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone") +fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { + let mut env = env::vars().collect::>(); + env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); + log::debug!("$ {cmdline}"); + let status = cmd!("bash", "-lc", cmdline).full_env(env).run()?; + if !status.status.success() { + let e = anyhow::anyhow!("command failed: {cmdline}"); + log::error!("{e}"); + return Err(e); + } + Ok(()) } -fn run_smoketests_inner( - python: &str, - suite_name: Option<&str>, - start_server: bool, - docker: &Option, - per_suite: bool, - args: &[String], -) -> (String, bool) { - let mut output = String::new(); - let mut ok = true; - - // Server setup - let mut pid: Option = None; - let mut port: Option = None; - if let Some(compose_file) = docker.as_ref() { - output.push_str("Starting server..\n"); - // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. - let cmdline = format!("docker compose -f {} up -d", compose_file); - let res = cmd!("bash", "-lc", &cmdline).stderr_to_stdout().run(); - if let Err(e) = res { - output.push_str(&format!("Failed to start docker: {}\n", e)); - return (output, false); - } - } else if start_server { - // Bare server: shared (port 5432) in serial mode, or per-suite port + --remote-server in parallel mode. - if !per_suite { - if let Err(e) = prebuild_bare_server() { - output.push_str(&format!("Failed to prebuild server: {}\n", e)); - return (output, false); - } - output.push_str("Starting server..\n"); - let cmdline = "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!"; - let pid_str = cmd!("bash", "-lc", cmdline).read().unwrap_or_default(); - match pid_str.trim().parse::() { - Ok(p) => pid = Some(p), - Err(e) => { - output.push_str(&format!("Failed to parse server PID from '{}': {}\n", pid_str, e)); - return (output, false); - } +fn run_smoketests_batch(start_server: bool, docker: &Option, args: &[String]) -> Result<()> { + let mut started_pid: Option = None; + match (start_server, docker.as_ref()) { + (start_server, Some(compose_file)) => { + if !start_server { + warn!("--docker implies --start-server=true"); } - } else { - let free_port = match find_free_port() { - Ok(p) => p, - Err(e) => { - output.push_str(&format!("Failed to find free port: {}\n", e)); - return (output, false); - } - }; - port = Some(free_port); - output.push_str(&format!("Starting local server on port {}..\n", free_port)); - let cmdline = format!( - "nohup cargo run -p spacetimedb-cli -- start --pg-port {} >/dev/null 2>&1 & echo $!", - free_port - ); - let pid_str = cmd!("bash", "-lc", &cmdline).read().unwrap_or_default(); - match pid_str.trim().parse::() { - Ok(p) => pid = Some(p), - Err(e) => { - output.push_str(&format!("Failed to parse server PID from '{}': {}\n", pid_str, e)); - return (output, false); - } + println!("Starting server.."); + // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. + bash!(&format!("docker compose -f {compose_file} up -d"))?; + } + (true, None) => { + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests + // before the server is up. + bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + + println!("Starting server.."); + let pid_str; + if cfg!(target_os = "windows") { + pid_str = cmd!( + "powershell", + "-NoProfile", + "-Command", + "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --pg-port 5432' -PassThru; $p.Id" + ) + .read() + .unwrap_or_default(); + } else { + pid_str = cmd!( + "bash", + "-lc", + "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!" + ) + .read() + .unwrap_or_default(); } + started_pid = Some( + pid_str + .trim() + .parse::() + .expect("Failed to get PID of started process"), + ) } + (false, None) => {} } - // Build smoketests args - let mut smoketests_args = Vec::new(); - if let Some(name) = suite_name { - smoketests_args.push(name.to_string()); - } - smoketests_args.extend(args.iter().cloned()); + // TODO: does this work on windows? + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + let python = if py3_available { "python3" } else { "python" }; + println!("Running smoketests.."); + let mut smoketests_args = args.to_vec(); if let Some(compose_file) = docker.as_ref() { // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to // run the server in docker while still retaining full control over what tests they want. smoketests_args.push("--compose-file".to_string()); smoketests_args.push(compose_file.to_string()); } + let test_result = bash!(&format!("{python} -m smoketests {}", smoketests_args.join(" "))); - if start_server && docker.is_none() && per_suite { - if let Some(p) = port { - smoketests_args.push("--remote-server".to_string()); - smoketests_args.push(format!("http://127.0.0.1:{}", p)); - } - } - - output.push_str("Running smoketests..\n"); - let cmdline = format!("{} -m smoketests {}", python, smoketests_args.join(" ")); - let res = cmd!("bash", "-lc", &cmdline).stderr_to_stdout().read(); - match res { - Ok(out) => { - output.push_str(&out); - } - Err(e) => { - output.push_str(&format!("smoketests failed: {}\n", e)); - ok = false; + // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process + match (start_server, docker.as_ref()) { + (_, Some(compose_file)) => { + println!("Shutting down server.."); + let _ = bash!(&format!("docker compose -f {compose_file} down")); } - } - - // Shutdown - if let Some(compose_file) = docker.as_ref() { - output.push_str("Shutting down server..\n"); - let down_cmd = format!("docker compose -f {} down", compose_file); - let _ = cmd!("bash", "-lc", &down_cmd).run(); - } - - if let Some(p) = pid { - output.push_str("Shutting down server..\n"); - let kill_cmd = format!("kill {}", p); - let _ = cmd!("bash", "-lc", &kill_cmd).run(); - } - - (output, ok) -} - -fn run_smoketests_serial(python: &str, start_server: bool, docker: &Option, args: &[String]) -> Result<()> { - let (output, ok) = run_smoketests_inner(python, None, start_server, docker, false, args); - print!("{}", output); - if !ok { - bail!("smoketests failed"); - } - Ok(()) -} - -fn find_free_port() -> Result { - let listener = TcpListener::bind("127.0.0.1:0").context("failed to bind to an ephemeral port")?; - let port = listener - .local_addr() - .context("failed to read local address for ephemeral port")? - .port(); - drop(listener); - Ok(port) -} - -fn list_smoketest_suites() -> Result> { - let mut suites = Vec::new(); - for entry in fs::read_dir("smoketests/tests").context("failed to read smoketests/tests directory")? { - let entry = entry?; - let path = entry.path(); - if let Some(name) = path.file_name().and_then(|n| n.to_str()) { - if name == "__init__.py" { - continue; - } - if let Some(ext) = path.extension().and_then(|e| e.to_str()) { - if ext == "py" { - if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) { - suites.push(stem.to_string()); - } - } + (true, None) => { + println!("Shutting down server.."); + let pid = if let Some(pid) = started_pid { + pid + } else { + // We constructed a `Some` above in this case + unreachable!(); + }; + if cfg!(target_os = "windows") { + let _ = bash!(&format!( + "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", + pid + )); + } else { + let _ = bash!(&format!("kill {}", pid)); } } + (false, None) => {} } - suites.sort(); - Ok(suites) -} -fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { - let mut env = env::vars().collect::>(); - env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); - log::debug!("$ {cmdline}"); - let status = cmd!("bash", "-lc", cmdline).full_env(env).run()?; - if !status.status.success() { - let e = anyhow::anyhow!("command failed: {cmdline}"); - log::error!("{e}"); - return Err(e); - } - Ok(()) + test_result } fn main() -> Result<()> { @@ -356,74 +278,9 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { start_server, docker, - parallel, args, }) => { - // TODO: does this work on windows? - let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") - .run() - .map(|s| s.status.success()) - .unwrap_or(false); - let python = if py3_available { "python3" } else { "python" }; - - if !parallel { - run_smoketests_serial(python, start_server, &docker, &args)?; - } else { - // Parallel mode: run each top-level smoketest suite in its own process. - let suites = list_smoketest_suites()?; - if suites.is_empty() { - bail!("No smoketest suites found in smoketests/tests"); - } - - let mut handles = Vec::new(); - for suite in suites { - let suite_name = suite.clone(); - let docker = docker.clone(); - let args = args.clone(); - let python = python.to_string(); - let start_server = start_server; - - let handle = thread::spawn(move || { - let (output, ok) = - run_smoketests_inner(&python, Some(&suite_name), start_server, &docker, true, &args); - (suite_name, output, ok) - }); - - handles.push(handle); - } - - let mut all_ok = true; - let mut results = Vec::new(); - for handle in handles { - match handle.join() { - Ok((suite, output, ok)) => { - results.push((suite, output, ok)); - } - Err(_) => { - results.push(("".to_string(), "thread panicked".to_string(), false)); - } - } - } - - // Print outputs in a stable order. - results.sort_by(|a, b| a.0.cmp(&b.0)); - for (suite, output, ok) in &results { - println!("===== smoketests suite: {} =====", suite); - print!("{}", output); - println!( - "===== end suite: {} (status: {}) =====", - suite, - if *ok { "ok" } else { "FAILED" } - ); - if !ok { - all_ok = false; - } - } - - if !all_ok { - bail!("One or more smoketest suites failed"); - } - } + run_smoketests_batch(start_server, &docker, &args)?; } Some(CiCmd::UpdateFlow { From a0ccb9ebf41441ed9d494b677a5906be6e9784ee Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 14:39:09 -0800 Subject: [PATCH 047/160] [bfops/parallel-smoketests]: WIP --- .github/docker-compose.yml | 4 +-- docker-compose.yml | 6 ++-- tools/ci/src/main.rs | 65 +++++++++++++++++++++++++++++++------- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/.github/docker-compose.yml b/.github/docker-compose.yml index 400c044c4ed..76e3eb251d2 100644 --- a/.github/docker-compose.yml +++ b/.github/docker-compose.yml @@ -6,9 +6,9 @@ services: context: ../ dockerfile: .github/Dockerfile ports: - - "3000:3000" + - "${STDB_PORT:-3000}:3000" # Postgres - - "5432:5432" + - "${STDB_PG_PORT:-5432}:5432" entrypoint: spacetime start --pg-port 5432 privileged: true environment: diff --git a/docker-compose.yml b/docker-compose.yml index 718281892dd..0b00256a808 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,11 +24,11 @@ services: - key_files:/etc/spacetimedb - /stdb ports: - - "3000:3000" + - "${STDB_PORT:-3000}:3000" # Postgres - - "5432:5432" + - "${STDB_PG_PORT:-5432}:5432" # Tracy - - "8086:8086" + - "${STDB_TRACY_PORT:-8086}:8086" entrypoint: cargo watch -i flamegraphs -i log.conf --why -C crates/standalone -x 'run start --data-dir=/stdb/data --jwt-pub-key-path=/etc/spacetimedb/id_ecdsa.pub --jwt-priv-key-path=/etc/spacetimedb/id_ecdsa --pg-port 5432' privileged: true environment: diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index a9d2c171c1c..9e93d996902 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,8 +1,9 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use log::warn; use std::collections::HashMap; +use std::net::TcpListener; use std::path::Path; use std::{env, fs}; @@ -148,30 +149,57 @@ fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { Ok(()) } -fn run_smoketests_batch(start_server: bool, docker: &Option, args: &[String]) -> Result<()> { +#[derive(Debug, Clone)] +pub enum StartServer { + /// Do not start a server. + No, + + /// Start a server normally. + Yes { random_port: bool }, + + /// Start a server using Docker Compose. + Docker { random_port: bool, compose_file: PathBuf }, +} + +fn find_free_port() -> Result { + let listener = TcpListener::bind("127.0.0.1:0").context("failed to bind to an ephemeral port")?; + let port = listener + .local_addr() + .context("failed to read local address for ephemeral port")? + .port(); + drop(listener); + Ok(port) +} + +fn run_smoketests_batch(start_server: StartServer, args: &[String]) -> Result<()> { let mut started_pid: Option = None; match (start_server, docker.as_ref()) { (start_server, Some(compose_file)) => { - if !start_server { - warn!("--docker implies --start-server=true"); - } println!("Starting server.."); - // This means we ignore `.dockerignore`, beacuse it omits `target`, which our CI Dockerfile needs. - bash!(&format!("docker compose -f {compose_file} up -d"))?; + let server_port = find_free_port()?; + let pg_port = find_free_port()?; + let tracy_port = find_free_port()?; + bash!(&format!( + "STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port} docker compose -f {compose_file} up -d" + ))?; } (true, None) => { // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests // before the server is up. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; - println!("Starting server.."); + // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port + let server_port = find_free_port()?; + println!("Starting server on port {server_port}.."); let pid_str; if cfg!(target_os = "windows") { pid_str = cmd!( "powershell", "-NoProfile", "-Command", - "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --pg-port 5432' -PassThru; $p.Id" + &format!( + "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --listen-addr 0.0.0.0:{server_port} --pg-port 5432' -PassThru; $p.Id" + ) ) .read() .unwrap_or_default(); @@ -179,7 +207,9 @@ fn run_smoketests_batch(start_server: bool, docker: &Option, args: &[Str pid_str = cmd!( "bash", "-lc", - "nohup cargo run -p spacetimedb-cli -- start --pg-port 5432 >/dev/null 2>&1 & echo $!" + &format!( + "nohup cargo run -p spacetimedb-cli -- start --listen-addr 0.0.0.0:{server_port} --pg-port 5432 >/dev/null 2>&1 & echo $!" + ) ) .read() .unwrap_or_default(); @@ -280,7 +310,20 @@ fn main() -> Result<()> { docker, args, }) => { - run_smoketests_batch(start_server, &docker, &args)?; + let start_server = match (start_server, docker.as_ref()) { + (start_server, Some(compose_file)) => { + if !start_server { + warn!("--docker implies --start-server=true"); + } + StartServer::Docker { + random_port: false, + compose_file: compose_file.into(), + } + } + (true, None) => StartServer::Yes { random_port: false }, + (false, None) => StartServer::No, + }; + run_smoketests_batch(start_server, &args)?; } Some(CiCmd::UpdateFlow { From cd2db36ce568f0c8f62c4ac248b6a67be1696118 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 15:02:26 -0800 Subject: [PATCH 048/160] [bfops/parallel-smoketests]: builds with random port selection / project names --- tools/ci/src/main.rs | 118 ++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 9e93d996902..b2e29377c4b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -4,7 +4,7 @@ use duct::cmd; use log::warn; use std::collections::HashMap; use std::net::TcpListener; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -151,14 +151,16 @@ fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { #[derive(Debug, Clone)] pub enum StartServer { - /// Do not start a server. No, - - /// Start a server normally. Yes { random_port: bool }, + Docker { compose_file: PathBuf, random_port: bool }, +} - /// Start a server using Docker Compose. - Docker { random_port: bool, compose_file: PathBuf }, +#[derive(Debug, Clone)] +pub enum ServerState { + None, + Yes { pid: i32 }, + Docker { compose_file: PathBuf, project: String }, } fn find_free_port() -> Result { @@ -171,58 +173,75 @@ fn find_free_port() -> Result { Ok(port) } -fn run_smoketests_batch(start_server: StartServer, args: &[String]) -> Result<()> { - let mut started_pid: Option = None; - match (start_server, docker.as_ref()) { - (start_server, Some(compose_file)) => { +fn run_smoketests_batch(server_mode: StartServer, args: &[String]) -> Result<()> { + let server_state = match server_mode { + StartServer::No => ServerState::None, + StartServer::Docker { + compose_file, + random_port, + } => { println!("Starting server.."); - let server_port = find_free_port()?; - let pg_port = find_free_port()?; - let tracy_port = find_free_port()?; + let env_string; + let project; + if random_port { + let server_port = find_free_port()?; + let pg_port = find_free_port()?; + let tracy_port = find_free_port()?; + env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); + project = format!("spacetimedb-smoketests-{server_port}"); + } else { + env_string = String::new(); + project = "spacetimedb-smoketests".to_string(); + }; + let compose_str = compose_file.to_string_lossy(); bash!(&format!( - "STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port} docker compose -f {compose_file} up -d" + "{env_string} docker compose -f {compose_str} --project {project} up -d" ))?; + ServerState::Docker { compose_file, project } } - (true, None) => { + StartServer::Yes { random_port } => { // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests // before the server is up. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port - let server_port = find_free_port()?; - println!("Starting server on port {server_port}.."); + let arg_string = if random_port { + let server_port = find_free_port()?; + let pg_port = find_free_port()?; + &format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port}") + } else { + "--pg-port 5432" + }; + println!("Starting server.."); let pid_str; if cfg!(target_os = "windows") { pid_str = cmd!( - "powershell", - "-NoProfile", - "-Command", - &format!( - "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start --listen-addr 0.0.0.0:{server_port} --pg-port 5432' -PassThru; $p.Id" + "powershell", + "-NoProfile", + "-Command", + &format!( + "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start {arg_string}' -PassThru; $p.Id" + ) ) - ) - .read() - .unwrap_or_default(); + .read() + .unwrap_or_default(); } else { pid_str = cmd!( "bash", "-lc", - &format!( - "nohup cargo run -p spacetimedb-cli -- start --listen-addr 0.0.0.0:{server_port} --pg-port 5432 >/dev/null 2>&1 & echo $!" - ) + &format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!") ) .read() .unwrap_or_default(); } - started_pid = Some( - pid_str + ServerState::Yes { + pid: pid_str .trim() .parse::() .expect("Failed to get PID of started process"), - ) + } } - (false, None) => {} - } + }; // TODO: does this work on windows? let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") @@ -232,29 +251,18 @@ fn run_smoketests_batch(start_server: StartServer, args: &[String]) -> Result<() let python = if py3_available { "python3" } else { "python" }; println!("Running smoketests.."); - let mut smoketests_args = args.to_vec(); - if let Some(compose_file) = docker.as_ref() { - // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to - // run the server in docker while still retaining full control over what tests they want. - smoketests_args.push("--compose-file".to_string()); - smoketests_args.push(compose_file.to_string()); - } - let test_result = bash!(&format!("{python} -m smoketests {}", smoketests_args.join(" "))); + let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process - match (start_server, docker.as_ref()) { - (_, Some(compose_file)) => { + match server_state { + ServerState::None => {} + ServerState::Docker { compose_file, project } => { println!("Shutting down server.."); - let _ = bash!(&format!("docker compose -f {compose_file} down")); + let compose_str = compose_file.to_string_lossy(); + let _ = bash!(&format!("docker compose -f {compose_str} --project {project} down")); } - (true, None) => { + ServerState::Yes { pid } => { println!("Shutting down server.."); - let pid = if let Some(pid) = started_pid { - pid - } else { - // We constructed a `Some` above in this case - unreachable!(); - }; if cfg!(target_os = "windows") { let _ = bash!(&format!( "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", @@ -264,7 +272,6 @@ fn run_smoketests_batch(start_server: StartServer, args: &[String]) -> Result<() let _ = bash!(&format!("kill {}", pid)); } } - (false, None) => {} } test_result @@ -323,6 +330,13 @@ fn main() -> Result<()> { (true, None) => StartServer::Yes { random_port: false }, (false, None) => StartServer::No, }; + let mut args = args.to_vec(); + if let Some(compose_file) = docker.as_ref() { + // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to + // run the server in docker while still retaining full control over what tests they want. + args.push("--compose-file".to_string()); + args.push(compose_file.to_string()); + } run_smoketests_batch(start_server, &args)?; } From 045753431992a67c7ab8503eab66a60740186189 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 21 Nov 2025 15:05:31 -0800 Subject: [PATCH 049/160] [bfops/parallel-smoketests]: add --local-only --- smoketests/__init__.py | 1 + smoketests/__main__.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/smoketests/__init__.py b/smoketests/__init__.py index 93e179e30dd..cd50ef11575 100644 --- a/smoketests/__init__.py +++ b/smoketests/__init__.py @@ -80,6 +80,7 @@ def requires_anonymous_login(item): return item def requires_local_server(item): + setattr(item, "_requires_local_server", True) if REMOTE_SERVER: return unittest.skip("running against a remote server")(item) return item diff --git a/smoketests/__main__.py b/smoketests/__main__.py index 7d435fc9f89..c963744f230 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -85,6 +85,7 @@ def main(): parser.add_argument("--list", action="store_true", help="list the tests that would be run, but don't run them") parser.add_argument("--remote-server", action="store", help="Run against a remote server") parser.add_argument("--spacetime-login", action="store_true", help="Use `spacetime login` for these tests (and disable tests that don't work with that)") + parser.add_argument("--local-only", action="store_true", help="Only run tests that require a local server") args = parser.parse_args() if args.docker: @@ -114,6 +115,25 @@ def main(): loader.testNamePatterns = args.testNamePatterns tests = loader.loadTestsFromNames(testlist) + + if args.local_only: + def _is_local_only(test_case): + method_name = getattr(test_case, "_testMethodName", None) + if method_name is not None and hasattr(test_case, method_name): + method = getattr(test_case, method_name) + if getattr(method, "_requires_local_server", False): + return True + # Also allow class-level decoration + if getattr(test_case.__class__, "_requires_local_server", False): + return True + return False + + filtered = unittest.TestSuite() + for t in _iter_all_tests(tests): + if _is_local_only(t): + filtered.addTest(t) + tests = filtered + if args.list: failed_cls = getattr(unittest.loader, "_FailedTest", None) any_failed = False From 9eef7cbeca40c70fbb5d1224a3cd9b7a178dde38 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 12:07:00 -0800 Subject: [PATCH 050/160] [bfops/parallel-smoketests]: add smoketests --list=json --- smoketests/__main__.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index c963744f230..b4f2160352f 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -82,7 +82,7 @@ def main(): help='Only run tests which match the given substring') parser.add_argument("-x", dest="exclude", nargs="*", default=[]) parser.add_argument("--no-build-cli", action="store_true", help="don't cargo build the cli") - parser.add_argument("--list", action="store_true", help="list the tests that would be run, but don't run them") + parser.add_argument("--list", nargs="?", const="text", choices=("text", "json"), default=None, help="list the tests that would be run (optionally as 'text' or 'json'), but don't run them") parser.add_argument("--remote-server", action="store", help="Run against a remote server") parser.add_argument("--spacetime-login", action="store_true", help="Use `spacetime login` for these tests (and disable tests that don't work with that)") parser.add_argument("--local-only", action="store_true", help="Only run tests that require a local server") @@ -134,22 +134,39 @@ def _is_local_only(test_case): filtered.addTest(t) tests = filtered - if args.list: + if args.list is not None: failed_cls = getattr(unittest.loader, "_FailedTest", None) any_failed = False + test_names = [] + failed_tests = [] for test in _iter_all_tests(tests): name = test.id() if isinstance(test, failed_cls): any_failed = True - print('') - print("Failed to construct %s:" % test.id()) exc = getattr(test, "_exception", None) - if exc is not None: - tb = ''.join(traceback.format_exception(exc)) - print(tb.rstrip()) - print('') + tb = ''.join(traceback.format_exception(exc)) if exc is not None else None + failed_tests.append({ + "test_id": name, + "error": tb.rstrip() if tb is not None else None, + }) + if args.list == "text": + print('') + print("Failed to construct %s:" % name) + if tb is not None: + print(tb.rstrip()) + print('') else: - print(f"{name}") + test_names.append(name) + if args.list == "text": + print(f"{name}") + + if args.list == "json": + output = { + "tests": test_names, + "errors": failed_tests, + } + print(json.dumps(output)) + exit(1 if any_failed else 0) if not args.no_build_cli: From a5149212cd578818888f1ee4693885d359ade9af Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 12:53:44 -0800 Subject: [PATCH 051/160] [bfops/parallel-smoketests]: WIP parallel logic --- Cargo.lock | 1 + tools/ci/Cargo.toml | 1 + tools/ci/src/main.rs | 99 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66c824d694f..09db7759ff2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -874,6 +874,7 @@ dependencies = [ "duct", "log", "regex", + "serde_json", ] [[package]] diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index e907526a42c..19a40d0afe1 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -10,3 +10,4 @@ chrono = { workspace = true, features=["clock"] } clap.workspace = true regex.workspace = true duct.workspace = true +serde_json.workspace = true diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index b2e29377c4b..c4016b299a5 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -2,7 +2,8 @@ use anyhow::{bail, Context, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; use log::warn; -use std::collections::HashMap; +use serde_json; +use std::collections::{HashMap, HashSet}; use std::net::TcpListener; use std::path::{Path, PathBuf}; use std::{env, fs}; @@ -67,6 +68,12 @@ enum CiCmd { long_help = "Use docker for smoketests, specifying a docker compose file. If no value is provided, docker-compose.yml is used by default. This cannot be combined with --start-server." )] docker: Option, + #[arg( + long = "parallel", + default_value_t = false, + long_help = "Run smoketests in parallel batches grouped by test suite" + )] + parallel: bool, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -173,7 +180,7 @@ fn find_free_port() -> Result { Ok(port) } -fn run_smoketests_batch(server_mode: StartServer, args: &[String]) -> Result<()> { +fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { let server_state = match server_mode { StartServer::No => ServerState::None, StartServer::Docker { @@ -243,13 +250,6 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String]) -> Result<()> } }; - // TODO: does this work on windows? - let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") - .run() - .map(|s| s.status.success()) - .unwrap_or(false); - let python = if py3_available { "python3" } else { "python" }; - println!("Running smoketests.."); let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); @@ -315,6 +315,7 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { start_server, docker, + parallel, args, }) => { let start_server = match (start_server, docker.as_ref()) { @@ -323,11 +324,11 @@ fn main() -> Result<()> { warn!("--docker implies --start-server=true"); } StartServer::Docker { - random_port: false, + random_port: parallel, compose_file: compose_file.into(), } } - (true, None) => StartServer::Yes { random_port: false }, + (true, None) => StartServer::Yes { random_port: parallel }, (false, None) => StartServer::No, }; let mut args = args.to_vec(); @@ -337,7 +338,81 @@ fn main() -> Result<()> { args.push("--compose-file".to_string()); args.push(compose_file.to_string()); } - run_smoketests_batch(start_server, &args)?; + + // TODO: does this work on windows? + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + let python = if py3_available { "python3" } else { "python" }; + + if parallel { + println!("Listing smoketests for parallel execution.."); + + let mut list_args: Vec = args.to_vec(); + list_args.push("--list=json".to_string()); + let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); + + // TODO: do actually check the return code here. and make --list=json not return non-zero if there are errors. + let list_output = cmd!("bash", "-lc", list_cmdline) + .stderr_to_stdout() + .unchecked() + .read()?; + + let parsed: serde_json::Value = serde_json::from_str(&list_output)?; + let tests = parsed.get("tests").and_then(|v| v.as_array()).cloned().unwrap(); + let errors = parsed + .get("errors") + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); + + if !errors.is_empty() { + println!("Errors while constructing smoketests:"); + for err in &errors { + let test_id = err.get("test_id").and_then(|v| v.as_str()).unwrap(); + let msg = err.get("error").and_then(|v| v.as_str()).unwrap(); + println!("{test_id}"); + println!("{msg}"); + } + // If there were errors constructing tests, treat this as a failure + // and do not run any batches. + return Err(anyhow::anyhow!( + "Errors encountered while constructing smoketests; aborting parallel run" + )); + } + + let batches: HashSet = tests + .into_iter() + .map(|t| { + let name = t.as_str().unwrap(); + let parts = name.split('.').collect::>(); + parts[2].to_string() + }) + .collect(); + + let mut any_failed_batch = false; + for batch in batches { + println!("Running smoketests batch {batch}.."); + // TODO: this doesn't work properly if the user passed multiple batches as input. + let mut batch_args: Vec = Vec::new(); + batch_args.push(batch.clone()); + batch_args.extend(args.iter().cloned()); + + // TODO: capture output and print it only in contiguous blocks + let result = run_smoketests_batch(start_server.clone(), &batch_args, python); + + if result.is_err() { + any_failed_batch = true; + } + } + + if any_failed_batch { + anyhow::bail!("One or more smoketest batches failed"); + } + } else { + run_smoketests_batch(start_server, &args, python)?; + } } Some(CiCmd::UpdateFlow { From 45ee326998bc289af1c668dd524f2d34f0a1bb5b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 13:04:39 -0800 Subject: [PATCH 052/160] [bfops/parallel-smoketests]: custom python path option --- tools/ci/src/main.rs | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c4016b299a5..b5b3eff2fa9 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -74,6 +74,12 @@ enum CiCmd { long_help = "Run smoketests in parallel batches grouped by test suite" )] parallel: bool, + #[arg( + long = "python", + value_name = "PYTHON_PATH", + long_help = "Python interpreter to use for smoketests" + )] + python: Option, #[arg( trailing_var_arg = true, long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" @@ -316,6 +322,7 @@ fn main() -> Result<()> { start_server, docker, parallel, + python, args, }) => { let start_server = match (start_server, docker.as_ref()) { @@ -339,12 +346,20 @@ fn main() -> Result<()> { args.push(compose_file.to_string()); } - // TODO: does this work on windows? - let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") - .run() - .map(|s| s.status.success()) - .unwrap_or(false); - let python = if py3_available { "python3" } else { "python" }; + let python = if let Some(p) = python { + p + } else { + // TODO: does this work on windows? + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + if py3_available { + "python3".to_string() + } else { + "python".to_string() + } + }; if parallel { println!("Listing smoketests for parallel execution.."); @@ -400,7 +415,7 @@ fn main() -> Result<()> { batch_args.extend(args.iter().cloned()); // TODO: capture output and print it only in contiguous blocks - let result = run_smoketests_batch(start_server.clone(), &batch_args, python); + let result = run_smoketests_batch(start_server.clone(), &batch_args, &python); if result.is_err() { any_failed_batch = true; @@ -411,7 +426,7 @@ fn main() -> Result<()> { anyhow::bail!("One or more smoketest batches failed"); } } else { - run_smoketests_batch(start_server, &args, python)?; + run_smoketests_batch(start_server, &args, &python)?; } } From 46793a4077d6818086110408b80e7e53a2aa893a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 13:07:03 -0800 Subject: [PATCH 053/160] [bfops/parallel-smoketests]: actual parallel --- tools/ci/src/main.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index b5b3eff2fa9..9acfaa0fa70 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -406,17 +406,29 @@ fn main() -> Result<()> { }) .collect(); - let mut any_failed_batch = false; + // Run each batch in parallel threads. + let mut handles = Vec::new(); for batch in batches { - println!("Running smoketests batch {batch}.."); - // TODO: this doesn't work properly if the user passed multiple batches as input. + let start_server_clone = start_server.clone(); + let python_clone = python.clone(); let mut batch_args: Vec = Vec::new(); + // TODO: this doesn't work properly if the user passed multiple batches as input. batch_args.push(batch.clone()); batch_args.extend(args.iter().cloned()); - // TODO: capture output and print it only in contiguous blocks - let result = run_smoketests_batch(start_server.clone(), &batch_args, &python); + handles.push(std::thread::spawn(move || { + println!("Running smoketests batch {batch}.."); + // TODO: capture output and print it only in contiguous blocks + run_smoketests_batch(start_server_clone, &batch_args, &python_clone) + })); + } + let mut any_failed_batch = false; + for handle in handles { + // If the thread panicked or the batch failed, treat it as a failure. + let result = handle + .join() + .unwrap_or_else(|_| Err(anyhow::anyhow!("smoketest batch thread panicked",))); if result.is_err() { any_failed_batch = true; } From f3944f3a8f11dda0e4492b08dbb5fde33617651d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 13:09:36 -0800 Subject: [PATCH 054/160] [bfops/parallel-smoketests]: TODO --- tools/ci/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 9acfaa0fa70..d02a780ac49 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -215,6 +215,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) StartServer::Yes { random_port } => { // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests // before the server is up. + // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port From e98d52563edebc97941b940fde93f495f317b5d0 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 16:30:52 -0800 Subject: [PATCH 055/160] [bfops/parallel-smoketests]: review --- tools/ci/src/main.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d02a780ac49..fc7dbe00cec 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -276,6 +276,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) pid )); } else { + // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? let _ = bash!(&format!("kill {}", pid)); } } @@ -417,26 +418,29 @@ fn main() -> Result<()> { batch_args.push(batch.clone()); batch_args.extend(args.iter().cloned()); - handles.push(std::thread::spawn(move || { - println!("Running smoketests batch {batch}.."); - // TODO: capture output and print it only in contiguous blocks - run_smoketests_batch(start_server_clone, &batch_args, &python_clone) - })); + handles.push(( + batch.clone(), + std::thread::spawn(move || { + println!("Running smoketests batch {batch}.."); + // TODO: capture output and print it only in contiguous blocks + run_smoketests_batch(start_server_clone, &batch_args, &python_clone) + }), + )); } - let mut any_failed_batch = false; - for handle in handles { + let mut failed_batches = vec![]; + for (batch, handle) in handles { // If the thread panicked or the batch failed, treat it as a failure. let result = handle .join() .unwrap_or_else(|_| Err(anyhow::anyhow!("smoketest batch thread panicked",))); if result.is_err() { - any_failed_batch = true; + failed_batches.push(batch); } } - if any_failed_batch { - anyhow::bail!("One or more smoketest batches failed"); + if !failed_batches.is_empty() { + anyhow::bail!("Smoketest batch(es) failed: {}", failed_batches.join(", ")); } } else { run_smoketests_batch(start_server, &args, &python)?; From 52e3f6edcbc4f178f5d47e90fc3ac7abf490aa93 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 16:32:41 -0800 Subject: [PATCH 056/160] [bfops/parallel-smoketests]: use parallel smoketests in CI --- .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 b1928b0a600..0b547ee7e9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,10 +108,10 @@ jobs: run: | # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore - cargo ci smoketests --docker .github/docker-compose.yml -- ${SMOKETEST_ARGS} + cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} - name: Run smoketests (Windows) if: runner.os == 'Windows' - run: cargo ci smoketests -- ${SMOKETEST_ARGS} + run: cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} test: name: Test Suite From 40cb0deb16d492dfab0d95e1ee4039013e89dabc Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 16:37:39 -0800 Subject: [PATCH 057/160] [bfops/parallel-smoketests]: Don't build CLI in smoketests if we're already building it --- tools/ci/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index fc7dbe00cec..246270eee71 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -187,6 +187,7 @@ fn find_free_port() -> Result { } fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { + let mut args: Vec<_> = args.iter().cloned().collect(); let server_state = match server_mode { StartServer::No => ServerState::None, StartServer::Docker { @@ -217,6 +218,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) // before the server is up. // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + args.push("--no-build-cli".into()); // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port let arg_string = if random_port { From 83f3fffb3ecbb52313edabe2cf9d44fab22a2c1f Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 16:43:25 -0800 Subject: [PATCH 058/160] [bfops/parallel-smoketests]: consolidate builds, and fix --- tools/ci/src/main.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 246270eee71..b37ee642ed3 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -187,7 +187,6 @@ fn find_free_port() -> Result { } fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { - let mut args: Vec<_> = args.iter().cloned().collect(); let server_state = match server_mode { StartServer::No => ServerState::None, StartServer::Docker { @@ -209,17 +208,11 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) }; let compose_str = compose_file.to_string_lossy(); bash!(&format!( - "{env_string} docker compose -f {compose_str} --project {project} up -d" + "{env_string} docker compose -f {compose_str} --project-name {project} up -d" ))?; ServerState::Docker { compose_file, project } } StartServer::Yes { random_port } => { - // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests - // before the server is up. - // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. - bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; - args.push("--no-build-cli".into()); - // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port let arg_string = if random_port { let server_port = find_free_port()?; @@ -268,7 +261,9 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) ServerState::Docker { compose_file, project } => { println!("Shutting down server.."); let compose_str = compose_file.to_string_lossy(); - let _ = bash!(&format!("docker compose -f {compose_str} --project {project} down")); + let _ = bash!(&format!( + "docker compose -f {compose_str} --project-name {project} down" + )); } ServerState::Yes { pid } => { println!("Shutting down server.."); @@ -410,6 +405,14 @@ fn main() -> Result<()> { }) .collect(); + if matches!(start_server, StartServer::Yes { .. }) { + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests + // before the server is up. + // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. + bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + args.push("--no-build-cli".into()); + } + // Run each batch in parallel threads. let mut handles = Vec::new(); for batch in batches { From 75b47acc4f6232ebb83dedda58efe23454ca77a6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 16:54:21 -0800 Subject: [PATCH 059/160] [bfops/parallel-smoketests]: todos --- tools/ci/src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index b37ee642ed3..5a58cb2edb6 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -235,6 +235,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) .read() .unwrap_or_default(); } else { + // TODO: Maybe we do this in a thread instead? Then it's easier to kill pid_str = cmd!( "bash", "-lc", @@ -253,6 +254,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) }; println!("Running smoketests.."); + // TODO: Don't we need to _use_ the port here?! let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process From 73548c1f239fa9ff2f9d0b14c248a83d1738eb91 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 23:01:51 -0800 Subject: [PATCH 060/160] [bfops/parallel-smoketests]: Pre-build in non-parallel case too --- tools/ci/src/main.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 5a58cb2edb6..aafc445399c 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -362,6 +362,16 @@ fn main() -> Result<()> { } }; + if matches!(start_server, StartServer::Yes { .. }) { + println!("Building SpacetimeDB.."); + + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests + // before the server is up. + // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. + bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + args.push("--no-build-cli".into()); + } + if parallel { println!("Listing smoketests for parallel execution.."); @@ -407,14 +417,6 @@ fn main() -> Result<()> { }) .collect(); - if matches!(start_server, StartServer::Yes { .. }) { - // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests - // before the server is up. - // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. - bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; - args.push("--no-build-cli".into()); - } - // Run each batch in parallel threads. let mut handles = Vec::new(); for batch in batches { From 04eeb8c0e8c6d7ddac4e347b807e9d3f4c83e5df Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 24 Nov 2025 23:01:59 -0800 Subject: [PATCH 061/160] [bfops/parallel-smoketests]: sanitize cargo env --- tools/ci/src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index aafc445399c..93cf0028312 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -287,6 +287,13 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) fn main() -> Result<()> { let cli = Cli::parse(); + // Remove all Cargo-provided env vars from the subcommand + for (key, _) in std::env::vars() { + if key.starts_with("CARGO_") && key != "CARGO_TARGET_DIR" { + std::env::remove_var(key); + } + } + match cli.cmd { Some(CiCmd::Test) => { bash!("cargo test --all -- --skip unreal")?; From de4ab492713bb2cfb579c26f28b3695a940dcb17 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 25 Nov 2025 10:37:31 -0800 Subject: [PATCH 062/160] [bfops/parallel-smoketests]: WIP --- tools/ci/src/main.rs | 70 ++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 93cf0028312..e69057af239 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -6,7 +6,8 @@ use serde_json; use std::collections::{HashMap, HashSet}; use std::net::TcpListener; use std::path::{Path, PathBuf}; -use std::{env, fs}; +use std::time::Duration; +use std::{env, fs, thread}; const README_PATH: &str = "tools/ci/README.md"; @@ -187,6 +188,9 @@ fn find_free_port() -> Result { } fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { + // TODO: If we set --remote-server, check that it's not already in there. + let mut args: Vec<_> = args.iter().cloned().collect(); + let server_state = match server_mode { StartServer::No => ServerState::None, StartServer::Docker { @@ -202,6 +206,8 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) let tracy_port = find_free_port()?; env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); project = format!("spacetimedb-smoketests-{server_port}"); + args.push("--remote-server".into()); + args.push(format!("http://localhost:{server_port}")); } else { env_string = String::new(); project = "spacetimedb-smoketests".to_string(); @@ -214,13 +220,16 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) } StartServer::Yes { random_port } => { // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port - let arg_string = if random_port { + let arg_string; + if random_port { let server_port = find_free_port()?; let pg_port = find_free_port()?; - &format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port}") + arg_string = format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port}"); + args.push("--remote-server".into()); + args.push(format!("http://localhost:{server_port}")); } else { - "--pg-port 5432" - }; + arg_string = "--pg-port 5432".into(); + } println!("Starting server.."); let pid_str; if cfg!(target_os = "windows") { @@ -253,33 +262,36 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) } }; + // TODO: be smarter about this + println!("Waiting for server to start.."); + thread::sleep(Duration::from_secs(5)); + println!("Running smoketests.."); - // TODO: Don't we need to _use_ the port here?! let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process - match server_state { - ServerState::None => {} - ServerState::Docker { compose_file, project } => { - println!("Shutting down server.."); - let compose_str = compose_file.to_string_lossy(); - let _ = bash!(&format!( - "docker compose -f {compose_str} --project-name {project} down" - )); - } - ServerState::Yes { pid } => { - println!("Shutting down server.."); - if cfg!(target_os = "windows") { - let _ = bash!(&format!( - "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", - pid - )); - } else { - // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? - let _ = bash!(&format!("kill {}", pid)); - } - } - } + // match server_state { + // ServerState::None => {} + // ServerState::Docker { compose_file, project } => { + // println!("Shutting down server.."); + // let compose_str = compose_file.to_string_lossy(); + // let _ = bash!(&format!( + // "docker compose -f {compose_str} --project-name {project} down" + // )); + // } + // ServerState::Yes { pid } => { + // println!("Shutting down server.."); + // if cfg!(target_os = "windows") { + // let _ = bash!(&format!( + // "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", + // pid + // )); + // } else { + // // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? + // let _ = bash!(&format!("kill {}", pid)); + // } + // } + // } test_result } @@ -369,6 +381,8 @@ fn main() -> Result<()> { } }; + // TODO: Handle --local-only tests + if matches!(start_server, StartServer::Yes { .. }) { println!("Building SpacetimeDB.."); From 941763dc6069cc0406fc7b592690a11cefa61ba9 Mon Sep 17 00:00:00 2001 From: Kasama Date: Wed, 3 Dec 2025 13:06:35 -0300 Subject: [PATCH 063/160] workaround: don't use cargo ci for windows jobs --- .github/workflows/ci.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8c2853e2d..f539552c98d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,9 +111,14 @@ jobs: if: runner.os == 'Windows' - name: Install python deps run: python -m pip install -r smoketests/requirements.txt + - name: Run smoketests windows + # Note: clear_database and replication only work in private + run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams + if: runner.os == 'Windows' - name: Run smoketests # Note: clear_database and replication only work in private run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams + if: runner.os != 'Windows' - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down @@ -292,9 +297,27 @@ jobs: run: sudo apt install -y libssl-dev - name: Build spacetimedb-update + run: cargo build --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update + if: runner.os == 'Windows' + + - name: Run self-install + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + ROOT_DIR="$(mktemp -d)" + # NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. + # My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it + # happens very frequently on the `macos-runner`, but we haven't seen it on any others). + cargo run --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update -- self-install --root-dir="${ROOT_DIR}" --yes + "${ROOT_DIR}"/spacetime --root-dir="${ROOT_DIR}" help + if: runner.os == 'Windows' + + - name: Test spacetimedb-update env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: cargo ci update-flow --target=${{ matrix.target }} --github-token-auth + if: runner.os != 'Windows' unreal_engine_tests: name: Unreal Engine Tests From 31fe084856fa2c9f65f0796820b2e9cc2cb4771f Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 3 Dec 2025 13:46:36 -0800 Subject: [PATCH 064/160] [bfops/parallel-smoketests]: WIP --- tools/ci/src/main.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index e69057af239..3288c33da14 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -6,8 +6,9 @@ use serde_json; use std::collections::{HashMap, HashSet}; use std::net::TcpListener; use std::path::{Path, PathBuf}; -use std::time::Duration; -use std::{env, fs, thread}; +use std::thread::sleep; +use std::time::{Duration, Instant}; +use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -187,6 +188,19 @@ fn find_free_port() -> Result { Ok(port) } +fn wait_until_http_ready(timeout: Duration) -> Result<()> { + let deadline = Instant::now() + timeout; + + while Instant::now() < deadline { + match bash!("cargo run -p spacetimedb-cli -- server ping localhost") { + Ok(_) => return Ok(()), + Err(e) => {} + } + sleep(Duration::from_millis(500)); + } + anyhow::bail!("Timed out waiting for SpacetimeDB"); +} + fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { // TODO: If we set --remote-server, check that it's not already in there. let mut args: Vec<_> = args.iter().cloned().collect(); @@ -253,6 +267,8 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) .read() .unwrap_or_default(); } + println!("Waiting for server to start.."); + wait_until_http_ready(Duration::from_secs(60))?; ServerState::Yes { pid: pid_str .trim() @@ -262,10 +278,6 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) } }; - // TODO: be smarter about this - println!("Waiting for server to start.."); - thread::sleep(Duration::from_secs(5)); - println!("Running smoketests.."); let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); From ae304d4836a37cf2ab4b4b70800dd6657ef48aa5 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 5 Dec 2025 09:48:14 -0800 Subject: [PATCH 065/160] [bfops/parallel-smoketests]: WIP --- tools/ci/src/main.rs | 131 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 20 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 3288c33da14..78c6d8c3c9c 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -57,11 +57,22 @@ enum CiCmd { /// Executes the smoketests suite with some default exclusions. Smoketests { #[arg( - long = "start-server", - default_value_t = true, - long_help = "Whether to start a local SpacetimeDB server before running smoketests" + long = "python", + value_name = "PYTHON_PATH", + long_help = "Python interpreter to use for smoketests" )] - start_server: bool, + python: Option, + + /// List the tests that would be run, but don't run them + #[arg( + long = "list", + num_args(0..=1), + default_missing_value = "text", + value_parser = ["text", "json"] + )] + list: Option, + + // Args that influence test selection #[arg( long = "docker", value_name = "COMPOSE_FILE", @@ -70,23 +81,50 @@ enum CiCmd { long_help = "Use docker for smoketests, specifying a docker compose file. If no value is provided, docker-compose.yml is used by default. This cannot be combined with --start-server." )] docker: Option, + /// Ignore tests which require dotnet + #[arg(long = "skip-dotnet", default_value_t = false)] + skip_dotnet: bool, + /// Only run tests which match the given substring (can be specified multiple times) + #[arg(short = 'k', action = clap::ArgAction::Append)] + test_name_patterns: Vec, + /// Exclude tests matching these names/patterns + #[arg(short = 'x', default_value = "", num_args(0..))] + exclude: Vec, + /// Run against a remote server + #[arg(long = "remote-server")] + remote_server: Option, + /// Only run tests that require a local server + #[arg(long = "local-only", default_value_t = false)] + local_only: bool, + /// Use `spacetime login` for these tests (and disable tests that don't work with that) + #[arg(long = "spacetime-login", default_value_t = false)] + spacetime_login: bool, + /// Tests to run (positional); if omitted, run all + #[arg(value_name = "TEST")] + test: Vec, + + // Args that only influence test running + /// Show all stdout/stderr from the tests as they're running + #[arg(long = "show-all-output", default_value_t = false)] + show_all_output: bool, + /// Don't cargo build the CLI in the Python runner + #[arg(long = "no-build-cli", default_value_t = false)] + no_build_cli: bool, + /// Do not stream docker logs alongside test output + #[arg(long = "no-docker-logs", default_value_t = false)] + no_docker_logs: bool, + #[arg( + long = "start-server", + default_value_t = true, + long_help = "Whether to start a local SpacetimeDB server before running smoketests" + )] + start_server: bool, #[arg( long = "parallel", default_value_t = false, long_help = "Run smoketests in parallel batches grouped by test suite" )] parallel: bool, - #[arg( - long = "python", - value_name = "PYTHON_PATH", - long_help = "Python interpreter to use for smoketests" - )] - python: Option, - #[arg( - trailing_var_arg = true, - long_help = "Additional arguments to pass to the smoketests runner. These are usually set by the CI environment, such as `-- --docker`" - )] - args: Vec, }, /// Tests the update flow /// @@ -353,9 +391,19 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { start_server, docker, + test, + no_docker_logs, + skip_dotnet, + show_all_output, + test_name_patterns, + exclude, + no_build_cli, + list, + remote_server, + spacetime_login, + local_only, parallel, python, - args, }) => { let start_server = match (start_server, docker.as_ref()) { (start_server, Some(compose_file)) => { @@ -370,10 +418,48 @@ fn main() -> Result<()> { (true, None) => StartServer::Yes { random_port: parallel }, (false, None) => StartServer::No, }; - let mut args = args.to_vec(); + + let mut args: Vec = Vec::new(); + + // Options that map 1:1 to the Python smoketests CLI + if no_docker_logs { + args.push("--no-docker-logs".to_string()); + } + if skip_dotnet { + args.push("--skip-dotnet".to_string()); + } + if show_all_output { + args.push("--show-all-output".to_string()); + } + for pat in test_name_patterns { + args.push("-k".to_string()); + args.push(pat); + } + for ex in exclude { + args.push("-x".to_string()); + args.push(ex); + } + if no_build_cli { + args.push("--no-build-cli".to_string()); + } + if let Some(list_mode) = list { + args.push(format!("--list={list_mode}").to_string()); + } + if let Some(remote) = remote_server { + args.push("--remote-server".to_string()); + args.push(remote); + } + if spacetime_login { + args.push("--spacetime-login".to_string()); + } + if local_only { + args.push("--local-only".to_string()); + } + + // Preserve any additional raw arguments that the caller provided. + args.extend(extra_args.into_iter()); if let Some(compose_file) = docker.as_ref() { - // Note that we do not assume that the user wants to pass --docker to the tests. We leave them the power to - // run the server in docker while still retaining full control over what tests they want. + args.push("--docker".to_string()); args.push("--compose-file".to_string()); args.push(compose_file.to_string()); } @@ -405,10 +491,15 @@ fn main() -> Result<()> { args.push("--no-build-cli".into()); } + // For the non-parallel run we append the positional tests to the + // shared `args` set of flags. + let mut runner_args: Vec = args.clone(); + runner_args.extend(test.into_iter()); + if parallel { println!("Listing smoketests for parallel execution.."); - let mut list_args: Vec = args.to_vec(); + let mut list_args: Vec = args.clone(); list_args.push("--list=json".to_string()); let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); From 7fd39fdd6220ee189f95134f4b4f6e8ee066b0fe Mon Sep 17 00:00:00 2001 From: Kasama Date: Mon, 8 Dec 2025 15:25:18 -0300 Subject: [PATCH 066/160] add: bash macro for python3 check --- tools/ci/src/ci_docs.rs | 1 + tools/ci/src/main.rs | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs index f6beb2eb8ef..f09114e6787 100644 --- a/tools/ci/src/ci_docs.rs +++ b/tools/ci/src/ci_docs.rs @@ -2,6 +2,7 @@ use clap::{Command, CommandFactory}; use crate::Cli; +// TODO: use clap_markdown instead of this custom implementation in the future pub fn generate_cli_docs() -> String { let mut cli = Cli::command(); let usage = generate_markdown(&mut cli, 2); diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d4750da29df..01daa5b2e2d 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -170,10 +170,7 @@ fn main() -> Result<()> { Some(CiCmd::Smoketests { args }) => { // On some systems, there is no `python`, but there is `python3`. - let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") - .run() - .map(|s| s.status.success()) - .unwrap_or(false); + let py3_available = bash!("command -v python3 >/dev/null 2>&1").is_ok(); let python = if py3_available { "python3" } else { "python" }; bash!(&format!("{python} -m smoketests {}", args.join(" ")))?; } From 31d1503e6a911b2041f9adc24b160dcba75bac16 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 12:40:37 -0800 Subject: [PATCH 067/160] [bfops/parallel-smoketests]: refactoring --- tools/ci/src/main.rs | 474 +++++++++++++++++++++++++++---------------- 1 file changed, 299 insertions(+), 175 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 78c6d8c3c9c..d17c4ec814f 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -88,7 +88,7 @@ enum CiCmd { #[arg(short = 'k', action = clap::ArgAction::Append)] test_name_patterns: Vec, /// Exclude tests matching these names/patterns - #[arg(short = 'x', default_value = "", num_args(0..))] + #[arg(short = 'x', num_args(0..))] exclude: Vec, /// Run against a remote server #[arg(long = "remote-server")] @@ -226,13 +226,13 @@ fn find_free_port() -> Result { Ok(port) } -fn wait_until_http_ready(timeout: Duration) -> Result<()> { +fn wait_until_http_ready(timeout: Duration, server_url: &str) -> Result<()> { let deadline = Instant::now() + timeout; while Instant::now() < deadline { - match bash!("cargo run -p spacetimedb-cli -- server ping localhost") { + match bash!(&format!("cargo run -p spacetimedb-cli -- server ping {server_url}")) { Ok(_) => return Ok(()), - Err(e) => {} + Err(_) => {} } sleep(Duration::from_millis(500)); } @@ -252,6 +252,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) println!("Starting server.."); let env_string; let project; + let server_url; if random_port { let server_port = find_free_port()?; let pg_port = find_free_port()?; @@ -259,28 +260,34 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); project = format!("spacetimedb-smoketests-{server_port}"); args.push("--remote-server".into()); - args.push(format!("http://localhost:{server_port}")); + server_url = format!("http://localhost:{server_port}"); + args.push(server_url.clone()); } else { env_string = String::new(); project = "spacetimedb-smoketests".to_string(); + server_url = "http://localhost:3000".to_string(); }; let compose_str = compose_file.to_string_lossy(); bash!(&format!( "{env_string} docker compose -f {compose_str} --project-name {project} up -d" ))?; + wait_until_http_ready(Duration::from_secs(60), &server_url)?; ServerState::Docker { compose_file, project } } StartServer::Yes { random_port } => { // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port let arg_string; + let server_url; if random_port { let server_port = find_free_port()?; let pg_port = find_free_port()?; arg_string = format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port}"); args.push("--remote-server".into()); - args.push(format!("http://localhost:{server_port}")); + server_url = format!("http://localhost:{server_port}"); + args.push(server_url.clone()); } else { arg_string = "--pg-port 5432".into(); + server_url = "http://localhost:3000".to_string(); } println!("Starting server.."); let pid_str; @@ -296,17 +303,13 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) .read() .unwrap_or_default(); } else { + let run = format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!"); + println!("Running: {run}"); // TODO: Maybe we do this in a thread instead? Then it's easier to kill - pid_str = cmd!( - "bash", - "-lc", - &format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!") - ) - .read() - .unwrap_or_default(); + pid_str = cmd!("bash", "-lc", &run).read().unwrap_or_default(); } println!("Waiting for server to start.."); - wait_until_http_ready(Duration::from_secs(60))?; + wait_until_http_ready(Duration::from_secs(60), &server_url)?; ServerState::Yes { pid: pid_str .trim() @@ -346,6 +349,251 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) test_result } +fn server_start_config(start_server: bool, docker: Option, random_port: bool) -> StartServer { + match (start_server, docker.as_ref()) { + (start_server, Some(compose_file)) => { + if !start_server { + warn!("--docker implies --start-server=true"); + } + StartServer::Docker { + random_port, + compose_file: compose_file.into(), + } + } + (true, None) => StartServer::Yes { random_port }, + (false, None) => StartServer::No, + } +} + +fn common_args( + docker: Option, + skip_dotnet: bool, + test_name_patterns: Vec, + exclude: Vec, + local_only: bool, + spacetime_login: bool, + show_all_output: bool, + no_build_cli: bool, + no_docker_logs: bool, +) -> Vec { + let mut args: Vec = Vec::new(); + + if no_docker_logs { + args.push("--no-docker-logs".to_string()); + } + if skip_dotnet { + args.push("--skip-dotnet".to_string()); + } + if show_all_output { + args.push("--show-all-output".to_string()); + } + for pat in test_name_patterns { + args.push("-k".to_string()); + args.push(pat); + } + for ex in exclude { + args.push("-x".to_string()); + args.push(ex); + } + if no_build_cli { + args.push("--no-build-cli".to_string()); + } + if spacetime_login { + args.push("--spacetime-login".to_string()); + } + if local_only { + args.push("--local-only".to_string()); + } + + if let Some(compose_file) = docker.as_ref() { + args.push("--docker".to_string()); + args.push("--compose-file".to_string()); + args.push(compose_file.to_string()); + } + + args +} + +fn infer_python() -> String { + // TODO: does this work on windows? + let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") + .run() + .map(|s| s.status.success()) + .unwrap_or(false); + if py3_available { + "python3".to_string() + } else { + "python".to_string() + } +} + +fn run_smoketests_serial( + python: String, + list: Option, + docker: Option, + skip_dotnet: bool, + test_name_patterns: Vec, + exclude: Vec, + remote_server: Option, + local_only: bool, + spacetime_login: bool, + test: Vec, + show_all_output: bool, + no_build_cli: bool, + no_docker_logs: bool, + start_server: StartServer, +) -> Result<()> { + let mut args = Vec::new(); + if let Some(list_mode) = list { + args.push(format!("--list={list_mode}").to_string()); + } + if let Some(remote) = remote_server { + args.push("--remote-server".to_string()); + args.push(remote); + } + for test in test { + args.push(test.clone()); + } + // The python smoketests take -x X Y Z, which can be ambiguous with passing test names as args to run. + // So, we make sure the anonymous test name arg has been added _before_ the exclude args which are a part of common_args. + args.extend(common_args( + docker, + skip_dotnet, + test_name_patterns, + exclude, + local_only, + spacetime_login, + show_all_output, + no_build_cli, + no_docker_logs, + )); + run_smoketests_batch(start_server, &args, &python)?; + Ok(()) +} + +fn run_smoketests_parallel( + python: String, + list: Option, + docker: Option, + skip_dotnet: bool, + test_name_patterns: Vec, + exclude: Vec, + remote_server: Option, + local_only: bool, + spacetime_login: bool, + test: Vec, + show_all_output: bool, + no_build_cli: bool, + no_docker_logs: bool, + start_server: StartServer, +) -> Result<()> { + let args = common_args( + docker, + skip_dotnet, + test_name_patterns, + exclude, + local_only, + spacetime_login, + show_all_output, + no_build_cli, + no_docker_logs, + ); + + // TODO: Handle --local-only tests + if list.is_some() { + anyhow::bail!("--list is not supported in parallel mode"); + } + if remote_server.is_some() { + anyhow::bail!("--remote-server is not supported in parallel mode"); + } + + println!("Listing smoketests for parallel execution.."); + + let tests = { + let mut list_args: Vec = args.clone(); + list_args.push("--list=json".to_string()); + for test in test { + list_args.push(test.clone()); + } + let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); + + // TODO: do actually check the return code here. and make --list=json not return non-zero if there are errors. + let list_output = cmd!("bash", "-lc", list_cmdline) + .stderr_to_stdout() + .unchecked() + .read()?; + + let parsed: serde_json::Value = serde_json::from_str(&list_output)?; + let tests = parsed.get("tests").and_then(|v| v.as_array()).cloned().unwrap(); + let errors = parsed + .get("errors") + .and_then(|v| v.as_array()) + .cloned() + .unwrap_or_default(); + + if !errors.is_empty() { + println!("Errors while constructing smoketests:"); + for err in &errors { + let test_id = err.get("test_id").and_then(|v| v.as_str()).unwrap(); + let msg = err.get("error").and_then(|v| v.as_str()).unwrap(); + println!("{test_id}"); + println!("{msg}"); + } + // If there were errors constructing tests, treat this as a failure + // and do not run any batches. + anyhow::bail!("Errors encountered while constructing smoketests; aborting parallel run"); + } + + tests + }; + + let batches: HashSet = tests + .into_iter() + .map(|t| { + let name = t.as_str().unwrap(); + let parts = name.split('.').collect::>(); + parts[2].to_string() + }) + .collect(); + + // Run each batch in parallel threads. + let mut handles = Vec::new(); + for batch in batches { + let start_server_clone = start_server.clone(); + let python_clone = python.clone(); + let mut batch_args: Vec = Vec::new(); + // TODO: this doesn't work properly if the user passed multiple batches as input. + batch_args.push(batch.clone()); + batch_args.extend(args.iter().cloned()); + + handles.push(( + batch.clone(), + std::thread::spawn(move || { + println!("Running smoketests batch {batch}.."); + // TODO: capture output and print it only in contiguous blocks + run_smoketests_batch(start_server_clone, &batch_args, &python_clone) + }), + )); + } + + let mut failed_batches = vec![]; + for (batch, handle) in handles { + // If the thread panicked or the batch failed, treat it as a failure. + let result = handle + .join() + .unwrap_or_else(|_| Err(anyhow::anyhow!("smoketest batch thread panicked",))); + if result.is_err() { + failed_batches.push(batch); + } + } + + if !failed_batches.is_empty() { + anyhow::bail!("Smoketest batch(es) failed: {}", failed_batches.join(", ")); + } + + Ok(()) +} + fn main() -> Result<()> { let cli = Cli::parse(); @@ -397,7 +645,7 @@ fn main() -> Result<()> { show_all_output, test_name_patterns, exclude, - no_build_cli, + mut no_build_cli, list, remote_server, spacetime_login, @@ -405,82 +653,7 @@ fn main() -> Result<()> { parallel, python, }) => { - let start_server = match (start_server, docker.as_ref()) { - (start_server, Some(compose_file)) => { - if !start_server { - warn!("--docker implies --start-server=true"); - } - StartServer::Docker { - random_port: parallel, - compose_file: compose_file.into(), - } - } - (true, None) => StartServer::Yes { random_port: parallel }, - (false, None) => StartServer::No, - }; - - let mut args: Vec = Vec::new(); - - // Options that map 1:1 to the Python smoketests CLI - if no_docker_logs { - args.push("--no-docker-logs".to_string()); - } - if skip_dotnet { - args.push("--skip-dotnet".to_string()); - } - if show_all_output { - args.push("--show-all-output".to_string()); - } - for pat in test_name_patterns { - args.push("-k".to_string()); - args.push(pat); - } - for ex in exclude { - args.push("-x".to_string()); - args.push(ex); - } - if no_build_cli { - args.push("--no-build-cli".to_string()); - } - if let Some(list_mode) = list { - args.push(format!("--list={list_mode}").to_string()); - } - if let Some(remote) = remote_server { - args.push("--remote-server".to_string()); - args.push(remote); - } - if spacetime_login { - args.push("--spacetime-login".to_string()); - } - if local_only { - args.push("--local-only".to_string()); - } - - // Preserve any additional raw arguments that the caller provided. - args.extend(extra_args.into_iter()); - if let Some(compose_file) = docker.as_ref() { - args.push("--docker".to_string()); - args.push("--compose-file".to_string()); - args.push(compose_file.to_string()); - } - - let python = if let Some(p) = python { - p - } else { - // TODO: does this work on windows? - let py3_available = cmd!("bash", "-lc", "command -v python3 >/dev/null 2>&1") - .run() - .map(|s| s.status.success()) - .unwrap_or(false); - if py3_available { - "python3".to_string() - } else { - "python".to_string() - } - }; - - // TODO: Handle --local-only tests - + let start_server = server_start_config(start_server, docker.clone(), parallel); if matches!(start_server, StartServer::Yes { .. }) { println!("Building SpacetimeDB.."); @@ -488,95 +661,46 @@ fn main() -> Result<()> { // before the server is up. // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; - args.push("--no-build-cli".into()); + no_build_cli = true; } - // For the non-parallel run we append the positional tests to the - // shared `args` set of flags. - let mut runner_args: Vec = args.clone(); - runner_args.extend(test.into_iter()); + let python = python.unwrap_or(infer_python()); + // These are split into two separate functions, so that we can ensure all the args are considered in both cases. if parallel { - println!("Listing smoketests for parallel execution.."); - - let mut list_args: Vec = args.clone(); - list_args.push("--list=json".to_string()); - let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); - - // TODO: do actually check the return code here. and make --list=json not return non-zero if there are errors. - let list_output = cmd!("bash", "-lc", list_cmdline) - .stderr_to_stdout() - .unchecked() - .read()?; - - let parsed: serde_json::Value = serde_json::from_str(&list_output)?; - let tests = parsed.get("tests").and_then(|v| v.as_array()).cloned().unwrap(); - let errors = parsed - .get("errors") - .and_then(|v| v.as_array()) - .cloned() - .unwrap_or_default(); - - if !errors.is_empty() { - println!("Errors while constructing smoketests:"); - for err in &errors { - let test_id = err.get("test_id").and_then(|v| v.as_str()).unwrap(); - let msg = err.get("error").and_then(|v| v.as_str()).unwrap(); - println!("{test_id}"); - println!("{msg}"); - } - // If there were errors constructing tests, treat this as a failure - // and do not run any batches. - return Err(anyhow::anyhow!( - "Errors encountered while constructing smoketests; aborting parallel run" - )); - } - - let batches: HashSet = tests - .into_iter() - .map(|t| { - let name = t.as_str().unwrap(); - let parts = name.split('.').collect::>(); - parts[2].to_string() - }) - .collect(); - - // Run each batch in parallel threads. - let mut handles = Vec::new(); - for batch in batches { - let start_server_clone = start_server.clone(); - let python_clone = python.clone(); - let mut batch_args: Vec = Vec::new(); - // TODO: this doesn't work properly if the user passed multiple batches as input. - batch_args.push(batch.clone()); - batch_args.extend(args.iter().cloned()); - - handles.push(( - batch.clone(), - std::thread::spawn(move || { - println!("Running smoketests batch {batch}.."); - // TODO: capture output and print it only in contiguous blocks - run_smoketests_batch(start_server_clone, &batch_args, &python_clone) - }), - )); - } - - let mut failed_batches = vec![]; - for (batch, handle) in handles { - // If the thread panicked or the batch failed, treat it as a failure. - let result = handle - .join() - .unwrap_or_else(|_| Err(anyhow::anyhow!("smoketest batch thread panicked",))); - if result.is_err() { - failed_batches.push(batch); - } - } - - if !failed_batches.is_empty() { - anyhow::bail!("Smoketest batch(es) failed: {}", failed_batches.join(", ")); - } + run_smoketests_parallel( + python, + list, + docker, + skip_dotnet, + test_name_patterns, + exclude, + remote_server, + local_only, + spacetime_login, + test, + show_all_output, + no_build_cli, + no_docker_logs, + start_server, + )?; } else { - run_smoketests_batch(start_server, &args, &python)?; + run_smoketests_serial( + python, + list, + docker, + skip_dotnet, + test_name_patterns, + exclude, + remote_server, + local_only, + spacetime_login, + test, + show_all_output, + no_build_cli, + no_docker_logs, + start_server, + )?; } } From d234abc4d2ac54e64a2d802ffbbbbe0388246cb6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 13:02:38 -0800 Subject: [PATCH 068/160] [bfops/parallel-smoketests]: no more random_port, proper data dir, some parallel invocation works --- Cargo.lock | 1 + tools/ci/Cargo.toml | 1 + tools/ci/src/main.rs | 207 ++++++++++++++++++++++--------------------- 3 files changed, 110 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09db7759ff2..2143028c83c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,6 +875,7 @@ dependencies = [ "log", "regex", "serde_json", + "tempfile", ] [[package]] diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 19a40d0afe1..a9e755830cf 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -11,3 +11,4 @@ clap.workspace = true regex.workspace = true duct.workspace = true serde_json.workspace = true +tempfile.workspace = true diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d17c4ec814f..04c2491652e 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -9,6 +9,7 @@ use std::path::{Path, PathBuf}; use std::thread::sleep; use std::time::{Duration, Instant}; use std::{env, fs}; +use tempfile::TempDir; const README_PATH: &str = "tools/ci/README.md"; @@ -205,15 +206,8 @@ fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { #[derive(Debug, Clone)] pub enum StartServer { No, - Yes { random_port: bool }, - Docker { compose_file: PathBuf, random_port: bool }, -} - -#[derive(Debug, Clone)] -pub enum ServerState { - None, - Yes { pid: i32 }, - Docker { compose_file: PathBuf, project: String }, + Yes, + Docker { compose_file: PathBuf }, } fn find_free_port() -> Result { @@ -230,69 +224,72 @@ fn wait_until_http_ready(timeout: Duration, server_url: &str) -> Result<()> { let deadline = Instant::now() + timeout; while Instant::now() < deadline { - match bash!(&format!("cargo run -p spacetimedb-cli -- server ping {server_url}")) { - Ok(_) => return Ok(()), - Err(_) => {} + // Use duct::cmd directly so we can suppress output from the ping command. + let status = cmd( + "cargo", + &["run", "-p", "spacetimedb-cli", "--", "server", "ping", server_url], + ) + .stdout_null() + .stderr_null() + .unchecked() + .run(); + + if let Ok(status) = status { + if status.status.success() { + return Ok(()); + } } sleep(Duration::from_millis(500)); } anyhow::bail!("Timed out waiting for SpacetimeDB"); } -fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { - // TODO: If we set --remote-server, check that it's not already in there. - let mut args: Vec<_> = args.iter().cloned().collect(); +#[derive(Debug)] +pub enum ServerState { + None, + Yes { pid: i32, data_dir: TempDir }, + Docker { compose_file: PathBuf, project: String }, +} - let server_state = match server_mode { - StartServer::No => ServerState::None, - StartServer::Docker { - compose_file, - random_port, - } => { - println!("Starting server.."); - let env_string; - let project; - let server_url; - if random_port { +impl ServerState { + fn start(start_mode: StartServer, args: &mut Vec) -> Result { + match start_mode { + StartServer::No => Ok(Self::None), + StartServer::Docker { compose_file } => { + println!("Starting server.."); let server_port = find_free_port()?; let pg_port = find_free_port()?; let tracy_port = find_free_port()?; - env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); - project = format!("spacetimedb-smoketests-{server_port}"); + let env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); + let project = format!("spacetimedb-smoketests-{server_port}"); args.push("--remote-server".into()); - server_url = format!("http://localhost:{server_port}"); + let server_url = format!("http://localhost:{server_port}"); args.push(server_url.clone()); - } else { - env_string = String::new(); - project = "spacetimedb-smoketests".to_string(); - server_url = "http://localhost:3000".to_string(); - }; - let compose_str = compose_file.to_string_lossy(); - bash!(&format!( - "{env_string} docker compose -f {compose_str} --project-name {project} up -d" - ))?; - wait_until_http_ready(Duration::from_secs(60), &server_url)?; - ServerState::Docker { compose_file, project } - } - StartServer::Yes { random_port } => { - // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port - let arg_string; - let server_url; - if random_port { + let compose_str = compose_file.to_string_lossy(); + bash!(&format!( + "{env_string} docker compose -f {compose_str} --project-name {project} up -d" + ))?; + wait_until_http_ready(Duration::from_secs(60), &server_url)?; + Ok(ServerState::Docker { compose_file, project }) + } + StartServer::Yes => { + // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port + + // Create a temporary data directory for this server instance. + let data_dir = TempDir::new()?; + let data_dir_str = data_dir.path().to_string_lossy(); + let server_port = find_free_port()?; let pg_port = find_free_port()?; - arg_string = format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port}"); + let arg_string = + format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port} --data-dir {data_dir_str}"); args.push("--remote-server".into()); - server_url = format!("http://localhost:{server_port}"); + let server_url = format!("http://localhost:{server_port}"); args.push(server_url.clone()); - } else { - arg_string = "--pg-port 5432".into(); - server_url = "http://localhost:3000".to_string(); - } - println!("Starting server.."); - let pid_str; - if cfg!(target_os = "windows") { - pid_str = cmd!( + println!("Starting server.."); + let pid_str; + if cfg!(target_os = "windows") { + pid_str = cmd!( "powershell", "-NoProfile", "-Command", @@ -302,65 +299,78 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) ) .read() .unwrap_or_default(); - } else { - let run = format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!"); - println!("Running: {run}"); - // TODO: Maybe we do this in a thread instead? Then it's easier to kill - pid_str = cmd!("bash", "-lc", &run).read().unwrap_or_default(); + } else { + let run = + format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!"); + println!("Running: {run}"); + // TODO: Maybe we do this in a thread instead? Then it's easier to kill + pid_str = cmd!("bash", "-lc", &run).read().unwrap_or_default(); + } + println!("Waiting for server to start.."); + wait_until_http_ready(Duration::from_secs(60), &server_url)?; + Ok(ServerState::Yes { + pid: pid_str + .trim() + .parse::() + .expect("Failed to get PID of started process"), + data_dir, + }) } - println!("Waiting for server to start.."); - wait_until_http_ready(Duration::from_secs(60), &server_url)?; - ServerState::Yes { - pid: pid_str - .trim() - .parse::() - .expect("Failed to get PID of started process"), + } + } +} + +impl Drop for ServerState { + fn drop(&mut self) { + // TODO: Make an effort to run the wind-down beahvior if we ctrl-c the process as well + match self { + ServerState::None => {} + ServerState::Docker { compose_file, project } => { + println!("Shutting down server.."); + let compose_str = compose_file.to_string_lossy(); + let _ = bash!(&format!( + "docker compose -f {compose_str} --project-name {project} down" + )); + } + ServerState::Yes { pid, data_dir } => { + println!("Shutting down server.."); + if cfg!(target_os = "windows") { + let _ = bash!(&format!( + "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", + pid + )); + } else { + // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? + let _ = bash!(&format!("kill {}", pid)); + } + let _ = data_dir; } } - }; + } +} - println!("Running smoketests.."); - let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); +fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { + // TODO: If we set --remote-server, check that it's not already in there. + let mut args: Vec<_> = args.iter().cloned().collect(); - // TODO: Make an effort to run the wind-down behavior if we ctrl-C this process - // match server_state { - // ServerState::None => {} - // ServerState::Docker { compose_file, project } => { - // println!("Shutting down server.."); - // let compose_str = compose_file.to_string_lossy(); - // let _ = bash!(&format!( - // "docker compose -f {compose_str} --project-name {project} down" - // )); - // } - // ServerState::Yes { pid } => { - // println!("Shutting down server.."); - // if cfg!(target_os = "windows") { - // let _ = bash!(&format!( - // "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", - // pid - // )); - // } else { - // // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? - // let _ = bash!(&format!("kill {}", pid)); - // } - // } - // } + let _server = ServerState::start(server_mode, &mut args)?; + println!("Running smoketests: {}", args.join(" ")); + let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); test_result } -fn server_start_config(start_server: bool, docker: Option, random_port: bool) -> StartServer { +fn server_start_config(start_server: bool, docker: Option) -> StartServer { match (start_server, docker.as_ref()) { (start_server, Some(compose_file)) => { if !start_server { warn!("--docker implies --start-server=true"); } StartServer::Docker { - random_port, compose_file: compose_file.into(), } } - (true, None) => StartServer::Yes { random_port }, + (true, None) => StartServer::Yes, (false, None) => StartServer::No, } } @@ -562,7 +572,6 @@ fn run_smoketests_parallel( let start_server_clone = start_server.clone(); let python_clone = python.clone(); let mut batch_args: Vec = Vec::new(); - // TODO: this doesn't work properly if the user passed multiple batches as input. batch_args.push(batch.clone()); batch_args.extend(args.iter().cloned()); @@ -653,7 +662,7 @@ fn main() -> Result<()> { parallel, python, }) => { - let start_server = server_start_config(start_server, docker.clone(), parallel); + let start_server = server_start_config(start_server, docker.clone()); if matches!(start_server, StartServer::Yes { .. }) { println!("Building SpacetimeDB.."); From 6a6fafec5c0871c231d802a55cea98d3917e976a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 13:09:43 -0800 Subject: [PATCH 069/160] [bfops/parallel-smoketests]: todos --- smoketests/__main__.py | 1 + tools/ci/src/main.rs | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/smoketests/__main__.py b/smoketests/__main__.py index b4f2160352f..350854700a2 100644 --- a/smoketests/__main__.py +++ b/smoketests/__main__.py @@ -166,6 +166,7 @@ def _is_local_only(test_case): "errors": failed_tests, } print(json.dumps(output)) + exit(0) exit(1 if any_failed else 0) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 04c2491652e..ac451388afb 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -350,7 +350,6 @@ impl Drop for ServerState { } fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) -> Result<()> { - // TODO: If we set --remote-server, check that it's not already in there. let mut args: Vec<_> = args.iter().cloned().collect(); let _server = ServerState::start(server_mode, &mut args)?; @@ -509,14 +508,16 @@ fn run_smoketests_parallel( no_docker_logs, ); - // TODO: Handle --local-only tests if list.is_some() { - anyhow::bail!("--list is not supported in parallel mode"); + anyhow::bail!("--list does not make sense with --parallel"); } if remote_server.is_some() { + // This is just because we manually provide --remote-server later, so it requires some refactoring. anyhow::bail!("--remote-server is not supported in parallel mode"); } + // TODO: Handle --local-only tests + println!("Listing smoketests for parallel execution.."); let tests = { @@ -527,13 +528,14 @@ fn run_smoketests_parallel( } let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); - // TODO: do actually check the return code here. and make --list=json not return non-zero if there are errors. - let list_output = cmd!("bash", "-lc", list_cmdline) + let output = cmd!("bash", "-lc", list_cmdline) .stderr_to_stdout() - .unchecked() - .read()?; + .read() + .expect("Failed to list smoketests"); + + println!("Output: {output}"); - let parsed: serde_json::Value = serde_json::from_str(&list_output)?; + let parsed: serde_json::Value = serde_json::from_str(&output)?; let tests = parsed.get("tests").and_then(|v| v.as_array()).cloned().unwrap(); let errors = parsed .get("errors") From 4d0ae8617da20744f88473aee10f05ef819965de Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 13:27:53 -0800 Subject: [PATCH 070/160] [bfops/parallel-smoketests]: proper shutdown? --- tools/ci/src/main.rs | 87 +++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index ac451388afb..5c7e38da9c8 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -6,7 +6,7 @@ use serde_json; use std::collections::{HashMap, HashSet}; use std::net::TcpListener; use std::path::{Path, PathBuf}; -use std::thread::sleep; +use std::thread; use std::time::{Duration, Instant}; use std::{env, fs}; use tempfile::TempDir; @@ -239,16 +239,22 @@ fn wait_until_http_ready(timeout: Duration, server_url: &str) -> Result<()> { return Ok(()); } } - sleep(Duration::from_millis(500)); + thread::sleep(Duration::from_millis(500)); } anyhow::bail!("Timed out waiting for SpacetimeDB"); } -#[derive(Debug)] pub enum ServerState { None, - Yes { pid: i32, data_dir: TempDir }, - Docker { compose_file: PathBuf, project: String }, + Yes { + handle: thread::JoinHandle<()>, + data_dir: TempDir, + }, + Docker { + handle: thread::JoinHandle<()>, + compose_file: PathBuf, + project: String, + }, } impl ServerState { @@ -265,12 +271,22 @@ impl ServerState { args.push("--remote-server".into()); let server_url = format!("http://localhost:{server_port}"); args.push(server_url.clone()); - let compose_str = compose_file.to_string_lossy(); - bash!(&format!( - "{env_string} docker compose -f {compose_str} --project-name {project} up -d" - ))?; + let compose_str = compose_file.to_string_lossy().to_string(); + + let handle = thread::spawn({ + let project = project.clone(); + move || { + let _ = bash!(&format!( + "{env_string} docker compose -f {compose_str} --project-name {project} up" + )); + } + }); wait_until_http_ready(Duration::from_secs(60), &server_url)?; - Ok(ServerState::Docker { compose_file, project }) + Ok(ServerState::Docker { + handle, + compose_file, + project, + }) } StartServer::Yes => { // TODO: Make sure that this isn't brittle / multiple parallel batches don't grab the same port @@ -287,34 +303,12 @@ impl ServerState { let server_url = format!("http://localhost:{server_port}"); args.push(server_url.clone()); println!("Starting server.."); - let pid_str; - if cfg!(target_os = "windows") { - pid_str = cmd!( - "powershell", - "-NoProfile", - "-Command", - &format!( - "$p = Start-Process cargo -ArgumentList 'run -p spacetimedb-cli -- start {arg_string}' -PassThru; $p.Id" - ) - ) - .read() - .unwrap_or_default(); - } else { - let run = - format!("nohup cargo run -p spacetimedb-cli -- start {arg_string} >/dev/null 2>&1 & echo $!"); - println!("Running: {run}"); - // TODO: Maybe we do this in a thread instead? Then it's easier to kill - pid_str = cmd!("bash", "-lc", &run).read().unwrap_or_default(); - } + let handle = thread::spawn(move || { + let _ = bash!(&format!("cargo run -p spacetimedb-cli -- start {arg_string}")); + }); println!("Waiting for server to start.."); wait_until_http_ready(Duration::from_secs(60), &server_url)?; - Ok(ServerState::Yes { - pid: pid_str - .trim() - .parse::() - .expect("Failed to get PID of started process"), - data_dir, - }) + Ok(ServerState::Yes { handle, data_dir }) } } } @@ -322,27 +316,22 @@ impl ServerState { impl Drop for ServerState { fn drop(&mut self) { - // TODO: Make an effort to run the wind-down beahvior if we ctrl-c the process as well + // TODO: Consider doing a dance to have the server thread die, instead of just dying with this process. match self { ServerState::None => {} - ServerState::Docker { compose_file, project } => { + ServerState::Docker { + handle: _, + compose_file, + project, + } => { println!("Shutting down server.."); let compose_str = compose_file.to_string_lossy(); let _ = bash!(&format!( "docker compose -f {compose_str} --project-name {project} down" )); } - ServerState::Yes { pid, data_dir } => { - println!("Shutting down server.."); - if cfg!(target_os = "windows") { - let _ = bash!(&format!( - "powershell -NoProfile -Command \"Stop-Process -Id {} -Force -ErrorAction SilentlyContinue\"", - pid - )); - } else { - // TODO: I keep getting errors about the pid not existing.. but the servers seem to shut down? - let _ = bash!(&format!("kill {}", pid)); - } + ServerState::Yes { handle: _, data_dir } => { + println!("Shutting down server (temp data-dir will be dropped).."); let _ = data_dir; } } From 1725a70cc85e249f4f64e349b295c23f57c629e2 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 13:29:21 -0800 Subject: [PATCH 071/160] [bfops/parallel-smoketests]: todo --- tools/ci/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 5c7e38da9c8..9822c18ef35 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -659,7 +659,6 @@ fn main() -> Result<()> { // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests // before the server is up. - // TODO: The `cargo run` invocation still seems to rebuild a bunch? investigate.. maybe we infer the binary path from cargo metadata. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; no_build_cli = true; } From 38626c3d7d6b4384399d9d2d73711d8e19706a6b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 13:48:20 -0800 Subject: [PATCH 072/160] [bfops/parallel-smoketests]: review --- .github/workflows/ci.yml | 2 -- tools/ci/src/main.rs | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b547ee7e9c..4a2519261f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,8 +83,6 @@ jobs: if: runner.os == 'Windows' run: choco install psql -y --no-progress shell: powershell - - name: Build crates - run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update - name: Start Docker daemon if: runner.os == 'Linux' run: /usr/local/bin/start-docker.sh diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 9822c18ef35..07693f113e5 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -657,8 +657,7 @@ fn main() -> Result<()> { if matches!(start_server, StartServer::Yes { .. }) { println!("Building SpacetimeDB.."); - // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk starting the tests - // before the server is up. + // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk timing out waiting for the server to come up. bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; no_build_cli = true; } From 9d83149d357a6debeda0d7ba7a0ee3de1621d5bb Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 23:07:33 -0800 Subject: [PATCH 073/160] [bfops/parallel-smoketests]: try tweak --- .github/workflows/ci.yml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a2519261f9..663d56b561c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,13 +22,10 @@ jobs: name: Smoketests strategy: matrix: - runner: [spacetimedb-new-runner, windows-latest] + runner: [ubuntu-latest, windows-latest] include: - - runner: spacetimedb-new-runner + - runner: ubuntu-latest smoketest_args: --docker - container: - image: localhost:5000/spacetimedb-ci:latest - options: --privileged - runner: windows-latest smoketest_args: --no-build-cli container: null @@ -83,9 +80,8 @@ jobs: if: runner.os == 'Windows' run: choco install psql -y --no-progress shell: powershell - - name: Start Docker daemon - if: runner.os == 'Linux' - run: /usr/local/bin/start-docker.sh + - name: Build crates + run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update # the sdk-manifests on windows-latest are messed up, so we need to update them - name: Fix sdk-manifests From 828257378ea664e07e872dc4351f01a507c82de9 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 8 Dec 2025 23:07:43 -0800 Subject: [PATCH 074/160] [bfops/parallel-smoketests]: tweaks --- tools/ci/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 07693f113e5..1192d3baef7 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -259,6 +259,8 @@ pub enum ServerState { impl ServerState { fn start(start_mode: StartServer, args: &mut Vec) -> Result { + // TODO: Currently the server output leaks. We should be capturing it and only printing if the test fails. + match start_mode { StartServer::No => Ok(Self::None), StartServer::Docker { compose_file } => { @@ -277,7 +279,7 @@ impl ServerState { let project = project.clone(); move || { let _ = bash!(&format!( - "{env_string} docker compose -f {compose_str} --project-name {project} up" + "{env_string} docker compose -f {compose_str} --project-name {project} up --abort-on-container-exit" )); } }); From f97fcf0aca396cef78b5a40b6065dda789a4be26 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 09:46:27 -0800 Subject: [PATCH 075/160] [bfops/parallel-smoketests]: try diff runner --- .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 663d56b561c..eb7704ee70a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +22,9 @@ jobs: name: Smoketests strategy: matrix: - runner: [ubuntu-latest, windows-latest] + runner: [spacetimedb-runner, windows-latest] include: - - runner: ubuntu-latest + - runner: spacetimedb-runner smoketest_args: --docker - runner: windows-latest smoketest_args: --no-build-cli From a55e765c2d762d1b47fc3f6f6da2b6d5d95b3cff Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 09:58:36 -0800 Subject: [PATCH 076/160] [bfops/parallel-smoketests]: fail-fast: false --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb7704ee70a..5a7b51f93b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,7 @@ jobs: docker_smoketests: name: Smoketests strategy: + fail-fast: false matrix: runner: [spacetimedb-runner, windows-latest] include: From 8edcad1bf8b3e00872145ea43c7afdb4e50d450c Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:04:02 -0800 Subject: [PATCH 077/160] [bfops/parallel-smoketests]: skip println --- tools/ci/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 1192d3baef7..33c78b8ec47 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -524,8 +524,6 @@ fn run_smoketests_parallel( .read() .expect("Failed to list smoketests"); - println!("Output: {output}"); - let parsed: serde_json::Value = serde_json::from_str(&output)?; let tests = parsed.get("tests").and_then(|v| v.as_array()).cloned().unwrap(); let errors = parsed From 0a04e11e59f80eb7a6b5394519fefb1596dbf763 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:20:23 -0800 Subject: [PATCH 078/160] [bfops/parallel-smoketests]: try expanding docker address pool --- .github/workflows/ci.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a7b51f93b0..095a83210c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,10 +23,13 @@ jobs: strategy: fail-fast: false matrix: - runner: [spacetimedb-runner, windows-latest] + runner: [spacetimedb-new-runner, windows-latest] include: - - runner: spacetimedb-runner + - runner: spacetimedb-new-runner smoketest_args: --docker + container: + image: localhost:5000/spacetimedb-ci:latest + options: --privileged - runner: windows-latest smoketest_args: --no-build-cli container: null @@ -81,6 +84,18 @@ jobs: if: runner.os == 'Windows' run: choco install psql -y --no-progress shell: powershell + - name: Start Docker daemon + if: runner.os == 'Linux' + run: /usr/local/bin/start-docker.sh + - name: Expand Docker network pool + run: | + echo '{ + "default-address-pools": [ + { "base": "10.10.0.0/16", "size": 24 }, + { "base": "10.20.0.0/16", "size": 24 } + ] + }' | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker - name: Build crates run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update From 058aa7bda7dd4cceef100e5a51da2fd9f75c725d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:23:40 -0800 Subject: [PATCH 079/160] [bfops/parallel-smoketests]: debug --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 095a83210c4..96068efa188 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,13 +89,15 @@ jobs: run: /usr/local/bin/start-docker.sh - name: Expand Docker network pool run: | + echo 'current contents:' + cat /etc/docker/daemon.json echo '{ "default-address-pools": [ { "base": "10.10.0.0/16", "size": 24 }, { "base": "10.20.0.0/16", "size": 24 } ] }' | sudo tee /etc/docker/daemon.json - sudo systemctl restart docker + sudo service docker restart - name: Build crates run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update From 4cc3e4243bfddf83a1bc41acbd9d2dcc0cd9cc3b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:33:45 -0800 Subject: [PATCH 080/160] [bfops/parallel-smoketests]: fix --- .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 96068efa188..79362bb2617 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,9 +89,9 @@ jobs: run: /usr/local/bin/start-docker.sh - name: Expand Docker network pool run: | - echo 'current contents:' - cat /etc/docker/daemon.json + cat /etc/docker/daemon.json echo '{ + "storage-driver": "vfs", "default-address-pools": [ { "base": "10.10.0.0/16", "size": 24 }, { "base": "10.20.0.0/16", "size": 24 } From 3880fb75137fe3226d81b2d978b18cbe97f2f2c6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:35:52 -0800 Subject: [PATCH 081/160] [bfops/parallel-smoketests]: fix for windows --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79362bb2617..c08f9334353 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,8 +88,10 @@ jobs: if: runner.os == 'Linux' run: /usr/local/bin/start-docker.sh - name: Expand Docker network pool + if: runner.os == 'Linux' run: | - cat /etc/docker/daemon.json + echo 'current contents:' + cat /etc/docker/daemon.json echo '{ "storage-driver": "vfs", "default-address-pools": [ From a98eee4b8db46c961e342e3dca0ca2429cea4f08 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:36:30 -0800 Subject: [PATCH 082/160] [bfops/parallel-smoketests]: stop saving on fail --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c08f9334353..135e7914f70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: with: workspaces: ${{ github.workspace }} shared-key: spacetimedb - cache-on-failure: true + cache-on-failure: false cache-all-crates: true cache-workspace-crates: true From 77e2faa0f3ae6083ed3bd8ecf9f66f2b8ad2e2f3 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 10:44:47 -0800 Subject: [PATCH 083/160] [bfops/parallel-smoketests]: more debug --- .github/workflows/ci.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 135e7914f70..3921d1fea1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,14 +84,12 @@ jobs: if: runner.os == 'Windows' run: choco install psql -y --no-progress shell: powershell - - name: Start Docker daemon - if: runner.os == 'Linux' - run: /usr/local/bin/start-docker.sh - name: Expand Docker network pool if: runner.os == 'Linux' run: | echo 'current contents:' cat /etc/docker/daemon.json + echo 'New contents': echo '{ "storage-driver": "vfs", "default-address-pools": [ @@ -100,6 +98,13 @@ jobs: ] }' | sudo tee /etc/docker/daemon.json sudo service docker restart + - name: Start Docker daemon + if: runner.os == 'Linux' + run: /usr/local/bin/start-docker.sh + - name: Check docker info + if: runner.os == 'Linux' + run: | + docker info - name: Build crates run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update From 697a1cc0c7f77d5d5c8ac5d76762989a37d4b717 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 11:08:23 -0800 Subject: [PATCH 084/160] [bfops/parallel-smoketests]: actually initialize logger --- tools/ci/Cargo.toml | 1 + tools/ci/src/main.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index a9e755830cf..47eb5f5836c 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -12,3 +12,4 @@ regex.workspace = true duct.workspace = true serde_json.workspace = true tempfile.workspace = true +env_logger.workspace = true diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 33c78b8ec47..03db55a2606 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -595,6 +595,8 @@ fn run_smoketests_parallel( } fn main() -> Result<()> { + env_logger::init(); + let cli = Cli::parse(); // Remove all Cargo-provided env vars from the subcommand From 4ad39e2a6a4cc0460a73947ce68a201764434a54 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 11:18:42 -0800 Subject: [PATCH 085/160] [bfops/parallel-smoketests]: rust_log --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3921d1fea1e..f4f559e7c1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,6 +125,7 @@ jobs: - name: Run smoketests (Linux) if: runner.os == 'Linux' run: | + export RUST_LOG=debug # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} From 07c96670ae0ac266f20a731ce762d3bebebd3e6a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 11:18:51 -0800 Subject: [PATCH 086/160] [bfops/parallel-smoketests]: cleanup --- tools/ci/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 03db55a2606..560329732d2 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -346,8 +346,7 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) let _server = ServerState::start(server_mode, &mut args)?; println!("Running smoketests: {}", args.join(" ")); - let test_result = bash!(&format!("{python} -m smoketests {}", args.join(" "))); - test_result + bash!(&format!("{python} -m smoketests {}", args.join(" "))) } fn server_start_config(start_server: bool, docker: Option) -> StartServer { From 464bca90abbd137bc4f326ae8fa6252ecd061cf6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 11:22:39 -0800 Subject: [PATCH 087/160] [bfops/parallel-smoketests]: review/debug --- Cargo.lock | 1 + tools/ci/src/main.rs | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2143028c83c..49a45c3c3b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -872,6 +872,7 @@ dependencies = [ "chrono", "clap 4.5.50", "duct", + "env_logger 0.10.2", "log", "regex", "serde_json", diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 560329732d2..c2274e061e5 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -221,6 +221,7 @@ fn find_free_port() -> Result { } fn wait_until_http_ready(timeout: Duration, server_url: &str) -> Result<()> { + println!("Waiting for server to start: {server_url}.."); let deadline = Instant::now() + timeout; while Instant::now() < deadline { @@ -241,7 +242,7 @@ fn wait_until_http_ready(timeout: Duration, server_url: &str) -> Result<()> { } thread::sleep(Duration::from_millis(500)); } - anyhow::bail!("Timed out waiting for SpacetimeDB"); + anyhow::bail!("Timed out waiting for {server_url}"); } pub enum ServerState { @@ -308,7 +309,6 @@ impl ServerState { let handle = thread::spawn(move || { let _ = bash!(&format!("cargo run -p spacetimedb-cli -- start {arg_string}")); }); - println!("Waiting for server to start.."); wait_until_http_ready(Duration::from_secs(60), &server_url)?; Ok(ServerState::Yes { handle, data_dir }) } @@ -581,7 +581,8 @@ fn run_smoketests_parallel( let result = handle .join() .unwrap_or_else(|_| Err(anyhow::anyhow!("smoketest batch thread panicked",))); - if result.is_err() { + if let Err(e) = result { + println!("Smoketest batch {batch} failed: {e:?}"); failed_batches.push(batch); } } From 0ef3cf0cab6836ad2f04b3e1157370b38b4c5f48 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 11:25:55 -0800 Subject: [PATCH 088/160] [bfops/parallel-smoketests]: lnoger timeout --- tools/ci/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c2274e061e5..74f02411081 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -284,7 +284,7 @@ impl ServerState { )); } }); - wait_until_http_ready(Duration::from_secs(60), &server_url)?; + wait_until_http_ready(Duration::from_secs(300), &server_url)?; Ok(ServerState::Docker { handle, compose_file, @@ -309,7 +309,7 @@ impl ServerState { let handle = thread::spawn(move || { let _ = bash!(&format!("cargo run -p spacetimedb-cli -- start {arg_string}")); }); - wait_until_http_ready(Duration::from_secs(60), &server_url)?; + wait_until_http_ready(Duration::from_secs(300), &server_url)?; Ok(ServerState::Yes { handle, data_dir }) } } From 4070974b62daa953bf3eb1caa89aba0f9ef8bf80 Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:15:49 -0800 Subject: [PATCH 089/160] Apply suggestion from @bfops Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f539552c98d..cfd6b0cc15e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,7 @@ jobs: if: runner.os == 'Windows' - name: Install python deps run: python -m pip install -r smoketests/requirements.txt - - name: Run smoketests windows + - name: Run smoketests (windows) # Note: clear_database and replication only work in private run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams if: runner.os == 'Windows' From 3fb016d2a78868f7dad4e036a419c376d94ee237 Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Tue, 9 Dec 2025 14:15:59 -0800 Subject: [PATCH 090/160] Apply suggestion from @bfops Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfd6b0cc15e..4bb309a5136 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -115,7 +115,7 @@ jobs: # Note: clear_database and replication only work in private run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams if: runner.os == 'Windows' - - name: Run smoketests + - name: Run smoketests (Linux) # Note: clear_database and replication only work in private run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams if: runner.os != 'Windows' From 2b5eaf85747fc258d0db2a53eac60b146ef5e768 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Tue, 9 Dec 2025 14:33:22 -0800 Subject: [PATCH 091/160] [bfops/windows-ci]: windows --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f539552c98d..79141639f11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,14 +111,9 @@ jobs: if: runner.os == 'Windows' - name: Install python deps run: python -m pip install -r smoketests/requirements.txt - - name: Run smoketests windows - # Note: clear_database and replication only work in private - run: python -m smoketests ${{ matrix.smoketest_args }} -x clear_database replication teams - if: runner.os == 'Windows' - name: Run smoketests # Note: clear_database and replication only work in private run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams - if: runner.os != 'Windows' - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down From 94302a85369f904ec356d221535bcbb07e97bc30 Mon Sep 17 00:00:00 2001 From: Roberto Pommella Alegro Date: Wed, 10 Dec 2025 14:58:38 -0300 Subject: [PATCH 092/160] Update tools/ci/src/main.rs Co-authored-by: Zeke Foppa <196249+bfops@users.noreply.github.com> Signed-off-by: Roberto Pommella Alegro --- tools/ci/src/main.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 01daa5b2e2d..6bf1171a6e0 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -164,6 +164,12 @@ fn main() -> Result<()> { Some(CiCmd::WasmBindings) => { bash!("cargo test -p spacetimedb-codegen")?; + // Make sure the `Cargo.lock` file reflects the latest available versions. + // This is what users would end up with on a fresh module, so we want to + // catch any compile errors arising from a different transitive closure + // of dependencies than what is in the workspace lock file. + // + // For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714 bash!("cargo update")?; bash!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; } From cba51f5bf224d78df299e56778626450c84861e0 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 09:59:28 -0800 Subject: [PATCH 093/160] [bfops/parallel-smoketests]: try openssl fix --- .github/workflows/ci.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4f559e7c1c..877d6eca88a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -129,9 +129,14 @@ jobs: # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} + - name: Install OpenSSL via vcpkg + uses: microsoft/vcpkg-action@v2 + if: runner.os == 'Windows' - name: Run smoketests (Windows) if: runner.os == 'Windows' - run: cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} + run: | + export OPENSSL_NO_VENDOR=1 + cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} test: name: Test Suite From 5005f6fff55fbafdea03b188405c8da2d154595e Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:09:03 -0800 Subject: [PATCH 094/160] [bfops/parallel-smoketests]: windows CI tweak --- .github/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 877d6eca88a..ed70777d291 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,10 +130,17 @@ jobs: rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} - name: Install OpenSSL via vcpkg - uses: microsoft/vcpkg-action@v2 + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: 'latest' if: runner.os == 'Windows' - name: Run smoketests (Windows) if: runner.os == 'Windows' + env: + OPENSSL_NO_VENDOR: 1 + VCPKG_ROOT: ${{ env.VCPKG_ROOT }} + # Required so openssl-sys finds vcpkg libs + OPENSSL_DIR: ${{ env.VCPKG_ROOT }}/installed/x64-windows run: | export OPENSSL_NO_VENDOR=1 cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} From af13b0c2d6da359d158fc424dfd5dea5e0f24505 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:14:31 -0800 Subject: [PATCH 095/160] [bfops/parallel-smoketests]: [REVERT] strip down CI --- .github/workflows/ci.yml | 598 --------------------------------------- 1 file changed, 598 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36cbecb70b4..40918664f1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -146,601 +146,3 @@ jobs: export OPENSSL_NO_VENDOR=1 cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} - test: - name: Test Suite - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - steps: - - name: Find Git ref - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - 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: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the smoketests job save the cache since it builds the most things - save-if: false - - - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: pnpm/action-setup@v4 - with: - run_install: true - - - name: Build typescript module sdk - working-directory: crates/bindings-typescript - run: pnpm build - - - name: Run tests - run: cargo ci test - - lints: - name: Lints - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - uses: dsherret/rust-toolchain-file@v1 - - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the smoketests job save the cache since it builds the most things - save-if: false - - - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Run ci lint - run: cargo ci lint - - wasm_bindings: - name: Build and test wasm bindings - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - steps: - - uses: actions/checkout@v3 - - - uses: dsherret/rust-toolchain-file@v1 - - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the smoketests job save the cache since it builds the most things - save-if: false - - - name: Run bindgen tests - run: cargo ci wasm-bindings - - publish_checks: - name: Check that packages are publishable - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - permissions: read-all - steps: - - uses: actions/checkout@v3 - - name: Set up Python env - run: | - test -d venv || python3 -m venv venv - venv/bin/pip3 install argparse toml - - name: Run checks - run: | - set -ueo pipefail - FAILED=0 - ROOTS=(spacetimedb spacetimedb-sdk) - CRATES=$(venv/bin/python3 tools/find-publish-list.py --recursive --directories --quiet "${ROOTS[@]}") - for crate_dir in $CRATES; do - if ! venv/bin/python3 tools/crate-publish-checks.py "${crate_dir}"; then - FAILED=$(( $FAILED + 1 )) - fi - done - if [ $FAILED -gt 0 ]; then - exit 1 - fi - - update: - name: Test spacetimedb-update flow (${{ matrix.target }}) - permissions: read-all - strategy: - matrix: - include: - - runner: spacetimedb-new-runner - target: x86_64-unknown-linux-gnu - container: - image: localhost:5000/spacetimedb-ci:latest - options: --privileged - - { target: aarch64-unknown-linux-gnu, runner: arm-runner } - - { target: aarch64-apple-darwin, runner: macos-latest } - - { target: x86_64-pc-windows-msvc, runner: windows-latest } - runs-on: ${{ matrix.runner }} - container: ${{ matrix.container }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Rust - uses: dsherret/rust-toolchain-file@v1 - - - name: Install rust target - run: rustup target add ${{ matrix.target }} - - - name: Install packages - if: ${{ matrix.runner == 'arm-runner' }} - shell: bash - run: sudo apt install -y libssl-dev - - - name: Build spacetimedb-update - run: cargo build --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update - if: runner.os == 'Windows' - - - name: Run self-install - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - shell: bash - run: | - ROOT_DIR="$(mktemp -d)" - # NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. - # My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it - # happens very frequently on the `macos-runner`, but we haven't seen it on any others). - cargo run --features github-token-auth --target ${{ matrix.target }} -p spacetimedb-update -- self-install --root-dir="${ROOT_DIR}" --yes - "${ROOT_DIR}"/spacetime --root-dir="${ROOT_DIR}" help - if: runner.os == 'Windows' - - - name: Test spacetimedb-update - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: cargo ci update-flow --target=${{ matrix.target }} --github-token-auth - if: runner.os != 'Windows' - - unreal_engine_tests: - name: Unreal Engine Tests - # This can't go on e.g. ubuntu-latest because that runner runs out of disk space. ChatGPT suggested that the general solution tends to be to use - # a custom runner. - runs-on: spacetimedb-new-runner - # Disable the tests because they are very flaky at the moment. - # TODO: Remove this line and re-enable the `if` line just below here. - if: false - # Skip if this is an external contribution. GitHub secrets will be empty, so the step would fail anyway. - # if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }} - container: - image: ghcr.io/epicgames/unreal-engine:dev-5.6 - credentials: - # Note(bfops): I don't think that `github.actor` needs to match the user that the token is for, because I'm using a token for my account and - # it seems to be totally happy. - # However, the token needs to be for a user that has access to the EpicGames org (see - # https://dev.epicgames.com/documentation/en-us/unreal-engine/downloading-source-code-in-unreal-engine?application_version=5.6) - username: ${{ github.actor }} - password: ${{ secrets.GHCR_TOKEN }} - # Run as root because otherwise we get permission denied for various directories inside the container. I tried doing dances to allow it to run - # without this (reassigning env vars and stuff), but was unable to get it to work and it felt like an uphill battle. - options: --user 0:0 - steps: - # Uncomment this before merging so that it will run properly if run manually through the GH actions flow. It was playing weird with rolled back - # commits though. - # - 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: Run Unreal Engine tests - working-directory: sdks/unreal - env: - UE_ROOT_PATH: /home/ue4/UnrealEngine - run: | - - apt-get update - apt-get install -y acl curl ca-certificates - - REPO="$GITHUB_WORKSPACE" - # Let ue4 read/write the workspace & tool caches without changing ownership - for p in "$REPO" "${RUNNER_TEMP:-/__t}" "${RUNNER_TOOL_CACHE:-/__t}"; do - [ -d "$p" ] && setfacl -R -m u:ue4:rwX -m d:u:ue4:rwX "$p" || true - done - - # Rust tool caches live under the runner tool cache so they persist - export CARGO_HOME="${RUNNER_TOOL_CACHE:-/__t}/cargo" - export RUSTUP_HOME="${RUNNER_TOOL_CACHE:-/__t}/rustup" - mkdir -p "$CARGO_HOME" "$RUSTUP_HOME" - chown -R ue4:ue4 "$CARGO_HOME" "$RUSTUP_HOME" - - # Make sure the UE build script is executable (and parents traversable) - UE_DIR="${UE_ROOT_PATH:-/home/ue4/UnrealEngine}" - chmod a+rx "$UE_DIR" "$UE_DIR/Engine" "$UE_DIR/Engine/Build" "$UE_DIR/Engine/Build/BatchFiles/Linux" || true - chmod a+rx "$UE_DIR/Engine/Build/BatchFiles/Linux/Build.sh" || true - - # Run the build & tests as ue4 (who owns the UE tree) - sudo -E -H -u ue4 env \ - HOME=/home/ue4 \ - XDG_CONFIG_HOME=/home/ue4/.config \ - CARGO_HOME="$CARGO_HOME" \ - RUSTUP_HOME="$RUSTUP_HOME" \ - PATH="$CARGO_HOME/bin:$PATH" \ - bash -lc ' - set -euxo pipefail - # Install rustup for ue4 if needed (uses the shared caches) - if ! command -v cargo >/dev/null 2>&1; then - curl -sSf https://sh.rustup.rs | sh -s -- -y - fi - rustup show >/dev/null - git config --global --add safe.directory "$GITHUB_WORKSPACE" || true - - cd "$GITHUB_WORKSPACE/sdks/unreal" - cargo --version - cargo test -- --test-threads=1 - ' - - ci_command_docs: - name: Check CI command docs - runs-on: ubuntu-latest - 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: Check for docs change - run: cargo ci self-docs --check - - cli_docs: - name: Check CLI docs - permissions: read-all - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - 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 }} - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: pnpm/action-setup@v4 - with: - run_install: true - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v4 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - uses: dsherret/rust-toolchain-file@v1 - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the smoketests job save the cache since it builds the most things - save-if: false - - - name: Check for docs change - run: | - cargo ci cli-docs - - unity-testsuite: - # Skip if this is an external contribution. - # The license secrets will be empty, so the step would fail anyway. - if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }} - permissions: - contents: read - checks: write - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - --cgroupns=host - timeout-minutes: 30 - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - steps: - - name: Checkout repository - id: checkout-stdb - uses: actions/checkout@v4 - - # Run cheap .NET tests first. If those fail, no need to run expensive Unity tests. - - - name: Setup dotnet - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Override NuGet packages - run: | - dotnet pack crates/bindings-csharp/BSATN.Runtime - dotnet pack crates/bindings-csharp/Runtime - - # Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository - # to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if - # available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages. - # This means that (if version numbers match) we will test the local versions of the C# packages, even - # if they're not pushed to NuGet. - # See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file. - cd sdks/csharp - ./tools~/write-nuget-config.sh ../.. - - # Now, setup the Unity tests. - - name: Patch spacetimedb dependency in Cargo.toml - working-directory: demo/Blackholio/server-rust - run: | - sed -i "s|spacetimedb *=.*|spacetimedb = \{ path = \"../../../crates/bindings\" \}|" Cargo.toml - cat Cargo.toml - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the main CI job save the cache since it builds the most things - save-if: false - - - name: Install SpacetimeDB CLI from the local checkout - run: | - cargo install --force --path crates/cli --locked --message-format=short - cargo install --force --path crates/standalone --locked --message-format=short - # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules). - ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime - - - name: Generate client bindings - working-directory: demo/Blackholio/server-rust - run: bash ./generate.sh -y - - - name: Check for changes - run: | - tools/check-diff.sh demo/Blackholio/client-unity/Assets/Scripts/autogen || { - echo 'Error: Bindings are dirty. Please run `demo/Blackholio/server-rust/generate.sh`.' - exit 1 - } - - - name: Check Unity meta files - uses: DeNA/unity-meta-check@v3 - with: - enable_pr_comment: ${{ github.event_name == 'pull_request' }} - target_path: sdks/csharp - env: - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - - - name: Start SpacetimeDB - run: | - spacetime start & - disown - - - name: Publish unity-tests module to SpacetimeDB - working-directory: demo/Blackholio/server-rust - run: | - spacetime logout && spacetime login --server-issued-login local - bash ./publish.sh - - - name: Patch com.clockworklabs.spacetimedbsdk dependency in manifest.json - working-directory: demo/Blackholio/client-unity/Packages - run: | - # Replace the com.clockworklabs.spacetimedbsdk dependency with the current branch. - # Note: Pointing to a local directory does not work, because our earlier steps nuke our meta files, which then causes Unity to not properly respect the DLLs (e.g. - # codegen does not work properly). - yq e -i '.dependencies["com.clockworklabs.spacetimedbsdk"] = "https://github.com/clockworklabs/SpacetimeDB.git?path=sdks/csharp#${{ github.head_ref }}"' manifest.json - cat manifest.json - - - uses: actions/cache@v3 - with: - path: demo/Blackholio/client-unity/Library - key: Unity-${{ github.head_ref }} - restore-keys: Unity- - - # We need this to support "Docker in Docker" - - name: Start Docker daemon - run: /usr/local/bin/start-docker.sh - - name: Run Unity tests - uses: game-ci/unity-test-runner@v4 - with: - unityVersion: 2022.3.32f1 # Adjust Unity version to a valid tag - projectPath: demo/Blackholio/client-unity # Path to the Unity project subdirectory - githubToken: ${{ secrets.GITHUB_TOKEN }} - testMode: playmode - useHostNetwork: true - artifactsPath: '' - env: - UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} - UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} - UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }} - - csharp-testsuite: - runs-on: spacetimedb-new-runner - container: - image: localhost:5000/spacetimedb-ci:latest - options: >- - --privileged - --cgroupns=host - timeout-minutes: 30 - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - steps: - - name: Checkout repository - id: checkout-stdb - uses: actions/checkout@v4 - - # Run cheap .NET tests first. If those fail, no need to run expensive Unity tests. - - - name: Setup dotnet - uses: actions/setup-dotnet@v3 - with: - global-json-file: global.json - - - name: Override NuGet packages - run: | - dotnet pack crates/bindings-csharp/BSATN.Runtime - dotnet pack crates/bindings-csharp/Runtime - - # Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository - # to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if - # available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages. - # This means that (if version numbers match) we will test the local versions of the C# packages, even - # if they're not pushed to NuGet. - # See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file. - cd sdks/csharp - ./tools~/write-nuget-config.sh ../.. - - - name: Run .NET tests - working-directory: sdks/csharp - run: dotnet test -warnaserror - - - name: Verify C# formatting - working-directory: sdks/csharp - run: dotnet format --no-restore --verify-no-changes SpacetimeDB.ClientSDK.sln - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - - name: Cache Rust dependencies - uses: Swatinem/rust-cache@v2 - with: - workspaces: ${{ github.workspace }} - shared-key: spacetimedb - # Let the main CI job save the cache since it builds the most things - save-if: false - - - name: Install SpacetimeDB CLI from the local checkout - run: | - cargo install --force --path crates/cli --locked --message-format=short - cargo install --force --path crates/standalone --locked --message-format=short - # Add a handy alias using the old binary name, so that we don't have to rewrite all scripts (incl. in submodules). - ln -sf $CARGO_HOME/bin/spacetimedb-cli $CARGO_HOME/bin/spacetime - - - name: Check quickstart-chat bindings are up to date - working-directory: sdks/csharp - run: | - bash tools~/gen-quickstart.sh - "${GITHUB_WORKSPACE}"/tools/check-diff.sh examples~/quickstart-chat || { - echo 'Error: quickstart-chat bindings have changed. Please run `sdks/csharp/tools~/gen-quickstart.sh`.' - exit 1 - } - - - name: Check client-api bindings are up to date - working-directory: sdks/csharp - run: | - bash tools~/gen-client-api.sh - "${GITHUB_WORKSPACE}"/tools/check-diff.sh src/SpacetimeDB/ClientApi || { - echo 'Error: Client API bindings are dirty. Please run `sdks/csharp/tools~/gen-client-api.sh`.' - exit 1 - } - - - name: Start SpacetimeDB - run: | - spacetime start & - disown - - - name: Run regression tests - run: | - bash sdks/csharp/tools~/run-regression-tests.sh - tools/check-diff.sh sdks/csharp/examples~/regression-tests || { - echo 'Error: Bindings are dirty. Please run `sdks/csharp/tools~/gen-regression-tests.sh`.' - exit 1 - } From 61caceea9a7d554189147bc871d287275b66c11a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:16:24 -0800 Subject: [PATCH 096/160] [bfops/parallel-smoketests]: strip down CI for windows --- .github/workflows/ci.yml | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40918664f1e..fbe16e16dda 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,24 +67,6 @@ jobs: cache-all-crates: true cache-workspace-crates: true - - 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: pnpm/action-setup@v4 - with: - run_install: true - - - name: Install psql (Windows) - if: runner.os == 'Windows' - run: choco install psql -y --no-progress - shell: powershell - name: Expand Docker network pool if: runner.os == 'Linux' run: | From 624089466f6654814e4f2756591475b5f5063db7 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:20:18 -0800 Subject: [PATCH 097/160] [bfops/parallel-smoketests]: move --- .github/workflows/ci.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbe16e16dda..5fd9f7a23f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,9 +88,6 @@ jobs: if: runner.os == 'Linux' run: | docker info - - name: Build crates - run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update - # the sdk-manifests on windows-latest are messed up, so we need to update them - name: Fix sdk-manifests if: runner.os == 'Windows' @@ -109,6 +106,8 @@ jobs: if: runner.os == 'Linux' run: | export RUST_LOG=debug + # Pre-build the crates because this docker compose file uses whatever's pre-built + cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} From 669db6bd4cec42972016953a9297d39e3d12a3b4 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:21:00 -0800 Subject: [PATCH 098/160] [bfops/parallel-smoketests]: cache-on-failure --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5fd9f7a23f0..ca7d08eb8da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: with: workspaces: ${{ github.workspace }} shared-key: spacetimedb - cache-on-failure: false + cache-on-failure: true cache-all-crates: true cache-workspace-crates: true From 4e969f7212c5d0433d2795b1a1cbcf390394d11c Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:28:18 -0800 Subject: [PATCH 099/160] [bfops/parallel-smoketests]: fix --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca7d08eb8da..6cb24ecbd73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -114,7 +114,7 @@ jobs: - name: Install OpenSSL via vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: 'latest' + runVcpkgInstall: true if: runner.os == 'Windows' - name: Run smoketests (Windows) if: runner.os == 'Windows' From bf714d36555308e0113261b069f944d9f8210cd6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:44:23 -0800 Subject: [PATCH 100/160] [bfops/parallel-smoketests]: try not-bash --- .github/workflows/ci.yml | 13 +------------ tools/ci/src/main.rs | 30 +++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cb24ecbd73..ddf3b64a9ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,19 +111,8 @@ jobs: # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} - - name: Install OpenSSL via vcpkg - uses: lukka/run-vcpkg@v11 - with: - runVcpkgInstall: true if: runner.os == 'Windows' - name: Run smoketests (Windows) if: runner.os == 'Windows' - env: - OPENSSL_NO_VENDOR: 1 - VCPKG_ROOT: ${{ env.VCPKG_ROOT }} - # Required so openssl-sys finds vcpkg libs - OPENSSL_DIR: ${{ env.VCPKG_ROOT }}/installed/x64-windows - run: | - export OPENSSL_NO_VENDOR=1 - cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} + run: cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 3ffde399425..991b41849da 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -296,18 +296,30 @@ impl ServerState { // Create a temporary data directory for this server instance. let data_dir = TempDir::new()?; - let data_dir_str = data_dir.path().to_string_lossy(); let server_port = find_free_port()?; let pg_port = find_free_port()?; - let arg_string = - format!("--listen-addr 0.0.0.0:{server_port} --pg-port {pg_port} --data-dir {data_dir_str}"); args.push("--remote-server".into()); let server_url = format!("http://localhost:{server_port}"); args.push(server_url.clone()); println!("Starting server.."); + let data_dir_str = data_dir.path().to_string_lossy().to_string(); let handle = thread::spawn(move || { - let _ = bash!(&format!("cargo run -p spacetimedb-cli -- start {arg_string}")); + let _ = cmd!( + "cargo", + "run", + "-p", + "spacetimedb-cli", + "--", + "start", + "--listen-addr", + "0.0.0.0:{server_port}", + "--pg-port", + pg_port.to_string(), + "--data-dir", + data_dir_str, + ) + .read(); }); wait_until_http_ready(Duration::from_secs(300), &server_url)?; Ok(ServerState::Yes { handle, data_dir }) @@ -663,7 +675,15 @@ fn main() -> Result<()> { println!("Building SpacetimeDB.."); // Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk timing out waiting for the server to come up. - bash!("cargo build -p spacetimedb-cli -p spacetimedb-standalone")?; + cmd!( + "cargo", + "build", + "-p", + "spacetimedb-cli", + "-p", + "spacetimedb-standalone" + ) + .run()?; no_build_cli = true; } From 9f1e81acf7406e5a23cf6651de1c79c438fa132b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:46:32 -0800 Subject: [PATCH 101/160] [bfops/parallel-smoketests]: fix --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddf3b64a9ba..8bd41f03dee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,7 +111,6 @@ jobs: # Our .dockerignore omits `target`, which our CI Dockerfile needs. rm .dockerignore cargo ci smoketests --docker .github/docker-compose.yml --parallel -- ${SMOKETEST_ARGS} - if: runner.os == 'Windows' - name: Run smoketests (Windows) if: runner.os == 'Windows' run: cargo ci smoketests --parallel -- ${SMOKETEST_ARGS} From bf0cdb625a68bfbe5b1a56687533bae94e666e5d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 10:51:58 -0800 Subject: [PATCH 102/160] [bfops/parallel-smoketests]: [REVERT] slim down more --- .github/workflows/ci.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bd41f03dee..fcd2484ea41 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,20 +88,9 @@ jobs: if: runner.os == 'Linux' run: | docker info - # the sdk-manifests on windows-latest are messed up, so we need to update them - - name: Fix sdk-manifests - if: runner.os == 'Windows' - working-directory: modules - # Powershell doesn't early-exit properly from a multi-line command if one fails - shell: bash - run: | - dotnet workload config --update-mode manifests - dotnet workload update - uses: actions/setup-python@v5 with: { python-version: '3.12' } if: runner.os == 'Windows' - - name: Install python deps - run: python -m pip install -r smoketests/requirements.txt - name: Run smoketests (Linux) if: runner.os == 'Linux' run: | From 9958148f99939585149cb0476a3b39538ca0ad51 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 12:23:41 -0800 Subject: [PATCH 103/160] [bfops/parallel-smoketests]: remove bash! --- tools/ci/src/main.rs | 208 +++++++++++++++++++++++++++---------------- 1 file changed, 130 insertions(+), 78 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 991b41849da..84f53397e24 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -163,15 +163,6 @@ enum CiCmd { }, } -macro_rules! bash { - ($cmdline:expr) => { - run_bash($cmdline, &Vec::new()) - }; - ($cmdline:expr, $envs:expr) => { - run_bash($cmdline, $envs) - }; -} - fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { let subcmds = Cli::command() .get_subcommands() @@ -184,25 +175,11 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { continue; } log::info!("executing cargo ci {subcmd}"); - bash!(&format!("cargo ci {subcmd}"))?; + cmd!("cargo", "ci", &subcmd).run()?; } Ok(()) } - -fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { - let mut env = env::vars().collect::>(); - env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); - log::debug!("$ {cmdline}"); - let status = cmd!("bash", "-lc", cmdline).full_env(env).run()?; - if !status.status.success() { - let e = anyhow::anyhow!("command failed: {cmdline}"); - log::error!("{e}"); - return Err(e); - } - Ok(()) -} - #[derive(Debug, Clone)] pub enum StartServer { No, @@ -269,7 +246,6 @@ impl ServerState { let server_port = find_free_port()?; let pg_port = find_free_port()?; let tracy_port = find_free_port()?; - let env_string = format!("STDB_PORT={server_port} STDB_PG_PORT={pg_port} STDB_TRACY_PORT={tracy_port}"); let project = format!("spacetimedb-smoketests-{server_port}"); args.push("--remote-server".into()); let server_url = format!("http://localhost:{server_port}"); @@ -279,9 +255,20 @@ impl ServerState { let handle = thread::spawn({ let project = project.clone(); move || { - let _ = bash!(&format!( - "{env_string} docker compose -f {compose_str} --project-name {project} up --abort-on-container-exit" - )); + let _ = cmd!( + "docker", + "compose", + "-f", + &compose_str, + "--project-name", + &project, + "up", + "--abort-on-container-exit", + ) + .env("STDB_PORT", server_port.to_string()) + .env("STDB_PG_PORT", pg_port.to_string()) + .env("STDB_TRACY_PORT", tracy_port.to_string()) + .run(); } }); wait_until_http_ready(Duration::from_secs(300), &server_url)?; @@ -313,7 +300,7 @@ impl ServerState { "--", "start", "--listen-addr", - "0.0.0.0:{server_port}", + &format!("0.0.0.0:{server_port}"), "--pg-port", pg_port.to_string(), "--data-dir", @@ -339,10 +326,17 @@ impl Drop for ServerState { project, } => { println!("Shutting down server.."); - let compose_str = compose_file.to_string_lossy(); - let _ = bash!(&format!( - "docker compose -f {compose_str} --project-name {project} down" - )); + let compose_str = compose_file.to_string_lossy().to_string(); + let _ = cmd!( + "docker", + "compose", + "-f", + &compose_str, + "--project-name", + project, + "down", + ) + .run(); } ServerState::Yes { handle: _, data_dir } => { println!("Shutting down server (temp data-dir will be dropped).."); @@ -358,7 +352,8 @@ fn run_smoketests_batch(server_mode: StartServer, args: &[String], python: &str) let _server = ServerState::start(server_mode, &mut args)?; println!("Running smoketests: {}", args.join(" ")); - bash!(&format!("{python} -m smoketests {}", args.join(" "))) + cmd(python, "-m", "smoketests", args).run()?; + Ok(()) } fn server_start_config(start_server: bool, docker: Option) -> StartServer { @@ -426,8 +421,7 @@ fn common_args( } fn infer_python() -> String { - // TODO: does this work on windows? - let py3_available = bash!("command -v python3 >/dev/null 2>&1").is_ok(); + let py3_available = cmd!("python3", "--version").run().is_ok(); if py3_available { "python3".to_string() } else { @@ -525,9 +519,8 @@ fn run_smoketests_parallel( for test in test { list_args.push(test.clone()); } - let list_cmdline = format!("{python} -m smoketests {}", list_args.join(" ")); - let output = cmd!("bash", "-lc", list_cmdline) + let output = cmd(&python, "-m", "smoketests", &list_args) .stderr_to_stdout() .read() .expect("Failed to list smoketests"); @@ -617,40 +610,84 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { - bash!("cargo test --all -- --skip unreal")?; + cmd!("cargo", "test", "--all", "--", "--skip", "unreal").run()?; // The fallocate tests have been flakely when running in parallel - bash!("cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1")?; - bash!("bash tools/check-diff.sh")?; - bash!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; - bash!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; + cmd!( + "cargo", + "test", + "-p", + "spacetimedb-durability", + "--features", + "fallocate", + "--", + "--test-threads=1", + ) + .run()?; + cmd!("bash", "tools/check-diff.sh").run()?; + cmd!( + "cargo", + "run", + "-p", + "spacetimedb-codegen", + "--example", + "regen-csharp-moduledef", + ) + .run()?; + cmd!("bash", "tools/check-diff.sh", "crates/bindings-csharp").run()?; + cmd!("dotnet", "test", "-warnaserror") + .dir("crates/bindings-csharp") + .run()?; } Some(CiCmd::Lint) => { - bash!("cargo fmt --all -- --check")?; - bash!("cargo clippy --all --tests --benches -- -D warnings")?; - bash!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; + cmd!("cargo", "fmt", "--all", "--", "--check").run()?; + cmd!( + "cargo", + "clippy", + "--all", + "--tests", + "--benches", + "--", + "-D", + "warnings", + ) + .run()?; + cmd!("dotnet", "tool", "restore").dir("crates/bindings-csharp").run()?; + cmd!("dotnet", "csharpier", "--check", ".") + .dir("crates/bindings-csharp") + .run()?; // `bindings` is the only crate we care strongly about documenting, // since we link to its docs.rs from our website. // We won't pass `--no-deps`, though, // since we want everything reachable through it to also work. // This includes `sats` and `lib`. - bash!( - "cd crates/bindings && cargo doc", + cmd!("cargo", "doc") + .dir("crates/bindings") // Make `cargo doc` exit with error on warnings, most notably broken links - &[("RUSTDOCFLAGS", "--deny warnings")] - )?; + .env("RUSTDOCFLAGS", "--deny warnings") + .run()?; } Some(CiCmd::WasmBindings) => { - bash!("cargo test -p spacetimedb-codegen")?; + cmd!("cargo", "test", "-p", "spacetimedb-codegen").run()?; // Make sure the `Cargo.lock` file reflects the latest available versions. // This is what users would end up with on a fresh module, so we want to // catch any compile errors arising from a different transitive closure // of dependencies than what is in the workspace lock file. // // For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714 - bash!("cargo update")?; - bash!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; + cmd!("cargo", "update").run()?; + cmd!( + "cargo", + "run", + "-p", + "spacetimedb-cli", + "--", + "build", + "--project-path", + "modules/module-test", + ) + .run()?; } Some(CiCmd::Smoketests { @@ -738,20 +775,34 @@ fn main() -> Result<()> { "" }; - bash!(&format!("echo 'checking update flow for target: {target}'"))?; - bash!(&format!( - "cargo build {github_token_auth_flag}{target} -p spacetimedb-update" - ))?; + println!("checking update flow for target: {target}"); + cmd!( + "cargo", + "build", + &format!("{github_token_auth_flag}{target}"), + "-p", + "spacetimedb-update", + ) + .run()?; // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it // happens very frequently on the `macos-runner`, but we haven't seen it on any others). - bash!(&format!( - r#" -ROOT_DIR="$(mktemp -d)" -cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes -"${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help - "# - ))?; + let root_dir = tempfile::tempdir()?; + let root_path = root_dir.path().to_string_lossy().to_string(); + cmd!( + "cargo", + "run", + &format!("{github_token_auth_flag}{target}"), + "-p", + "spacetimedb-update", + "--", + "self-install", + "--root-dir", + &root_path, + "--yes", + ) + .run()?; + cmd!(format!("{}/spacetime", root_path), "--root-dir", &root_path, "help",).run()?; } Some(CiCmd::CliDocs { spacetime_path }) => { @@ -766,20 +817,21 @@ cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install ); } - bash!("pnpm install --recursive")?; - bash!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; - bash!("pnpm format")?; - bash!("git status")?; - bash!( - r#" -if git diff --exit-code HEAD; then - echo "No docs changes detected" -else - echo "It looks like the CLI docs have changed:" - exit 1 -fi - "# - )?; + cmd!("pnpm", "install", "--recursive").run()?; + let cli_docs = cmd!("cargo", "run", "--features", "markdown-docs", "-p", "spacetimedb-cli",).read()?; + fs::write("docs/docs/cli-reference.md", cli_docs)?; + cmd!("pnpm", "format").run()?; + let status = cmd!("git", "diff", "--exit-code", "HEAD") + .stdout_null() + .stderr_null() + .unchecked() + .run()?; + if !status.status.success() { + println!("It looks like the CLI docs have changed:"); + anyhow::bail!("CLI docs are out of date"); + } else { + println!("No docs changes detected"); + } } Some(CiCmd::SelfDocs { check }) => { From dd6e2ab27445c4e34f738281bcad09f8020e3691 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 12:29:39 -0800 Subject: [PATCH 104/160] [bfops/windows-ci]: remove bash! --- tools/ci/src/main.rs | 182 ++++++++++++++++++++++++++++--------------- 1 file changed, 120 insertions(+), 62 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 6bf1171a6e0..a3f1f3f0bc1 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,9 +1,15 @@ -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; -use std::collections::HashMap; -use std::path::Path; +use log::warn; +use serde_json; +use std::collections::{HashMap, HashSet}; +use std::net::TcpListener; +use std::path::{Path, PathBuf}; +use std::thread; +use std::time::{Duration, Instant}; use std::{env, fs}; +use tempfile::TempDir; const README_PATH: &str = "tools/ci/README.md"; @@ -93,15 +99,6 @@ enum CiCmd { }, } -macro_rules! bash { - ($cmdline:expr) => { - run_bash($cmdline, &Vec::new()) - }; - ($cmdline:expr, $envs:expr) => { - run_bash($cmdline, $envs) - }; -} - fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { let subcmds = Cli::command() .get_subcommands() @@ -114,64 +111,110 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { continue; } log::info!("executing cargo ci {subcmd}"); - bash!(&format!("cargo ci {subcmd}"))?; + cmd!("cargo", "ci", &subcmd).run()?; } Ok(()) } -fn run_bash(cmdline: &str, additional_env: &[(&str, &str)]) -> Result<()> { - let mut env = env::vars().collect::>(); - env.extend(additional_env.iter().map(|(k, v)| (k.to_string(), v.to_string()))); - log::debug!("$ {cmdline}"); - let status = cmd!("bash", "-lc", cmdline).full_env(env).run()?; - if !status.status.success() { - let e = anyhow::anyhow!("command failed: {cmdline}"); - log::error!("{e}"); - return Err(e); } Ok(()) } +fn infer_python() -> String { + let py3_available = cmd!("python3", "--version").run().is_ok(); + if py3_available { + "python3".to_string() + } else { + "python".to_string() + } +} + fn main() -> Result<()> { + env_logger::init(); + let cli = Cli::parse(); match cli.cmd { Some(CiCmd::Test) => { - bash!("cargo test --all -- --skip unreal")?; + cmd!("cargo", "test", "--all", "--", "--skip", "unreal").run()?; // The fallocate tests have been flakely when running in parallel - bash!("cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1")?; - bash!("bash tools/check-diff.sh")?; - bash!("cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp")?; - bash!("(cd crates/bindings-csharp && dotnet test -warnaserror)")?; + cmd!( + "cargo", + "test", + "-p", + "spacetimedb-durability", + "--features", + "fallocate", + "--", + "--test-threads=1", + ) + .run()?; + cmd!("bash", "tools/check-diff.sh").run()?; + cmd!( + "cargo", + "run", + "-p", + "spacetimedb-codegen", + "--example", + "regen-csharp-moduledef", + ) + .run()?; + cmd!("bash", "tools/check-diff.sh", "crates/bindings-csharp").run()?; + cmd!("dotnet", "test", "-warnaserror") + .dir("crates/bindings-csharp") + .run()?; } Some(CiCmd::Lint) => { - bash!("cargo fmt --all -- --check")?; - bash!("cargo clippy --all --tests --benches -- -D warnings")?; - bash!("(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)")?; + cmd!("cargo", "fmt", "--all", "--", "--check").run()?; + cmd!( + "cargo", + "clippy", + "--all", + "--tests", + "--benches", + "--", + "-D", + "warnings", + ) + .run()?; + cmd!("dotnet", "tool", "restore").dir("crates/bindings-csharp").run()?; + cmd!("dotnet", "csharpier", "--check", ".") + .dir("crates/bindings-csharp") + .run()?; // `bindings` is the only crate we care strongly about documenting, // since we link to its docs.rs from our website. // We won't pass `--no-deps`, though, // since we want everything reachable through it to also work. // This includes `sats` and `lib`. - bash!( - "cd crates/bindings && cargo doc", + cmd!("cargo", "doc") + .dir("crates/bindings") // Make `cargo doc` exit with error on warnings, most notably broken links - &[("RUSTDOCFLAGS", "--deny warnings")] - )?; + .env("RUSTDOCFLAGS", "--deny warnings") + .run()?; } Some(CiCmd::WasmBindings) => { - bash!("cargo test -p spacetimedb-codegen")?; + cmd!("cargo", "test", "-p", "spacetimedb-codegen").run()?; // Make sure the `Cargo.lock` file reflects the latest available versions. // This is what users would end up with on a fresh module, so we want to // catch any compile errors arising from a different transitive closure // of dependencies than what is in the workspace lock file. // // For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714 - bash!("cargo update")?; - bash!("cargo run -p spacetimedb-cli -- build --project-path modules/module-test")?; + cmd!("cargo", "update").run()?; + cmd!( + "cargo", + "run", + "-p", + "spacetimedb-cli", + "--", + "build", + "--project-path", + "modules/module-test", + ) + .run()?; } Some(CiCmd::Smoketests { args }) => { @@ -192,20 +235,34 @@ fn main() -> Result<()> { "" }; - bash!(&format!("echo 'checking update flow for target: {target}'"))?; - bash!(&format!( - "cargo build {github_token_auth_flag}{target} -p spacetimedb-update" - ))?; + println!("checking update flow for target: {target}"); + cmd!( + "cargo", + "build", + &format!("{github_token_auth_flag}{target}"), + "-p", + "spacetimedb-update", + ) + .run()?; // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it // happens very frequently on the `macos-runner`, but we haven't seen it on any others). - bash!(&format!( - r#" -ROOT_DIR="$(mktemp -d)" -cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes -"${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help - "# - ))?; + let root_dir = tempfile::tempdir()?; + let root_path = root_dir.path().to_string_lossy().to_string(); + cmd!( + "cargo", + "run", + &format!("{github_token_auth_flag}{target}"), + "-p", + "spacetimedb-update", + "--", + "self-install", + "--root-dir", + &root_path, + "--yes", + ) + .run()?; + cmd!(format!("{}/spacetime", root_path), "--root-dir", &root_path, "help",).run()?; } Some(CiCmd::CliDocs { spacetime_path }) => { @@ -220,20 +277,21 @@ cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install ); } - bash!("pnpm install --recursive")?; - bash!("cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md")?; - bash!("pnpm format")?; - bash!("git status")?; - bash!( - r#" -if git diff --exit-code HEAD; then - echo "No docs changes detected" -else - echo "It looks like the CLI docs have changed:" - exit 1 -fi - "# - )?; + cmd!("pnpm", "install", "--recursive").run()?; + let cli_docs = cmd!("cargo", "run", "--features", "markdown-docs", "-p", "spacetimedb-cli",).read()?; + fs::write("docs/docs/cli-reference.md", cli_docs)?; + cmd!("pnpm", "format").run()?; + let status = cmd!("git", "diff", "--exit-code", "HEAD") + .stdout_null() + .stderr_null() + .unchecked() + .run()?; + if !status.status.success() { + println!("It looks like the CLI docs have changed:"); + anyhow::bail!("CLI docs are out of date"); + } else { + println!("No docs changes detected"); + } } Some(CiCmd::SelfDocs { check }) => { From 9635badbfc1066cbe54fc16f855b08040e83853e Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 12:38:04 -0800 Subject: [PATCH 105/160] [bfops/windows-ci]: review --- Cargo.lock | 2 ++ tools/ci/Cargo.toml | 2 ++ tools/ci/src/main.rs | 31 ++++++++++++------------------- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 566644cdf0a..d03bfb40099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -879,8 +879,10 @@ dependencies = [ "chrono", "clap 4.5.50", "duct", + "env_logger 0.10.2", "log", "regex", + "tempfile", ] [[package]] diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index e907526a42c..a79a4917ca7 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -10,3 +10,5 @@ chrono = { workspace = true, features=["clock"] } clap.workspace = true regex.workspace = true duct.workspace = true +tempfile.workspace = true +env_logger.workspace = true diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index a3f1f3f0bc1..581408c75da 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,15 +1,8 @@ -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; -use log::warn; -use serde_json; -use std::collections::{HashMap, HashSet}; -use std::net::TcpListener; -use std::path::{Path, PathBuf}; -use std::thread; -use std::time::{Duration, Instant}; +use std::path::Path; use std::{env, fs}; -use tempfile::TempDir; const README_PATH: &str = "tools/ci/README.md"; @@ -113,11 +106,6 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> { log::info!("executing cargo ci {subcmd}"); cmd!("cargo", "ci", &subcmd).run()?; } - - Ok(()) -} - - } Ok(()) } @@ -217,11 +205,16 @@ fn main() -> Result<()> { .run()?; } - Some(CiCmd::Smoketests { args }) => { - // On some systems, there is no `python`, but there is `python3`. - let py3_available = bash!("command -v python3 >/dev/null 2>&1").is_ok(); - let python = if py3_available { "python3" } else { "python" }; - bash!(&format!("{python} -m smoketests {}", args.join(" ")))?; + Some(CiCmd::Smoketests { args: smoketest_args }) => { + let python = infer_python(); + cmd( + python, + ["-m", "smoketests"] + .into_iter() + .map(|s| s.to_string()) + .chain(smoketest_args), + ) + .run()?; } Some(CiCmd::UpdateFlow { From 1a95d7c99b8260afbacfcaa2e6ac8d9e67ededc6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 12:45:51 -0800 Subject: [PATCH 106/160] [bfops/windows-ci]: [REVERT] cache on failure --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abf04057d48..8e1594337ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: with: workspaces: ${{ github.workspace }} shared-key: spacetimedb - cache-on-failure: true + cache-on-failure: false cache-all-crates: true cache-workspace-crates: true From 55d98cca08c0003e3593b8e49dec54f5ca9ab41b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 12:54:29 -0800 Subject: [PATCH 107/160] [bfops/windows-ci]: fix --- tools/ci/src/main.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 581408c75da..7a99de3302b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -221,20 +221,24 @@ fn main() -> Result<()> { target, github_token_auth, }) => { - let target = target.map(|t| format!("--target {t}")).unwrap_or_default(); - let github_token_auth_flag = if github_token_auth { - "--features github-token-auth " + let mut common_args = vec![]; + if let Some(target) = target.as_ref() { + common_args.push("--target"); + common_args.push(target); + println!("checking update flow for target: {target}"); } else { - "" - }; + println!("checking update flow"); + } + if github_token_auth { + common_args.push("--features"); + common_args.push("github-token-auth"); + } - println!("checking update flow for target: {target}"); - cmd!( + cmd( "cargo", - "build", - &format!("{github_token_auth_flag}{target}"), - "-p", - "spacetimedb-update", + ["build", "-p", "spacetimedb-update"] + .into_iter() + .chain(common_args.clone()), ) .run()?; // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`. @@ -242,17 +246,12 @@ fn main() -> Result<()> { // happens very frequently on the `macos-runner`, but we haven't seen it on any others). let root_dir = tempfile::tempdir()?; let root_path = root_dir.path().to_string_lossy().to_string(); - cmd!( + cmd( "cargo", - "run", - &format!("{github_token_auth_flag}{target}"), - "-p", - "spacetimedb-update", - "--", - "self-install", - "--root-dir", - &root_path, - "--yes", + ["run", "-p", "spacetimedb-update"] + .into_iter() + .chain(common_args.clone()) + .chain(["--", "self-install", "--root-dir", &root_path, "--yes"].into_iter()), ) .run()?; cmd!(format!("{}/spacetime", root_path), "--root-dir", &root_path, "help",).run()?; From f73a87efbcff3733401f17a6984fb8b42b1ce26c Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:02:37 -0800 Subject: [PATCH 108/160] [bfops/windows-ci]: fix root dir --- tools/ci/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 7a99de3302b..098153cb271 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -245,16 +245,17 @@ fn main() -> Result<()> { // My best guess is that, on the GitHub runners, the "anonymous" ratelimit is shared by *all* users of that runner (I think this because it // happens very frequently on the `macos-runner`, but we haven't seen it on any others). let root_dir = tempfile::tempdir()?; - let root_path = root_dir.path().to_string_lossy().to_string(); + let root_dir_string = root_dir.path().to_string_lossy().to_string(); + let root_arg = format!("--root-dir={}", root_dir_string); cmd( "cargo", ["run", "-p", "spacetimedb-update"] .into_iter() .chain(common_args.clone()) - .chain(["--", "self-install", "--root-dir", &root_path, "--yes"].into_iter()), + .chain(["--", "self-install", &root_arg, "--yes"].into_iter()), ) .run()?; - cmd!(format!("{}/spacetime", root_path), "--root-dir", &root_path, "help",).run()?; + cmd!(format!("{}/spacetime", root_dir_string), &root_arg, "help",).run()?; } Some(CiCmd::CliDocs { spacetime_path }) => { From 7af60ffb72af5c9358e9887bf3fe98fe0ce6d7e7 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:19:42 -0800 Subject: [PATCH 109/160] [bfops/windows-ci]: fix lints --- tools/ci/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 098153cb271..18816ef0385 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,3 +1,6 @@ +// This is a devtool and not part of the "real" codebase, so different constraints apply. +#![allow(clippy::disallowed_macros)] + use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; From 192a724db88943485d478544a733b87c9a339c30 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:25:56 -0800 Subject: [PATCH 110/160] [bfops/windows-ci]: revert From 829768f768cd8e51c88a2897b3e9e6fec66b4682 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:28:52 -0800 Subject: [PATCH 111/160] [bfops/windows-ci]: simplify --- .github/workflows/ci.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e1594337ca..5d195810b37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,14 +111,9 @@ jobs: if: runner.os == 'Windows' - name: Install python deps run: python -m pip install -r smoketests/requirements.txt - - name: Run smoketests (windows) + - name: Run smoketests # Note: clear_database and replication only work in private run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams - if: runner.os == 'Windows' - - name: Run smoketests (Linux) - # Note: clear_database and replication only work in private - run: cargo ci smoketests -- ${{ matrix.smoketest_args }} -x clear_database replication teams - if: runner.os != 'Windows' - name: Stop containers (Linux) if: always() && runner.os == 'Linux' run: docker compose -f .github/docker-compose.yml down From cde71e7551ae76003247076b70d596724190f47a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:31:03 -0800 Subject: [PATCH 112/160] [bfops/windows-ci]: revert --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d195810b37..79141639f11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: with: workspaces: ${{ github.workspace }} shared-key: spacetimedb - cache-on-failure: false + cache-on-failure: true cache-all-crates: true cache-workspace-crates: true From 4af6eaaaf6a1dd687611aaffa78276ae5e116640 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:32:07 -0800 Subject: [PATCH 113/160] [bfops/windows-ci]: revert --- crates/bindings/tests/ui/views.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bindings/tests/ui/views.rs b/crates/bindings/tests/ui/views.rs index abe92cc2425..f05092569fb 100644 --- a/crates/bindings/tests/ui/views.rs +++ b/crates/bindings/tests/ui/views.rs @@ -144,6 +144,7 @@ fn sched_table_view(_: &ViewContext, _args: ScheduledTable) -> Vec { vec![] } + #[table(name = player_info)] struct PlayerInfo { #[unique] From f93891ab89b787b7f7c3c40cb54895dac3900784 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:33:18 -0800 Subject: [PATCH 114/160] [bfops/windows-ci]: simplify --- tools/ci/src/main.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 18816ef0385..c611d5e227d 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,6 +1,3 @@ -// This is a devtool and not part of the "real" codebase, so different constraints apply. -#![allow(clippy::disallowed_macros)] - use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; @@ -228,9 +225,9 @@ fn main() -> Result<()> { if let Some(target) = target.as_ref() { common_args.push("--target"); common_args.push(target); - println!("checking update flow for target: {target}"); + log::info!("checking update flow for target: {target}"); } else { - println!("checking update flow"); + log::info!("checking update flow"); } if github_token_auth { common_args.push("--features"); @@ -283,10 +280,9 @@ fn main() -> Result<()> { .unchecked() .run()?; if !status.status.success() { - println!("It looks like the CLI docs have changed:"); anyhow::bail!("CLI docs are out of date"); } else { - println!("No docs changes detected"); + log::info!("No docs changes detected"); } } From a2de3e5228852dd2f8290af94e5450a21501065a Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Wed, 10 Dec 2025 13:36:35 -0800 Subject: [PATCH 115/160] [bfops/windows-ci]: review --- tools/ci/src/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c611d5e227d..6492f3ce578 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -274,11 +274,7 @@ fn main() -> Result<()> { let cli_docs = cmd!("cargo", "run", "--features", "markdown-docs", "-p", "spacetimedb-cli",).read()?; fs::write("docs/docs/cli-reference.md", cli_docs)?; cmd!("pnpm", "format").run()?; - let status = cmd!("git", "diff", "--exit-code", "HEAD") - .stdout_null() - .stderr_null() - .unchecked() - .run()?; + let status = cmd!("git", "diff", "--exit-code", "HEAD").unchecked().run()?; if !status.status.success() { anyhow::bail!("CLI docs are out of date"); } else { From 15998aec45ee93dfebce9a0d83775af597e7d0b2 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 11 Dec 2025 11:45:15 -0800 Subject: [PATCH 116/160] [bfops/windows-ci]: check correct file --- tools/ci/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 6492f3ce578..ccf11f37613 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -274,7 +274,9 @@ fn main() -> Result<()> { let cli_docs = cmd!("cargo", "run", "--features", "markdown-docs", "-p", "spacetimedb-cli",).read()?; fs::write("docs/docs/cli-reference.md", cli_docs)?; cmd!("pnpm", "format").run()?; - let status = cmd!("git", "diff", "--exit-code", "HEAD").unchecked().run()?; + let status = cmd!("git", "diff", "--exit-code", "HEAD", "--", "docs/docs/cli-reference.md") + .unchecked() + .run()?; if !status.status.success() { anyhow::bail!("CLI docs are out of date"); } else { From a28380c5af35edd66520d3e9b8ffef986d177d0c Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 11 Dec 2025 11:56:08 -0800 Subject: [PATCH 117/160] [bfops/windows-ci]: review --- tools/ci/src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index ccf11f37613..d69c383232b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -272,9 +272,11 @@ fn main() -> Result<()> { cmd!("pnpm", "install", "--recursive").run()?; let cli_docs = cmd!("cargo", "run", "--features", "markdown-docs", "-p", "spacetimedb-cli",).read()?; - fs::write("docs/docs/cli-reference.md", cli_docs)?; + // TODO: This is actually incorrect and needs updating, but is a correct migration of the previous workflow. + let path = "docs/docs/cli-reference.md"; + fs::write(path, cli_docs)?; cmd!("pnpm", "format").run()?; - let status = cmd!("git", "diff", "--exit-code", "HEAD", "--", "docs/docs/cli-reference.md") + let status = cmd!("git", "diff", "--exit-code", "HEAD", "--", path) .unchecked() .run()?; if !status.status.success() { From 7fca6c3134b177a202672bef83bc793eb1cf9b0b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Thu, 11 Dec 2025 13:04:45 -0800 Subject: [PATCH 118/160] [bfops/windows-ci]: review --- tools/ci/src/main.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d69c383232b..ccac62ddd22 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -126,6 +126,11 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { cmd!("cargo", "test", "--all", "--", "--skip", "unreal").run()?; + // TODO: This should check for a diff at the start. If there is one, we should alert the user + // that we're disabling diff checks because they have a dirty git repo, and to re-run in a clean one + // if they want those checks. + + // TODO: These tests don't work on user machines, because they apparently use `sudo`. // The fallocate tests have been flakely when running in parallel cmd!( "cargo", From 743b20dae11d09574f685d0e07f623ff0dc314cd Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 15 Dec 2025 10:24:20 -0800 Subject: [PATCH 119/160] [bfops/windows-ci]: move TODO --- tools/ci/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index ccac62ddd22..a5b9f24f1d0 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -125,12 +125,13 @@ fn main() -> Result<()> { match cli.cmd { Some(CiCmd::Test) => { + // TODO: This doesn't work on at least user Linux machines, because something here apparently uses `sudo`? + cmd!("cargo", "test", "--all", "--", "--skip", "unreal").run()?; // TODO: This should check for a diff at the start. If there is one, we should alert the user // that we're disabling diff checks because they have a dirty git repo, and to re-run in a clean one // if they want those checks. - // TODO: These tests don't work on user machines, because they apparently use `sudo`. // The fallocate tests have been flakely when running in parallel cmd!( "cargo", From 4b2a25c68d432a5e285a77aab05c166b5b2f2de7 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Mon, 15 Dec 2025 11:02:18 -0800 Subject: [PATCH 120/160] [bfops/pnpm-install-instruction]: almost fix CLI stuff --- crates/cli/src/subcommands/init.rs | 2 +- docs/README.md | 4 +- .../00100-cli-reference.md | 581 +++++++++++------- docs/scripts/regenerate-cli-reference.sh | 19 + tools/ci/src/main.rs | 8 +- 5 files changed, 366 insertions(+), 248 deletions(-) create mode 100755 docs/scripts/regenerate-cli-reference.sh diff --git a/crates/cli/src/subcommands/init.rs b/crates/cli/src/subcommands/init.rs index 19cec472996..0fa5b97f7ff 100644 --- a/crates/cli/src/subcommands/init.rs +++ b/crates/cli/src/subcommands/init.rs @@ -123,7 +123,7 @@ pub fn cli() -> clap::Command { .long("project-path") .value_name("PATH") .value_parser(clap::value_parser!(PathBuf)) - .help("Directory where the project will be created (defaults to ./)"), + .help("Directory where the project will be created (defaults to ./[PROJECT_NAME])"), ) .arg(Arg::new("project-name").value_name("PROJECT_NAME").help("Project name")) .arg( diff --git a/docs/README.md b/docs/README.md index ba64a765c14..c4d0013340f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,9 +39,7 @@ git push -u origin a-branch-name-that-describes-my-change your GitHub ### CLI Reference Section -1. Run `cargo run --features markdown-docs -p spacetimedb-cli > docs/docs/cli-reference.md` -2. Run `pnpm install` -3. Run `pnpm format` +To regenerate the CLI reference section, run `docs/scripts/regenerate-cli-reference.sh`. ### Docusaurus Documentation For more information on how to use Docusaurus, see the diff --git a/docs/docs/00500-cli-reference/00100-cli-reference.md b/docs/docs/00500-cli-reference/00100-cli-reference.md index 689172a854e..5ec7c2e0f5a 100644 --- a/docs/docs/00500-cli-reference/00100-cli-reference.md +++ b/docs/docs/00500-cli-reference/00100-cli-reference.md @@ -9,67 +9,71 @@ This document contains the help content for the `spacetime` command-line program **Command Overview:** -- [`spacetime`↴](#spacetime) -- [`spacetime publish`↴](#spacetime-publish) -- [`spacetime delete`↴](#spacetime-delete) -- [`spacetime logs`↴](#spacetime-logs) -- [`spacetime call`↴](#spacetime-call) -- [`spacetime describe`↴](#spacetime-describe) -- [`spacetime energy`↴](#spacetime-energy) -- [`spacetime energy balance`↴](#spacetime-energy-balance) -- [`spacetime sql`↴](#spacetime-sql) -- [`spacetime rename`↴](#spacetime-rename) -- [`spacetime generate`↴](#spacetime-generate) -- [`spacetime list`↴](#spacetime-list) -- [`spacetime login`↴](#spacetime-login) -- [`spacetime login show`↴](#spacetime-login-show) -- [`spacetime logout`↴](#spacetime-logout) -- [`spacetime init`↴](#spacetime-init) -- [`spacetime build`↴](#spacetime-build) -- [`spacetime server`↴](#spacetime-server) -- [`spacetime server list`↴](#spacetime-server-list) -- [`spacetime server set-default`↴](#spacetime-server-set-default) -- [`spacetime server add`↴](#spacetime-server-add) -- [`spacetime server remove`↴](#spacetime-server-remove) -- [`spacetime server fingerprint`↴](#spacetime-server-fingerprint) -- [`spacetime server ping`↴](#spacetime-server-ping) -- [`spacetime server edit`↴](#spacetime-server-edit) -- [`spacetime server clear`↴](#spacetime-server-clear) -- [`spacetime subscribe`↴](#spacetime-subscribe) -- [`spacetime start`↴](#spacetime-start) -- [`spacetime version`↴](#spacetime-version) - -## spacetime +* [`spacetime`↴](#spacetime) +* [`spacetime publish`↴](#spacetime-publish) +* [`spacetime delete`↴](#spacetime-delete) +* [`spacetime logs`↴](#spacetime-logs) +* [`spacetime call`↴](#spacetime-call) +* [`spacetime describe`↴](#spacetime-describe) +* [`spacetime dev`↴](#spacetime-dev) +* [`spacetime energy`↴](#spacetime-energy) +* [`spacetime energy balance`↴](#spacetime-energy-balance) +* [`spacetime sql`↴](#spacetime-sql) +* [`spacetime rename`↴](#spacetime-rename) +* [`spacetime generate`↴](#spacetime-generate) +* [`spacetime list`↴](#spacetime-list) +* [`spacetime login`↴](#spacetime-login) +* [`spacetime login show`↴](#spacetime-login-show) +* [`spacetime logout`↴](#spacetime-logout) +* [`spacetime init`↴](#spacetime-init) +* [`spacetime build`↴](#spacetime-build) +* [`spacetime server`↴](#spacetime-server) +* [`spacetime server list`↴](#spacetime-server-list) +* [`spacetime server set-default`↴](#spacetime-server-set-default) +* [`spacetime server add`↴](#spacetime-server-add) +* [`spacetime server remove`↴](#spacetime-server-remove) +* [`spacetime server fingerprint`↴](#spacetime-server-fingerprint) +* [`spacetime server ping`↴](#spacetime-server-ping) +* [`spacetime server edit`↴](#spacetime-server-edit) +* [`spacetime server clear`↴](#spacetime-server-clear) +* [`spacetime subscribe`↴](#spacetime-subscribe) +* [`spacetime start`↴](#spacetime-start) +* [`spacetime version`↴](#spacetime-version) + +## `spacetime` **Usage:** `spacetime [OPTIONS] ` ###### **Subcommands:** -- `publish` — Create and update a SpacetimeDB database -- `delete` — Deletes a SpacetimeDB database -- `logs` — Prints logs from a SpacetimeDB database -- `call` — Invokes a reducer function in a database. WARNING: This command is UNSTABLE and subject to breaking changes. -- `describe` — Describe the structure of a database or entities within it. WARNING: This command is UNSTABLE and subject to breaking changes. -- `energy` — Invokes commands related to database budgets. WARNING: This command is UNSTABLE and subject to breaking changes. -- `sql` — Runs a SQL query on the database. WARNING: This command is UNSTABLE and subject to breaking changes. -- `rename` — Rename a database -- `generate` — Generate client files for a spacetime module. -- `list` — Lists the databases attached to an identity. WARNING: This command is UNSTABLE and subject to breaking changes. -- `login` — Manage your login to the SpacetimeDB CLI -- `logout` — -- `init` — Initializes a new spacetime project. WARNING: This command is UNSTABLE and subject to breaking changes. -- `build` — Builds a spacetime module. -- `server` — Manage the connection to the SpacetimeDB server. WARNING: This command is UNSTABLE and subject to breaking changes. -- `subscribe` — Subscribe to SQL queries on the database. WARNING: This command is UNSTABLE and subject to breaking changes. -- `start` — Start a local SpacetimeDB instance -- `version` — Manage installed spacetime versions - -###### Options: - -- `--root-dir ` — The root directory to store all spacetime files in. -- `--config-path ` — The path to the cli.toml config file - -## spacetime publish +* `publish` — Create and update a SpacetimeDB database +* `delete` — Deletes a SpacetimeDB database +* `logs` — Prints logs from a SpacetimeDB database +* `call` — Invokes a reducer function in a database. WARNING: This command is UNSTABLE and subject to breaking changes. +* `describe` — Describe the structure of a database or entities within it. WARNING: This command is UNSTABLE and subject to breaking changes. +* `dev` — Start development mode with auto-regenerate client module bindings, auto-rebuild, and auto-publish on file changes. +* `energy` — Invokes commands related to database budgets. WARNING: This command is UNSTABLE and subject to breaking changes. +* `sql` — Runs a SQL query on the database. WARNING: This command is UNSTABLE and subject to breaking changes. +* `rename` — Rename a database +* `generate` — Generate client files for a spacetime module. +* `list` — Lists the databases attached to an identity. WARNING: This command is UNSTABLE and subject to breaking changes. +* `login` — Manage your login to the SpacetimeDB CLI +* `logout` — +* `init` — Initializes a new spacetime project. WARNING: This command is UNSTABLE and subject to breaking changes. +* `build` — Builds a spacetime module. +* `server` — Manage the connection to the SpacetimeDB server. WARNING: This command is UNSTABLE and subject to breaking changes. +* `subscribe` — Subscribe to SQL queries on the database. WARNING: This command is UNSTABLE and subject to breaking changes. +* `start` — Start a local SpacetimeDB instance +* `version` — Manage installed spacetime versions + +###### **Options:** + +* `--root-dir ` — The root directory to store all spacetime files in. +* `--config-path ` — The path to the cli.toml config file + + + +## `spacetime publish` Create and update a SpacetimeDB database @@ -77,31 +81,39 @@ Create and update a SpacetimeDB database Run `spacetime help publish` for more detailed information. -###### Arguments: +###### **Arguments:** + +* `` — A valid domain or identity for this database. -- `` — A valid domain or identity for this database. + Database names must match the regex `/^[a-z0-9]+(-[a-z0-9]+)*$/`, + i.e. only lowercase ASCII letters and numbers, separated by dashes. - Database names must match the regex `/^[a-z0-9]+(-[a-z0-9]+)*$/`, - i.e. only lowercase ASCII letters and numbers, separated by dashes. +###### **Options:** -###### Options: +* `-c`, `--delete-data ` — When publishing to an existing database identity, first DESTROY all data associated with the module. With 'on-conflict': only when breaking schema changes occur. -- `-c`, `--delete-data` — When publishing to an existing database identity, first DESTROY all data associated with the module -- `--build-options ` — Options to pass to the build command, for example --build-options='--lint-dir=' + Possible values: `always`, `on-conflict`, `never` - Default value: \`\` +* `--build-options ` — Options to pass to the build command, for example --build-options='--lint-dir=' -- `-p`, `--project-path ` — The system path (absolute or relative) to the module project + Default value: `` +* `-p`, `--project-path ` — The system path (absolute or relative) to the module project Default value: `.` +* `-b`, `--bin-path ` — The system path (absolute or relative) to the compiled wasm binary we should publish, instead of building the project. +* `-j`, `--js-path ` — UNSTABLE: The system path (absolute or relative) to the javascript file we should publish, instead of building the project. +* `--break-clients` — Allow breaking changes when publishing to an existing database identity. This will force publish even if it will break existing clients, but will NOT force publish if it would cause deletion of any data in the database. See --yes and --delete-data for details. +* `--anonymous` — Perform this action with an anonymous identity +* `--parent ` — A valid domain or identity of an existing database that should be the parent of this database. -- `-b`, `--bin-path ` — The system path (absolute or relative) to the compiled wasm binary we should publish, instead of building the project. -- `--break-clients` — Allow breaking changes when publishing to an existing database identity. This will break existing clients. -- `--anonymous` — Perform this action with an anonymous identity -- `-s`, `--server ` — The nickname, domain name or URL of the server to host the database. -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). + If a parent is given, the new database inherits the team permissions from the parent. + A parent can only be set when a database is created, not when it is updated. +* `-s`, `--server ` — The nickname, domain name or URL of the server to host the database. +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime delete + + +## `spacetime delete` Deletes a SpacetimeDB database @@ -109,16 +121,19 @@ Deletes a SpacetimeDB database Run `spacetime help delete` for more detailed information. -###### Arguments: -- `` — The name or identity of the database to delete +###### **Arguments:** + +* `` — The name or identity of the database to delete + +###### **Options:** + +* `-s`, `--server ` — The nickname, host name or URL of the server hosting the database +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -###### Options: -- `-s`, `--server ` — The nickname, host name or URL of the server hosting the database -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime logs +## `spacetime logs` Prints logs from a SpacetimeDB database @@ -126,24 +141,27 @@ Prints logs from a SpacetimeDB database Run `spacetime help logs` for more detailed information. -###### Arguments: -- `` — The name or identity of the database to print logs from +###### **Arguments:** -###### Options: +* `` — The name or identity of the database to print logs from -- `-s`, `--server ` — The nickname, host name or URL of the server hosting the database -- `-n`, `--num-lines ` — The number of lines to print from the start of the log of this database. If no num lines is provided, all lines will be returned. -- `-f`, `--follow` — A flag that causes logs to not stop when end of the log file is reached, but rather to wait for additional data to be appended to the input. -- `--format ` — Output format for the logs +###### **Options:** + +* `-s`, `--server ` — The nickname, host name or URL of the server hosting the database +* `-n`, `--num-lines ` — The number of lines to print from the start of the log of this database. If no num lines is provided, all lines will be returned. +* `-f`, `--follow` — A flag that causes logs to not stop when end of the log file is reached, but rather to wait for additional data to be appended to the input. +* `--format ` — Output format for the logs Default value: `text` Possible values: `text`, `json` -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). + + -## spacetime call +## `spacetime call` Invokes a reducer function in a database. WARNING: This command is UNSTABLE and subject to breaking changes. @@ -151,19 +169,22 @@ Invokes a reducer function in a database. WARNING: This command is UNSTABLE and Run `spacetime help call` for more detailed information. -###### Arguments: -- `` — The database name or identity to use to invoke the call -- `` — The name of the reducer to call -- `` — arguments formatted as JSON +###### **Arguments:** + +* `` — The database name or identity to use to invoke the call +* `` — The name of the reducer to call +* `` — arguments formatted as JSON + +###### **Options:** + +* `-s`, `--server ` — The nickname, host name or URL of the server hosting the database +* `--anonymous` — Perform this action with an anonymous identity +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -###### Options: -- `-s`, `--server ` — The nickname, host name or URL of the server hosting the database -- `--anonymous` — Perform this action with an anonymous identity -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime describe +## `spacetime describe` Describe the structure of a database or entities within it. WARNING: This command is UNSTABLE and subject to breaking changes. @@ -171,65 +192,105 @@ Describe the structure of a database or entities within it. WARNING: This comman Run `spacetime help describe` for more detailed information. -###### Arguments: -- `` — The name or identity of the database to describe -- `` — Whether to describe a reducer or table +###### **Arguments:** + +* `` — The name or identity of the database to describe +* `` — Whether to describe a reducer or table Possible values: `reducer`, `table` -- `` — The name of the entity to describe +* `` — The name of the entity to describe + +###### **Options:** + +* `--json` — Output the schema in JSON format. Currently required; in the future, omitting this will give human-readable output. +* `--anonymous` — Perform this action with an anonymous identity +* `-s`, `--server ` — The nickname, host name or URL of the server hosting the database +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). + + -###### Options: +## `spacetime dev` -- `--json` — Output the schema in JSON format. Currently required; in the future, omitting this will give human-readable output. -- `--anonymous` — Perform this action with an anonymous identity -- `-s`, `--server ` — The nickname, host name or URL of the server hosting the database -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). +Start development mode with auto-regenerate client module bindings, auto-rebuild, and auto-publish on file changes. -## spacetime energy +**Usage:** `spacetime dev [OPTIONS]` + +###### **Options:** + +* `--database ` — The database name/identity to publish to (optional, will prompt if not provided) +* `--project-path ` — The path to the project directory + + Default value: `.` +* `--module-bindings-path ` — The path to the module bindings directory relative to the project directory, defaults to `/src/module_bindings` + + Default value: `src/module_bindings` +* `--module-project-path ` — The path to the SpacetimeDB server module project relative to the project directory, defaults to `/spacetimedb` + + Default value: `spacetimedb` +* `--client-lang ` — The programming language for the generated client module bindings (e.g., typescript, csharp, python). If not specified, it will be detected from the project. + + Possible values: `csharp`, `typescript`, `rust`, `unrealcpp` + +* `-s`, `--server ` — The nickname, host name or URL of the server to publish to +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). +* `-c`, `--delete-data ` — When publishing to an existing database identity, first DESTROY all data associated with the module. With 'on-conflict': only when breaking schema changes occur. + + Possible values: `always`, `on-conflict`, `never` + + + + +## `spacetime energy` Invokes commands related to database budgets. WARNING: This command is UNSTABLE and subject to breaking changes. **Usage:** `spacetime energy energy ` -###### Subcommands: +###### **Subcommands:** + +* `balance` — Show current energy balance for an identity + -- `balance` — Show current energy balance for an identity -## spacetime energy balance +## `spacetime energy balance` Show current energy balance for an identity **Usage:** `spacetime energy balance [OPTIONS]` -###### Options: +###### **Options:** -- `-i`, `--identity ` — The identity to check the balance for. If no identity is provided, the default one will be used. -- `-s`, `--server ` — The nickname, host name or URL of the server from which to request balance information -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). +* `-i`, `--identity ` — The identity to check the balance for. If no identity is provided, the default one will be used. +* `-s`, `--server ` — The nickname, host name or URL of the server from which to request balance information +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime sql + + +## `spacetime sql` Runs a SQL query on the database. WARNING: This command is UNSTABLE and subject to breaking changes. **Usage:** `spacetime sql [OPTIONS] ` -###### Arguments: +###### **Arguments:** + +* `` — The name or identity of the database you would like to query +* `` — The SQL query to execute -- `` — The name or identity of the database you would like to query -- `` — The SQL query to execute +###### **Options:** -###### Options: +* `--interactive` — Instead of using a query, run an interactive command prompt for `SQL` expressions +* `--confirmed` — Instruct the server to deliver only updates of confirmed transactions +* `--anonymous` — Perform this action with an anonymous identity +* `-s`, `--server ` — The nickname, host name or URL of the server hosting the database +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -- `--interactive` — Instead of using a query, run an interactive command prompt for `SQL` expressions -- `--confirmed` — Instruct the server to deliver only updates of confirmed transactions -- `--anonymous` — Perform this action with an anonymous identity -- `-s`, `--server ` — The nickname, host name or URL of the server hosting the database -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime rename + +## `spacetime rename` Rename a database @@ -237,17 +298,20 @@ Rename a database Run `spacetime rename --help` for more detailed information. -###### Arguments: -- `` — The database identity to rename +###### **Arguments:** + +* `` — The database identity to rename -###### Options: +###### **Options:** -- `--to ` — The new name you would like to assign -- `-s`, `--server ` — The nickname, host name or URL of the server on which to set the name -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). +* `--to ` — The new name you would like to assign +* `-s`, `--server ` — The nickname, host name or URL of the server on which to set the name +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime generate + + +## `spacetime generate` Generate client files for a spacetime module. @@ -255,256 +319,286 @@ Generate client files for a spacetime module. Run `spacetime help publish` for more detailed information. -###### Options: +###### **Options:** -- `-b`, `--bin-path ` — The system path (absolute or relative) to the compiled wasm binary we should inspect -- `-p`, `--project-path ` — The system path (absolute or relative) to the project you would like to inspect +* `-b`, `--bin-path ` — The system path (absolute or relative) to the compiled wasm binary we should inspect +* `-j`, `--js-path ` — The system path (absolute or relative) to the bundled javascript file we should inspect +* `-p`, `--project-path ` — The system path (absolute or relative) to the project you would like to inspect Default value: `.` - -- `-o`, `--out-dir ` — The system path (absolute or relative) to the generate output directory -- `--uproject-dir ` — Path to the Unreal project directory, replaces --out-dir for Unreal generation (only used with --lang unrealcpp) -- `--namespace ` — The namespace that should be used +* `-o`, `--out-dir ` — The system path (absolute or relative) to the generate output directory +* `--uproject-dir ` — Path to the Unreal project directory, replaces --out-dir for Unreal generation (only used with --lang unrealcpp) +* `--namespace ` — The namespace that should be used Default value: `SpacetimeDB.Types` - -- `--module-name ` — The module name that should be used for DLL export macros (required for lang unrealcpp) -- `-l`, `--lang ` — The language to generate +* `--module-name ` — The module name that should be used for DLL export macros (required for lang unrealcpp) +* `-l`, `--lang ` — The language to generate Possible values: `csharp`, `typescript`, `rust`, `unrealcpp` -- `--build-options ` — Options to pass to the build command, for example --build-options='--lint-dir=' +* `--build-options ` — Options to pass to the build command, for example --build-options='--lint-dir=' + + Default value: `` +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). - Default value: \`\` -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime list +## `spacetime list` Lists the databases attached to an identity. WARNING: This command is UNSTABLE and subject to breaking changes. **Usage:** `spacetime list [OPTIONS]` -###### Options: +###### **Options:** + +* `-s`, `--server ` — The nickname, host name or URL of the server from which to list databases +* `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -- `-s`, `--server ` — The nickname, host name or URL of the server from which to list databases -- `-y`, `--yes` — Run non-interactively wherever possible. This will answer "yes" to almost all prompts, but will sometimes answer "no" to preserve non-interactivity (e.g. when prompting whether to log in with spacetimedb.com). -## spacetime login + +## `spacetime login` Manage your login to the SpacetimeDB CLI **Usage:** `spacetime login [OPTIONS] login ` -###### Subcommands: +###### **Subcommands:** -- `show` — Show the current login info +* `show` — Show the current login info -###### Options: +###### **Options:** -- `--auth-host ` — Fetch login token from a different host +* `--auth-host ` — Fetch login token from a different host Default value: `https://spacetimedb.com` +* `--server-issued-login ` — Log in to a SpacetimeDB server directly, without going through a global auth server +* `--token ` — Bypass the login flow and use a login token directly + -- `--server-issued-login ` — Log in to a SpacetimeDB server directly, without going through a global auth server -- `--token ` — Bypass the login flow and use a login token directly -## spacetime login show +## `spacetime login show` Show the current login info **Usage:** `spacetime login show [OPTIONS]` -###### Options: +###### **Options:** + +* `--token` — Also show the auth token + -- `--token` — Also show the auth token -## spacetime logout +## `spacetime logout` **Usage:** `spacetime logout [OPTIONS]` -###### Options: +###### **Options:** -- `--auth-host ` — Log out from a custom auth server +* `--auth-host ` — Log out from a custom auth server Default value: `https://spacetimedb.com` -## spacetime init + + +## `spacetime init` Initializes a new spacetime project. WARNING: This command is UNSTABLE and subject to breaking changes. -**Usage:** `spacetime init [OPTIONS] ` +**Usage:** `spacetime init [OPTIONS] [PROJECT_NAME]` -###### Arguments: +###### **Arguments:** * `` — Project name -###### Options: +###### **Options:** -* `--project-path ` — Directory where the project will be created (defaults to ./\) +* `--project-path ` — Directory where the project will be created (defaults to ./[PROJECT_NAME]) +* `--server-only` — Initialize server only from the template (no client) * `--lang ` — Server language: rust, csharp, typescript (it can only be used when --template is not specified) * `-t`, `--template