From 973339e706d73996247a802cb69457f3fec89eed Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 7 Aug 2026 12:01:42 -0400 Subject: [PATCH 01/37] Split cargo ci subcommands into packages --- .github/CODEOWNERS | 4 +- Cargo.lock | 154 ++- Cargo.toml | 16 + tools/ci/Cargo.toml | 11 - tools/ci/README.md | 315 ++---- tools/ci/commands/cli-docs/Cargo.toml | 11 + tools/ci/commands/cli-docs/src/main.rs | 53 + tools/ci/commands/dlls/Cargo.toml | 9 + tools/ci/commands/dlls/src/main.rs | 10 + tools/ci/commands/docs/Cargo.toml | 9 + tools/ci/commands/docs/src/main.rs | 28 + .../ci/commands/global-json-policy/Cargo.toml | 14 + .../commands/global-json-policy/src/main.rs | 337 ++++++ tools/ci/commands/keynote-bench/Cargo.toml | 10 + .../keynote-bench/src/main.rs} | 7 +- tools/ci/commands/lint/Cargo.toml | 14 + tools/ci/commands/lint/src/main.rs | 407 ++++++++ tools/ci/commands/other-workflows/Cargo.toml | 14 + .../other-workflows}/src/cla_assistant.rs | 18 +- .../other-workflows}/src/codeowners_check.rs | 3 +- tools/ci/commands/other-workflows/src/main.rs | 42 + tools/ci/commands/publish-checks/Cargo.toml | 9 + tools/ci/commands/publish-checks/src/main.rs | 37 + tools/ci/commands/self-docs/Cargo.toml | 9 + tools/ci/commands/self-docs/src/main.rs | 137 +++ tools/ci/commands/smoketests/Cargo.toml | 12 + .../smoketests/src/main.rs} | 24 +- tools/ci/commands/test/Cargo.toml | 9 + tools/ci/commands/test/src/main.rs | 131 +++ tools/ci/commands/typescript-test/Cargo.toml | 9 + tools/ci/commands/typescript-test/src/main.rs | 45 + tools/ci/commands/update-flow/Cargo.toml | 12 + tools/ci/commands/update-flow/src/main.rs | 69 ++ .../commands/version-upgrade-check/Cargo.toml | 9 + .../version-upgrade-check/src/main.rs | 22 + tools/ci/commands/wasm-bindings/Cargo.toml | 9 + tools/ci/commands/wasm-bindings/src/main.rs | 50 + tools/ci/common/Cargo.toml | 6 + tools/ci/common/src/lib.rs | 10 + tools/ci/src/ci_docs.rs | 85 -- tools/ci/src/main.rs | 985 ++---------------- tools/ci/src/util.rs | 11 - 42 files changed, 1921 insertions(+), 1255 deletions(-) create mode 100644 tools/ci/commands/cli-docs/Cargo.toml create mode 100644 tools/ci/commands/cli-docs/src/main.rs create mode 100644 tools/ci/commands/dlls/Cargo.toml create mode 100644 tools/ci/commands/dlls/src/main.rs create mode 100644 tools/ci/commands/docs/Cargo.toml create mode 100644 tools/ci/commands/docs/src/main.rs create mode 100644 tools/ci/commands/global-json-policy/Cargo.toml create mode 100644 tools/ci/commands/global-json-policy/src/main.rs create mode 100644 tools/ci/commands/keynote-bench/Cargo.toml rename tools/ci/{src/keynote_bench.rs => commands/keynote-bench/src/main.rs} (81%) create mode 100644 tools/ci/commands/lint/Cargo.toml create mode 100644 tools/ci/commands/lint/src/main.rs create mode 100644 tools/ci/commands/other-workflows/Cargo.toml rename tools/ci/{ => commands/other-workflows}/src/cla_assistant.rs (95%) rename tools/ci/{ => commands/other-workflows}/src/codeowners_check.rs (99%) create mode 100644 tools/ci/commands/other-workflows/src/main.rs create mode 100644 tools/ci/commands/publish-checks/Cargo.toml create mode 100644 tools/ci/commands/publish-checks/src/main.rs create mode 100644 tools/ci/commands/self-docs/Cargo.toml create mode 100644 tools/ci/commands/self-docs/src/main.rs create mode 100644 tools/ci/commands/smoketests/Cargo.toml rename tools/ci/{src/smoketest.rs => commands/smoketests/src/main.rs} (96%) create mode 100644 tools/ci/commands/test/Cargo.toml create mode 100644 tools/ci/commands/test/src/main.rs create mode 100644 tools/ci/commands/typescript-test/Cargo.toml create mode 100644 tools/ci/commands/typescript-test/src/main.rs create mode 100644 tools/ci/commands/update-flow/Cargo.toml create mode 100644 tools/ci/commands/update-flow/src/main.rs create mode 100644 tools/ci/commands/version-upgrade-check/Cargo.toml create mode 100644 tools/ci/commands/version-upgrade-check/src/main.rs create mode 100644 tools/ci/commands/wasm-bindings/Cargo.toml create mode 100644 tools/ci/commands/wasm-bindings/src/main.rs create mode 100644 tools/ci/common/Cargo.toml create mode 100644 tools/ci/common/src/lib.rs delete mode 100644 tools/ci/src/ci_docs.rs delete mode 100644 tools/ci/src/util.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7ed75409a55..4d6f7594981 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,8 +8,8 @@ LICENSE.txt @cloutiertyler /crates/cli/src/ @bfops @cloutiertyler @jdetter /tools/ci/ @bfops @cloutiertyler @jdetter -/tools/ci/src/keynote_bench.rs @joshua-spacetime @cloutiertyler @jdetter -/tools/ci/src/codeowners_check.rs @cloutiertyler +/tools/ci/commands/keynote-bench/ @joshua-spacetime @cloutiertyler @jdetter +/tools/ci/commands/other-workflows/src/codeowners_check.rs @cloutiertyler /tools/upgrade-version/ @bfops @jdetter @cloutiertyler @rekhoff /tools/release/ @bfops @jdetter @cloutiertyler @rekhoff /tools/license-check/ @bfops @jdetter @cloutiertyler diff --git a/Cargo.lock b/Cargo.lock index bb5abb64fdd..f63db3e9e7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,20 +899,172 @@ name = "ci" version = "0.1.0" dependencies = [ "anyhow", - "chrono", + "duct", +] + +[[package]] +name = "ci-cli-docs" +version = "0.1.0" +dependencies = [ + "anyhow", "clap 4.5.50", "duct", "env_logger 0.10.2", + "log", +] + +[[package]] +name = "ci-common" +version = "0.1.0" + +[[package]] +name = "ci-dlls" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-docs-build" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-global-json-policy" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "env_logger 0.10.2", + "log", + "regex", + "serde_json", + "tempfile", +] + +[[package]] +name = "ci-keynote-bench" +version = "0.1.0" +dependencies = [ + "anyhow", + "env_logger 0.10.2", "keynote-bench-harness", + "spacetimedb-guard", +] + +[[package]] +name = "ci-lint" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "env_logger 0.10.2", "log", "regex", + "serde_json", + "tempfile", +] + +[[package]] +name = "ci-other-workflows" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "env_logger 0.10.2", + "regex", "reqwest 0.12.24", "serde", "serde_json", +] + +[[package]] +name = "ci-publish-checks" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-self-docs" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", +] + +[[package]] +name = "ci-smoketests" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "env_logger 0.10.2", "spacetimedb-guard", "tempfile", ] +[[package]] +name = "ci-test" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-typescript-test" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-update-flow" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "env_logger 0.10.2", + "log", + "tempfile", +] + +[[package]] +name = "ci-version-upgrade-check" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + +[[package]] +name = "ci-wasm-bindings" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", + "env_logger 0.10.2", +] + [[package]] name = "ciborium" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index c31354942a5..2f4930191b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,22 @@ members = [ "sdks/rust/tests/procedural-view-pk-client", "sdks/rust/tests/event-table-client", "tools/ci", + "tools/ci/commands/test", + "tools/ci/commands/lint", + "tools/ci/commands/wasm-bindings", + "tools/ci/commands/dlls", + "tools/ci/commands/smoketests", + "tools/ci/commands/keynote-bench", + "tools/ci/commands/update-flow", + "tools/ci/commands/cli-docs", + "tools/ci/commands/self-docs", + "tools/ci/commands/global-json-policy", + "tools/ci/commands/publish-checks", + "tools/ci/commands/typescript-test", + "tools/ci/commands/version-upgrade-check", + "tools/ci/commands/docs", + "tools/ci/commands/other-workflows", + "tools/ci/common", "tools/keynote-bench-harness", "tools/license-check", "tools/replace-spacetimedb", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 3374b66b34d..61bd0a0e422 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -4,16 +4,5 @@ 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 -reqwest = { workspace = true, features = ["blocking", "json"] } -serde.workspace = true -serde_json.workspace = true duct.workspace = true -tempfile.workspace = true -env_logger.workspace = true -keynote-bench-harness = { path = "../keynote-bench-harness" } -spacetimedb-guard.workspace = true diff --git a/tools/ci/README.md b/tools/ci/README.md index 46eefb9cadc..ebc5a4fbbcd 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -6,360 +6,191 @@ This document provides an overview of the `cargo ci` command-line tool, and docu ## `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] +cargo ci [--skip ] [COMMAND] [ARGS]... ``` -**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 +cargo ci test ``` -**Options:** - -- `--help`: Print help (see a summary with '-h') - ### `lint` -Lints the codebase - -Runs rustfmt, clippy, csharpier, TypeScript lint, and generates rust docs to ensure there are no warnings. - **Usage:** ```bash -Usage: lint +cargo ci 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 +cargo ci wasm-bindings ``` -**Options:** - -- `--help`: Print help (see a summary with '-h') - ### `dlls` -Deprecated; use `cargo regen csharp dlls`. - -Builds and packs C# DLLs and NuGet packages for local Unity workflows. - **Usage:** ```bash -Usage: dlls +cargo ci dlls ``` -**Options:** - -- `--help`: Print help (see a summary with '-h') - ### `smoketests` -Runs smoketests - -Executes the smoketests suite with some default exclusions. - **Usage:** ```bash -Usage: smoketests [OPTIONS] [ARGS]... [COMMAND] -``` - -**Options:** - -- `--server `: Run tests against a remote server instead of spawning local servers. - -When specified, tests will connect to the given URL instead of starting local server instances. Tests that require local server control (like restart tests) will be skipped. - -- `--auth-host `: Use a SpacetimeAuth-issued login for remote-server tests. +This command builds the binaries needed by the smoketests, then runs them. This prevents race +conditions when running tests in parallel with nextest, where multiple test processes might try to +build the same binaries simultaneously -This is required for servers that reject direct server-issued logins for privileged operations. +Usage: ci-smoketests [OPTIONS] [ARGS]... [COMMAND] -Optionally accepts an auth host to pass through to `spacetime login`, for example `--auth-host=https://spacetimedb.com`. +Commands: + prepare Only build binaries without running tests + check-mod-list + help Print this message or the help of the given subcommand(s) -- `--dotnet `: -- `args `: Additional arguments to pass to the test runner -- `--help`: Print help (see a summary with '-h') +Arguments: + [ARGS]... + Additional arguments to pass to the test runner -#### `prepare` +Options: + --server + Run tests against a remote server instead of spawning local servers. -Only build binaries without running tests + When specified, tests will connect to the given URL instead of starting local server + instances. Tests that require local server control (like restart tests) will be skipped. -Use this before running `cargo test --all` to ensure binaries are built. + --auth-host[=] + Use a SpacetimeAuth-issued login for remote-server tests. -**Usage:** -```bash -Usage: prepare -``` - -**Options:** + This is required for servers that reject direct server-issued logins for privileged + operations. -- `--help`: Print help (see a summary with '-h') + Optionally accepts an auth host to pass through to `spacetime login`, for example + `--auth-host=https://spacetimedb.com`. -#### `check-mod-list` + --dotnet + [default: true] + [possible values: true, false] -**Usage:** -```bash -Usage: check-mod-list + -h, --help + Print help (see a summary with '-h') ``` -**Options:** - -- `--help`: Print help - -#### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - ### `keynote-bench` -Runs the keynote benchmark as a CI performance regression gate. - -Assumes release SpacetimeDB binaries and the TypeScript SDK are already built, runs the keynote SpacetimeDB benchmark for 60 seconds against the TypeScript and Rust modules, and fails if throughput is below 275K TPS for TypeScript or 300K TPS for Rust. - **Usage:** ```bash -Usage: keynote-bench +cargo ci keynote-bench ``` -**Options:** - -- `--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] -``` +Usage: ci-update-flow [OPTIONS] -**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. -- `--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 disabled. -- `--help`: Print help (see a summary with '-h') + --github-token-auth + Whether to enable github token authentication feature when building the update binary. By + default this is disabled. + + -h, --help + Print help (see a summary with '-h') +``` ### `cli-docs` **Usage:** ```bash -Usage: cli-docs [OPTIONS] -``` +Usage: ci-cli-docs [OPTIONS] -**Options:** +Options: + --spacetime-path + specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is + located) -- `--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') + -h, --help + Print help (see a summary with '-h') +``` ### `self-docs` **Usage:** ```bash -Usage: self-docs [OPTIONS] -``` +Usage: ci-self-docs [OPTIONS] -**Options:** +Options: + --check + Only check for changes, do not generate the docs -- `--check`: Only check for changes, do not generate the docs -- `--help`: Print help (see a summary with '-h') + -h, --help + Print help (see a summary with '-h') +``` ### `global-json-policy` **Usage:** ```bash -Usage: global-json-policy +cargo ci global-json-policy ``` -**Options:** - -- `--help`: Print help - ### `publish-checks` **Usage:** ```bash -Usage: publish-checks +cargo ci publish-checks ``` -**Options:** - -- `--help`: Print help - ### `typescript-test` **Usage:** ```bash -Usage: typescript-test +cargo ci typescript-test ``` -**Options:** - -- `--help`: Print help - ### `version-upgrade-check` **Usage:** ```bash -Usage: version-upgrade-check +cargo ci version-upgrade-check ``` -**Options:** - -- `--help`: Print help - ### `docs` **Usage:** ```bash -Usage: docs +cargo ci docs ``` -**Options:** - -- `--help`: Print help - ### `other-workflows` **Usage:** ```bash -Usage: other-workflows -``` - -**Options:** - -- `--help`: Print help - -#### `codeowners-check` - -**Usage:** -```bash -Usage: codeowners-check --base-ref --pr-number -``` - -**Options:** +Usage: ci-other-workflows -- `--base-ref `: Git ref to compare against, usually origin/ -- `--pr-number `: Pull request number to inspect for approval state -- `--help`: Print help +Commands: + codeowners-check Checks that sensitive CODEOWNERS-controlled files have the required approvals + cla-assistant Interacts with CLA Assistant + help Print this message or the help of the given subcommand(s) -#### `cla-assistant` - -**Usage:** -```bash -Usage: cla-assistant +Options: + -h, --help Print help ``` -**Options:** - -- `--help`: Print help - -##### `retry` - -**Usage:** -```bash -Usage: retry [OPTIONS] --pr-number -``` - -**Options:** - -- `--pr-number `: Pull request number to check -- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY -- `--help`: Print help - -##### `status` - -**Usage:** -```bash -Usage: status [OPTIONS] <--pr |--sha > -``` - -**Options:** - -- `--pr `: Pull request number whose head commit should be checked -- `--sha `: Commit SHA to check -- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY -- `--help`: Print help - -##### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - -#### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - -### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - - --- This document is auto-generated by running: ```bash cargo ci self-docs -``` \ No newline at end of file +``` diff --git a/tools/ci/commands/cli-docs/Cargo.toml b/tools/ci/commands/cli-docs/Cargo.toml new file mode 100644 index 00000000000..5e8dcee6dcd --- /dev/null +++ b/tools/ci/commands/cli-docs/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ci-cli-docs" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true +log.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/cli-docs/src/main.rs b/tools/ci/commands/cli-docs/src/main.rs new file mode 100644 index 00000000000..14cae753286 --- /dev/null +++ b/tools/ci/commands/cli-docs/src/main.rs @@ -0,0 +1,53 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use clap::Parser; +use duct::{cmd, Expression}; +use std::ffi::OsStr; +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} +use std::env; + +#[derive(Parser)] +struct Args { + #[arg( + long, + long_help = "specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is located)" + )] + spacetime_path: Option, +} + +fn main() -> Result<()> { + env_logger::init(); + let Args { spacetime_path } = Args::parse(); + 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"); + } + + pnpm(["install", "--recursive"]).run()?; + pnpm(["generate-cli-docs"]).dir("docs").run()?; + let out = cmd!("git", "status", "--porcelain", "--", "docs").read()?; + if out.is_empty() { + log::info!("No docs changes detected"); + } else { + anyhow::bail!("CLI docs are out of date:\n{out}"); + } + + Ok(()) +} diff --git a/tools/ci/commands/dlls/Cargo.toml b/tools/ci/commands/dlls/Cargo.toml new file mode 100644 index 00000000000..7ade3c64682 --- /dev/null +++ b/tools/ci/commands/dlls/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-dlls" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/dlls/src/main.rs b/tools/ci/commands/dlls/src/main.rs new file mode 100644 index 00000000000..fb31e24d1b0 --- /dev/null +++ b/tools/ci/commands/dlls/src/main.rs @@ -0,0 +1,10 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use duct::cmd; +fn main() -> Result<()> { + env_logger::init(); + eprintln!("warning: `cargo ci dlls` is deprecated; use `cargo regen csharp dlls` instead"); + cmd!("cargo", "regen", "csharp", "dlls").run()?; + + Ok(()) +} diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml new file mode 100644 index 00000000000..4bf39b2f08f --- /dev/null +++ b/tools/ci/commands/docs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-docs-build" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs new file mode 100644 index 00000000000..76de2c90747 --- /dev/null +++ b/tools/ci/commands/docs/src/main.rs @@ -0,0 +1,28 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use duct::{cmd, Expression}; +use std::ffi::OsStr; +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} +fn run_docs_build() -> Result<()> { + pnpm(["install"]).dir("docs").run()?; + pnpm(["build"]).dir("docs").run()?; + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + run_docs_build() +} diff --git a/tools/ci/commands/global-json-policy/Cargo.toml b/tools/ci/commands/global-json-policy/Cargo.toml new file mode 100644 index 00000000000..af85fbba7b6 --- /dev/null +++ b/tools/ci/commands/global-json-policy/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ci-global-json-policy" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true +regex.workspace = true +serde_json.workspace = true +tempfile.workspace = true +env_logger.workspace = true +log.workspace = true diff --git a/tools/ci/commands/global-json-policy/src/main.rs b/tools/ci/commands/global-json-policy/src/main.rs new file mode 100644 index 00000000000..0aa152ebda0 --- /dev/null +++ b/tools/ci/commands/global-json-policy/src/main.rs @@ -0,0 +1,337 @@ +#![allow(clippy::disallowed_macros)] +#![allow(dead_code)] + +use anyhow::{bail, Context, Result}; +use duct::{cmd, Expression}; +use serde_json::Value; +use std::collections::BTreeSet; +use std::fs; +use std::path::Path; +use std::path::PathBuf; + +/// On Windows, `pnpm` is installed as a `.cmd` shim which `CreateProcess` cannot +/// find without going through the shell. Wrapping with `cmd /c` fixes this. +/// On Unix, we invoke `pnpm` directly. +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} + +fn check_global_json_policy() -> Result<()> { + ensure_repo_root()?; + + let root_json = Path::new("global.json"); + let root_contents = fs::read_to_string(root_json)?; + + let globals = git_tracked_files(":(glob)**/global.json")?; + + let mut ok = true; + for p in globals { + let meta = fs::symlink_metadata(&p)?; + let is_symlink = meta.file_type().is_symlink(); + let is_template_global_json = p.strip_prefix(".").unwrap_or(&p).starts_with(Path::new("templates")); + if is_template_global_json && is_symlink { + eprintln!( + "Error: {} is a symlink. Template files must not be symlinks; they are copied literally and this will break if the CLI is built under Windows where symlinks are not supported.", + p.display() + ); + ok = false; + } + + let contents = fs::read_to_string(&p)?; + if contents != root_contents { + eprintln!("Error: {} does not match the root global.json contents", p.display()); + ok = false; + } else if !is_template_global_json || !is_symlink { + println!("OK: {}", p.display()); + } + } + + if !ok { + bail!("global.json policy check failed"); + } + + Ok(()) +} + +fn package_json_pnpm_version(package_manager: &str) -> Option<&str> { + package_manager.strip_prefix("pnpm@") +} + +fn git_tracked_files(pathspec: &str) -> Result> { + let output = cmd!("git", "ls-files", pathspec).read()?; + Ok(output.lines().map(PathBuf::from).collect()) +} + +fn package_json_string_value(package_json: &Value, key: &str) -> Option { + package_json.get(key)?.as_str().map(str::to_owned) +} + +fn package_json_engines_pnpm(package_json: &Value) -> Option { + package_json.get("engines")?.get("pnpm")?.as_str().map(str::to_owned) +} + +fn read_package_json(path: &Path) -> Result { + let contents = fs::read_to_string(path)?; + Ok(serde_json::from_str(&contents)?) +} + +fn is_npm_package_json(package_json: &Value) -> bool { + [ + "bin", + "dependencies", + "devDependencies", + "exports", + "main", + "optionalDependencies", + "packageManager", + "peerDependencies", + "scripts", + "type", + ] + .iter() + .any(|key| package_json.get(key).is_some()) +} + +fn is_template_path(path: &Path) -> bool { + path.starts_with("templates") +} + +fn minimum_release_age(path: &Path) -> Result { + let workspace = fs::read_to_string(path)?; + workspace + .lines() + .find_map(|line| { + let line = line.trim(); + let value = line.strip_prefix("minimumReleaseAge:")?.trim(); + value.parse::().ok() + }) + .ok_or_else(|| anyhow::anyhow!("{} is missing minimumReleaseAge", path.display())) +} + +fn npmrc_minimum_release_age(path: &Path, expected_minimum_release_age: u64) -> Result { + let contents = fs::read_to_string(path).map_err(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + anyhow::anyhow!( + "{} is tracked but missing from the working tree. Restore it with:\nminimum-release-age={}", + path.display(), + expected_minimum_release_age + ) + } else { + anyhow::anyhow!( + "failed to read {} while checking pnpm minimum package age: {err}", + path.display() + ) + } + })?; + contents + .lines() + .find_map(|line| { + let line = line.trim(); + let value = line.strip_prefix("minimum-release-age=")?.trim(); + value.parse::().ok() + }) + .ok_or_else(|| { + anyhow::anyhow!( + "{} must contain `minimum-release-age={}` to match root pnpm-workspace.yaml", + path.display(), + expected_minimum_release_age + ) + }) +} + +fn check_pnpm_release_age_policy() -> Result<()> { + ensure_repo_root()?; + + let root_package_json_path = Path::new("package.json"); + let root_package_json = read_package_json(root_package_json_path)?; + let package_manager = package_json_string_value(&root_package_json, "packageManager") + .ok_or_else(|| anyhow::anyhow!("package.json is missing packageManager"))?; + let package_manager_version = package_json_pnpm_version(&package_manager) + .ok_or_else(|| anyhow::anyhow!("packageManager must be pnpm@, found {package_manager:?}"))?; + + let expected_engine_pnpm = format!(">={package_manager_version}"); + let engine_pnpm = package_json_engines_pnpm(&root_package_json) + .ok_or_else(|| anyhow::anyhow!("package.json engines is missing pnpm"))?; + if engine_pnpm != expected_engine_pnpm { + bail!("package.json engines.pnpm must be {expected_engine_pnpm:?}, found {engine_pnpm:?}"); + } + + for package_json_path in git_tracked_files(":(glob)**/package.json")? { + let package_json = read_package_json(&package_json_path)?; + let Some(found_package_manager) = package_json_string_value(&package_json, "packageManager") else { + continue; + }; + if found_package_manager != package_manager { + bail!( + "{} packageManager must match root package.json: expected {:?}, found {:?}", + package_json_path.display(), + package_manager, + found_package_manager + ); + } + } + + let root_workspace_path = Path::new("pnpm-workspace.yaml"); + let root_minimum_release_age = minimum_release_age(root_workspace_path)?; + for workspace_path in git_tracked_files(":(glob)**/pnpm-workspace.yaml")? { + let found_minimum_release_age = minimum_release_age(&workspace_path)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimumReleaseAge must match root pnpm-workspace.yaml: expected {}, found {}", + workspace_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for npmrc_path in git_tracked_files(":(glob)**/.npmrc")? { + // Template package roots are copied into projects created by `spacetime init`. + // They must not embed this repo's package-age policy; smoketests enforce it + // at the pnpm process boundary instead. + if is_template_path(&npmrc_path) { + continue; + } + let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", + npmrc_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for package_json_path in git_tracked_files(":(glob)**/package.json")? { + // Template package roots are copied into projects created by `spacetime init`. + // They must not require adjacent .npmrc files for this repo's package-age + // policy; smoketests enforce it at the pnpm process boundary instead. + if is_template_path(&package_json_path) { + continue; + } + let package_json = read_package_json(&package_json_path)?; + if !is_npm_package_json(&package_json) { + continue; + } + let package_dir = package_json_path + .parent() + .expect("git-tracked package.json path should have a parent"); + let npmrc_path = package_dir.join(".npmrc"); + if !npmrc_path.is_file() { + bail!( + "{} is required because {} is an npm/pnpm package manifest.\nAdd {} containing:\nminimum-release-age={}", + npmrc_path.display(), + package_json_path.display(), + npmrc_path.display(), + root_minimum_release_age + ); + } + let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", + npmrc_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for workflow_path in git_tracked_files(".github/workflows/*")? { + let contents = fs::read_to_string(&workflow_path)?; + if contents.contains("pnpm/action-setup@v4") { + bail!( + "{} must use ./.github/actions/setup-pnpm instead of pnpm/action-setup@v4", + workflow_path.display() + ); + } + } + + Ok(()) +} + +/// Codex plugin ships a copy of `skills/`, because plugin installers do not follow symlinks, +/// this checks if the copy is in sync +fn check_codex_plugin_skills_sync() -> Result<()> { + let source = Path::new("skills"); + let copy = Path::new("codex-plugin/plugins/spacetimedb/skills"); + + if fs::symlink_metadata(copy) + .with_context(|| format!("reading {}", copy.display()))? + .file_type() + .is_symlink() + { + bail!( + "{} must be a real directory, not a symlink, plugin installers do not follow symlinks", + copy.display() + ); + } + + fn walk(root: &Path, dir: &Path, out: &mut BTreeSet) -> Result<()> { + for entry in fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { + let entry = entry?; + if entry.file_type()?.is_dir() { + walk(root, &entry.path(), out)?; + } else { + let rel = entry + .path() + .strip_prefix(root) + .expect("walked path should be under its root") + .to_path_buf(); + out.insert(rel); + } + } + Ok(()) + } + + let mut source_files = BTreeSet::new(); + walk(source, source, &mut source_files)?; + let mut copy_files = BTreeSet::new(); + walk(copy, copy, &mut copy_files)?; + + let mut drift = Vec::new(); + for rel in &source_files { + if !copy_files.contains(rel) { + drift.push(format!("missing from copy: {}", rel.display())); + } else if fs::read(source.join(rel))? != fs::read(copy.join(rel))? { + drift.push(format!("differs: {}", rel.display())); + } + } + for rel in ©_files { + if !source_files.contains(rel) { + drift.push(format!("extraneous in copy: {}", rel.display())); + } + } + + if !drift.is_empty() { + bail!( + "codex-plugin skills copy is out of sync with skills/:\n {}\nRun: node codex-plugin/scripts/check-skills-sync.ts --fix", + drift.join("\n ") + ); + } + Ok(()) +} + +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + check_global_json_policy() +} diff --git a/tools/ci/commands/keynote-bench/Cargo.toml b/tools/ci/commands/keynote-bench/Cargo.toml new file mode 100644 index 00000000000..c14587165e5 --- /dev/null +++ b/tools/ci/commands/keynote-bench/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ci-keynote-bench" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +keynote-bench-harness = { path = "../../../keynote-bench-harness" } +spacetimedb-guard.workspace = true +env_logger.workspace = true diff --git a/tools/ci/src/keynote_bench.rs b/tools/ci/commands/keynote-bench/src/main.rs similarity index 81% rename from tools/ci/src/keynote_bench.rs rename to tools/ci/commands/keynote-bench/src/main.rs index ed49bdd8185..8fcff0dd769 100644 --- a/tools/ci/src/keynote_bench.rs +++ b/tools/ci/commands/keynote-bench/src/main.rs @@ -2,10 +2,15 @@ use anyhow::Result; use keynote_bench_harness::KeynoteBenchConfig; use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; -pub fn run() -> Result<()> { +fn run() -> Result<()> { let cli_path = ensure_binaries_built(); let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); let server_url = server.host_url.clone(); keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) } + +fn main() -> Result<()> { + env_logger::init(); + run() +} diff --git a/tools/ci/commands/lint/Cargo.toml b/tools/ci/commands/lint/Cargo.toml new file mode 100644 index 00000000000..c1e456a84ca --- /dev/null +++ b/tools/ci/commands/lint/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ci-lint" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true +regex.workspace = true +serde_json.workspace = true +tempfile.workspace = true +env_logger.workspace = true +log.workspace = true diff --git a/tools/ci/commands/lint/src/main.rs b/tools/ci/commands/lint/src/main.rs new file mode 100644 index 00000000000..190c014cd66 --- /dev/null +++ b/tools/ci/commands/lint/src/main.rs @@ -0,0 +1,407 @@ +#![allow(clippy::disallowed_macros)] +#![allow(dead_code)] + +use anyhow::{bail, Context, Result}; +use duct::{cmd, Expression}; +use serde_json::Value; +use std::collections::BTreeSet; +use std::ffi::OsString; +use std::fs; +use std::path::Path; +use std::path::PathBuf; + +/// On Windows, `pnpm` is installed as a `.cmd` shim which `CreateProcess` cannot +/// find without going through the shell. Wrapping with `cmd /c` fixes this. +/// On Unix, we invoke `pnpm` directly. +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} + +fn check_global_json_policy() -> Result<()> { + ensure_repo_root()?; + + let root_json = Path::new("global.json"); + let root_contents = fs::read_to_string(root_json)?; + + let globals = git_tracked_files(":(glob)**/global.json")?; + + let mut ok = true; + for p in globals { + let meta = fs::symlink_metadata(&p)?; + let is_symlink = meta.file_type().is_symlink(); + let is_template_global_json = p.strip_prefix(".").unwrap_or(&p).starts_with(Path::new("templates")); + if is_template_global_json && is_symlink { + eprintln!( + "Error: {} is a symlink. Template files must not be symlinks; they are copied literally and this will break if the CLI is built under Windows where symlinks are not supported.", + p.display() + ); + ok = false; + } + + let contents = fs::read_to_string(&p)?; + if contents != root_contents { + eprintln!("Error: {} does not match the root global.json contents", p.display()); + ok = false; + } else if !is_template_global_json || !is_symlink { + println!("OK: {}", p.display()); + } + } + + if !ok { + bail!("global.json policy check failed"); + } + + Ok(()) +} + +fn package_json_pnpm_version(package_manager: &str) -> Option<&str> { + package_manager.strip_prefix("pnpm@") +} + +fn git_tracked_files(pathspec: &str) -> Result> { + let output = cmd!("git", "ls-files", pathspec).read()?; + Ok(output.lines().map(PathBuf::from).collect()) +} + +fn package_json_string_value(package_json: &Value, key: &str) -> Option { + package_json.get(key)?.as_str().map(str::to_owned) +} + +fn package_json_engines_pnpm(package_json: &Value) -> Option { + package_json.get("engines")?.get("pnpm")?.as_str().map(str::to_owned) +} + +fn read_package_json(path: &Path) -> Result { + let contents = fs::read_to_string(path)?; + Ok(serde_json::from_str(&contents)?) +} + +fn is_npm_package_json(package_json: &Value) -> bool { + [ + "bin", + "dependencies", + "devDependencies", + "exports", + "main", + "optionalDependencies", + "packageManager", + "peerDependencies", + "scripts", + "type", + ] + .iter() + .any(|key| package_json.get(key).is_some()) +} + +fn is_template_path(path: &Path) -> bool { + path.starts_with("templates") +} + +fn minimum_release_age(path: &Path) -> Result { + let workspace = fs::read_to_string(path)?; + workspace + .lines() + .find_map(|line| { + let line = line.trim(); + let value = line.strip_prefix("minimumReleaseAge:")?.trim(); + value.parse::().ok() + }) + .ok_or_else(|| anyhow::anyhow!("{} is missing minimumReleaseAge", path.display())) +} + +fn npmrc_minimum_release_age(path: &Path, expected_minimum_release_age: u64) -> Result { + let contents = fs::read_to_string(path).map_err(|err| { + if err.kind() == std::io::ErrorKind::NotFound { + anyhow::anyhow!( + "{} is tracked but missing from the working tree. Restore it with:\nminimum-release-age={}", + path.display(), + expected_minimum_release_age + ) + } else { + anyhow::anyhow!( + "failed to read {} while checking pnpm minimum package age: {err}", + path.display() + ) + } + })?; + contents + .lines() + .find_map(|line| { + let line = line.trim(); + let value = line.strip_prefix("minimum-release-age=")?.trim(); + value.parse::().ok() + }) + .ok_or_else(|| { + anyhow::anyhow!( + "{} must contain `minimum-release-age={}` to match root pnpm-workspace.yaml", + path.display(), + expected_minimum_release_age + ) + }) +} + +fn check_pnpm_release_age_policy() -> Result<()> { + ensure_repo_root()?; + + let root_package_json_path = Path::new("package.json"); + let root_package_json = read_package_json(root_package_json_path)?; + let package_manager = package_json_string_value(&root_package_json, "packageManager") + .ok_or_else(|| anyhow::anyhow!("package.json is missing packageManager"))?; + let package_manager_version = package_json_pnpm_version(&package_manager) + .ok_or_else(|| anyhow::anyhow!("packageManager must be pnpm@, found {package_manager:?}"))?; + + let expected_engine_pnpm = format!(">={package_manager_version}"); + let engine_pnpm = package_json_engines_pnpm(&root_package_json) + .ok_or_else(|| anyhow::anyhow!("package.json engines is missing pnpm"))?; + if engine_pnpm != expected_engine_pnpm { + bail!("package.json engines.pnpm must be {expected_engine_pnpm:?}, found {engine_pnpm:?}"); + } + + for package_json_path in git_tracked_files(":(glob)**/package.json")? { + let package_json = read_package_json(&package_json_path)?; + let Some(found_package_manager) = package_json_string_value(&package_json, "packageManager") else { + continue; + }; + if found_package_manager != package_manager { + bail!( + "{} packageManager must match root package.json: expected {:?}, found {:?}", + package_json_path.display(), + package_manager, + found_package_manager + ); + } + } + + let root_workspace_path = Path::new("pnpm-workspace.yaml"); + let root_minimum_release_age = minimum_release_age(root_workspace_path)?; + for workspace_path in git_tracked_files(":(glob)**/pnpm-workspace.yaml")? { + let found_minimum_release_age = minimum_release_age(&workspace_path)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimumReleaseAge must match root pnpm-workspace.yaml: expected {}, found {}", + workspace_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for npmrc_path in git_tracked_files(":(glob)**/.npmrc")? { + // Template package roots are copied into projects created by `spacetime init`. + // They must not embed this repo's package-age policy; smoketests enforce it + // at the pnpm process boundary instead. + if is_template_path(&npmrc_path) { + continue; + } + let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", + npmrc_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for package_json_path in git_tracked_files(":(glob)**/package.json")? { + // Template package roots are copied into projects created by `spacetime init`. + // They must not require adjacent .npmrc files for this repo's package-age + // policy; smoketests enforce it at the pnpm process boundary instead. + if is_template_path(&package_json_path) { + continue; + } + let package_json = read_package_json(&package_json_path)?; + if !is_npm_package_json(&package_json) { + continue; + } + let package_dir = package_json_path + .parent() + .expect("git-tracked package.json path should have a parent"); + let npmrc_path = package_dir.join(".npmrc"); + if !npmrc_path.is_file() { + bail!( + "{} is required because {} is an npm/pnpm package manifest.\nAdd {} containing:\nminimum-release-age={}", + npmrc_path.display(), + package_json_path.display(), + npmrc_path.display(), + root_minimum_release_age + ); + } + let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; + if found_minimum_release_age != root_minimum_release_age { + bail!( + "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", + npmrc_path.display(), + root_minimum_release_age, + found_minimum_release_age + ); + } + } + + for workflow_path in git_tracked_files(".github/workflows/*")? { + let contents = fs::read_to_string(&workflow_path)?; + if contents.contains("pnpm/action-setup@v4") { + bail!( + "{} must use ./.github/actions/setup-pnpm instead of pnpm/action-setup@v4", + workflow_path.display() + ); + } + } + + Ok(()) +} + +/// Codex plugin ships a copy of `skills/`, because plugin installers do not follow symlinks, +/// this checks if the copy is in sync +fn check_codex_plugin_skills_sync() -> Result<()> { + let source = Path::new("skills"); + let copy = Path::new("codex-plugin/plugins/spacetimedb/skills"); + + if fs::symlink_metadata(copy) + .with_context(|| format!("reading {}", copy.display()))? + .file_type() + .is_symlink() + { + bail!( + "{} must be a real directory, not a symlink, plugin installers do not follow symlinks", + copy.display() + ); + } + + fn walk(root: &Path, dir: &Path, out: &mut BTreeSet) -> Result<()> { + for entry in fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { + let entry = entry?; + if entry.file_type()?.is_dir() { + walk(root, &entry.path(), out)?; + } else { + let rel = entry + .path() + .strip_prefix(root) + .expect("walked path should be under its root") + .to_path_buf(); + out.insert(rel); + } + } + Ok(()) + } + + let mut source_files = BTreeSet::new(); + walk(source, source, &mut source_files)?; + let mut copy_files = BTreeSet::new(); + walk(copy, copy, &mut copy_files)?; + + let mut drift = Vec::new(); + for rel in &source_files { + if !copy_files.contains(rel) { + drift.push(format!("missing from copy: {}", rel.display())); + } else if fs::read(source.join(rel))? != fs::read(copy.join(rel))? { + drift.push(format!("differs: {}", rel.display())); + } + } + for rel in ©_files { + if !source_files.contains(rel) { + drift.push(format!("extraneous in copy: {}", rel.display())); + } + } + + if !drift.is_empty() { + bail!( + "codex-plugin skills copy is out of sync with skills/:\n {}\nRun: node codex-plugin/scripts/check-skills-sync.ts --fix", + drift.join("\n ") + ); + } + Ok(()) +} + +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} +fn tracked_rs_files_under(path: &str) -> Result> { + let output = cmd!("git", "ls-files", "--", path).read()?; + Ok(output + .lines() + .filter(|line| line.ends_with(".rs")) + .map(PathBuf::from) + .collect()) +} + +fn main() -> Result<()> { + env_logger::init(); + ensure_repo_root()?; + check_pnpm_release_age_policy()?; + check_codex_plugin_skills_sync()?; + // `cargo fmt --all` only checks files that Cargo discovers through workspace/package targets. + // However, we also keep Rust sources in a locations that are tracked but not part of our workspace, + // so this approach properly catches all the files, where `cargo fmt` does not. + let mut files = Vec::new(); + files.extend(tracked_rs_files_under(".")?); + const RUSTFMT_BATCH_SIZE: usize = 200; + for batch in files.chunks(RUSTFMT_BATCH_SIZE) { + let mut args = Vec::::with_capacity(batch.len() + 1); + args.push("--check".into()); + args.extend(batch.iter().map(|path| path.as_os_str().to_os_string())); + cmd("rustfmt", args).run()?; + } + cmd!( + "cargo", + "clippy", + "--timings", + "--all", + "--tests", + "--benches", + "--", + "-D", + "warnings", + ) + .run()?; + cmd!( + "cargo", + "clippy", + "--timings", + "--no-default-features", + "--features=browser", + "-pspacetimedb-sdk", + "--tests", + "--benches", + "--", + "-D", + "warnings", + ) + .run()?; + cmd!("dotnet", "tool", "restore").dir("crates/bindings-csharp").run()?; + cmd!("dotnet", "csharpier", "--check", ".") + .dir("crates/bindings-csharp") + .run()?; + pnpm(["lint"]).run()?; + cmd!("cargo", "test", "--doc", "--target", "wasm32-unknown-unknown") + .dir("crates/bindings") + .run()?; + cmd!("cargo", "test", "--doc").dir("crates/bindings").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`. + cmd!("cargo", "doc") + .dir("crates/bindings") + // Make `cargo doc` exit with error on warnings, most notably broken links + .env("RUSTDOCFLAGS", "--deny warnings") + .run()?; + + Ok(()) +} diff --git a/tools/ci/commands/other-workflows/Cargo.toml b/tools/ci/commands/other-workflows/Cargo.toml new file mode 100644 index 00000000000..543f7702f66 --- /dev/null +++ b/tools/ci/commands/other-workflows/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ci-other-workflows" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true +regex.workspace = true +reqwest = { workspace = true, features = ["blocking", "json"] } +serde.workspace = true +serde_json.workspace = true +env_logger.workspace = true diff --git a/tools/ci/src/cla_assistant.rs b/tools/ci/commands/other-workflows/src/cla_assistant.rs similarity index 95% rename from tools/ci/src/cla_assistant.rs rename to tools/ci/commands/other-workflows/src/cla_assistant.rs index 065db03ef70..29b148be5e7 100644 --- a/tools/ci/src/cla_assistant.rs +++ b/tools/ci/commands/other-workflows/src/cla_assistant.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; const CLA_CONTEXT: &str = "license/cla"; #[derive(Subcommand)] -pub(crate) enum ClaAssistantCmd { +pub enum ClaAssistantCmd { /// Retries CLA Assistant if `license/cla` is the only remaining PR blocker. Retry(RetryArgs), @@ -20,14 +20,14 @@ pub(crate) enum ClaAssistantCmd { } #[derive(Args)] -pub(crate) struct RetryArgs { +pub struct RetryArgs { /// Pull request number to check. #[arg(long)] - pub(crate) pr_number: u64, + pub pr_number: u64, /// Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY. #[arg(long)] - pub(crate) repo: Option, + pub repo: Option, } #[derive(Args)] @@ -37,21 +37,21 @@ pub(crate) struct RetryArgs { .multiple(false) .args(["pr", "sha"]), ))] -pub(crate) struct StatusArgs { +pub struct StatusArgs { /// Pull request number whose head commit should be checked. #[arg(long)] - pub(crate) pr: Option, + pub pr: Option, /// Commit SHA to check. #[arg(long)] - pub(crate) sha: Option, + pub sha: Option, /// Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY. #[arg(long)] - pub(crate) repo: Option, + pub repo: Option, } -pub(crate) fn run(cmd: ClaAssistantCmd) -> Result<()> { +pub fn run(cmd: ClaAssistantCmd) -> Result<()> { match cmd { ClaAssistantCmd::Retry(args) => retry(args), ClaAssistantCmd::Status(args) => status(args), diff --git a/tools/ci/src/codeowners_check.rs b/tools/ci/commands/other-workflows/src/codeowners_check.rs similarity index 99% rename from tools/ci/src/codeowners_check.rs rename to tools/ci/commands/other-workflows/src/codeowners_check.rs index 006d597cf87..f456100b9dc 100644 --- a/tools/ci/src/codeowners_check.rs +++ b/tools/ci/commands/other-workflows/src/codeowners_check.rs @@ -1,3 +1,4 @@ +use crate::ensure_repo_root; use anyhow::{anyhow, bail, Context, Result}; use duct::cmd; use serde_json::Value; @@ -7,7 +8,7 @@ use std::path::{Path, PathBuf}; const REPO: &str = "clockworklabs/SpacetimeDB"; pub fn run(base_ref: &str, pr_number: u64) -> Result<()> { - super::ensure_repo_root()?; + ensure_repo_root()?; fetch_base_ref(base_ref)?; let review = ReviewStatus::fetch(pr_number)?; diff --git a/tools/ci/commands/other-workflows/src/main.rs b/tools/ci/commands/other-workflows/src/main.rs new file mode 100644 index 00000000000..9dd466e41e2 --- /dev/null +++ b/tools/ci/commands/other-workflows/src/main.rs @@ -0,0 +1,42 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use clap::{Parser, Subcommand}; +use std::path::Path; + +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} + +mod cla_assistant; +mod codeowners_check; + +#[derive(Parser)] +struct Args { + #[command(subcommand)] + cmd: OtherWorkflowsCmd, +} +#[derive(Subcommand)] +enum OtherWorkflowsCmd { + /// Checks that sensitive CODEOWNERS-controlled files have the required approvals. + CodeownersCheck { + #[arg(long)] + base_ref: String, + #[arg(long)] + pr_number: u64, + }, + /// Interacts with CLA Assistant. + ClaAssistant { + #[command(subcommand)] + cmd: cla_assistant::ClaAssistantCmd, + }, +} +fn main() -> Result<()> { + env_logger::init(); + match Args::parse().cmd { + OtherWorkflowsCmd::CodeownersCheck { base_ref, pr_number } => codeowners_check::run(&base_ref, pr_number), + OtherWorkflowsCmd::ClaAssistant { cmd } => cla_assistant::run(cmd), + } +} diff --git a/tools/ci/commands/publish-checks/Cargo.toml b/tools/ci/commands/publish-checks/Cargo.toml new file mode 100644 index 00000000000..67093422620 --- /dev/null +++ b/tools/ci/commands/publish-checks/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-publish-checks" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/publish-checks/src/main.rs b/tools/ci/commands/publish-checks/src/main.rs new file mode 100644 index 00000000000..83f6315d4b2 --- /dev/null +++ b/tools/ci/commands/publish-checks/src/main.rs @@ -0,0 +1,37 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use duct::cmd; +fn run_publish_checks() -> Result<()> { + cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; + cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; + + let crates = cmd!( + "venv/bin/python3", + "tools/find-publish-list.py", + "--recursive", + "--directories", + "--quiet", + "spacetimedb", + "spacetimedb-sdk" + ) + .read()?; + + let mut failed = Vec::new(); + for crate_dir in crates.split_whitespace() { + if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { + eprintln!("crate publish checks failed for {crate_dir}: {err}"); + failed.push(crate_dir.to_string()); + } + } + + if !failed.is_empty() { + bail!("crate publish checks failed for: {}", failed.join(", ")); + } + + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + run_publish_checks() +} diff --git a/tools/ci/commands/self-docs/Cargo.toml b/tools/ci/commands/self-docs/Cargo.toml new file mode 100644 index 00000000000..382aed26d67 --- /dev/null +++ b/tools/ci/commands/self-docs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-self-docs" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true diff --git a/tools/ci/commands/self-docs/src/main.rs b/tools/ci/commands/self-docs/src/main.rs new file mode 100644 index 00000000000..90cc263baf4 --- /dev/null +++ b/tools/ci/commands/self-docs/src/main.rs @@ -0,0 +1,137 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use clap::Parser; +use duct::cmd; +use std::fs; +use std::path::Path; + +#[derive(Parser)] +struct Args { + #[arg( + long, + default_value_t = false, + long_help = "Only check for changes, do not generate the docs" + )] + check: bool, +} +struct CommandDoc { + name: &'static str, + package: &'static str, + help_safe: bool, +} + +const COMMANDS: &[CommandDoc] = &[ + CommandDoc { + name: "test", + package: "ci-test", + help_safe: false, + }, + CommandDoc { + name: "lint", + package: "ci-lint", + help_safe: false, + }, + CommandDoc { + name: "wasm-bindings", + package: "ci-wasm-bindings", + help_safe: false, + }, + CommandDoc { + name: "dlls", + package: "ci-dlls", + help_safe: false, + }, + CommandDoc { + name: "smoketests", + package: "ci-smoketests", + help_safe: true, + }, + CommandDoc { + name: "keynote-bench", + package: "ci-keynote-bench", + help_safe: false, + }, + CommandDoc { + name: "update-flow", + package: "ci-update-flow", + help_safe: true, + }, + CommandDoc { + name: "cli-docs", + package: "ci-cli-docs", + help_safe: true, + }, + CommandDoc { + name: "self-docs", + package: "ci-self-docs", + help_safe: true, + }, + CommandDoc { + name: "global-json-policy", + package: "ci-global-json-policy", + help_safe: false, + }, + CommandDoc { + name: "publish-checks", + package: "ci-publish-checks", + help_safe: false, + }, + CommandDoc { + name: "typescript-test", + package: "ci-typescript-test", + help_safe: false, + }, + CommandDoc { + name: "version-upgrade-check", + package: "ci-version-upgrade-check", + help_safe: false, + }, + CommandDoc { + name: "docs", + package: "ci-docs-build", + help_safe: false, + }, + CommandDoc { + name: "other-workflows", + package: "ci-other-workflows", + help_safe: true, + }, +]; + +fn normalize_help(help: &str) -> String { + help.lines().map(str::trim_end).collect::>().join("\n") +} + +fn command_usage(command: &CommandDoc) -> Result { + if command.help_safe { + let help = cmd!("cargo", "run", "--quiet", "--package", command.package, "--", "--help").read()?; + return Ok(normalize_help(&help)); + } + Ok(format!("cargo ci {}", command.name)) +} +fn generate() -> Result { + let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); + for command in COMMANDS { + out.push_str(&format!( + "### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", + command.name, + command_usage(command)?.trim(), + )); + } + out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); + Ok(out) +} +fn main() -> Result<()> { + let args = Args::parse(); + let readme = generate()?; + let path = Path::new("tools/ci/README.md"); + if args.check { + let existing = fs::read_to_string(path).unwrap_or_default(); + if existing != readme { + bail!("README.md is out of date. Please run `cargo ci self-docs` to update it."); + } + } else { + fs::write(path, readme)?; + } + Ok(()) +} diff --git a/tools/ci/commands/smoketests/Cargo.toml b/tools/ci/commands/smoketests/Cargo.toml new file mode 100644 index 00000000000..3fe57943e37 --- /dev/null +++ b/tools/ci/commands/smoketests/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ci-smoketests" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +clap.workspace = true +tempfile.workspace = true +spacetimedb-guard.workspace = true +env_logger.workspace = true diff --git a/tools/ci/src/smoketest.rs b/tools/ci/commands/smoketests/src/main.rs similarity index 96% rename from tools/ci/src/smoketest.rs rename to tools/ci/commands/smoketests/src/main.rs index 2a35b91ca59..7e271a0c95d 100644 --- a/tools/ci/src/smoketest.rs +++ b/tools/ci/commands/smoketests/src/main.rs @@ -1,21 +1,26 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, ensure, Context, Result}; -use clap::{Args, Subcommand}; +use clap::{Parser, Subcommand}; use duct::cmd; use spacetimedb_guard::ensure_binaries_built; use std::ffi::OsStr; use std::path::Path; use std::process::{Command, Stdio}; use std::{env, fs}; -use tempfile::TempDir; -use crate::util; +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} +use tempfile::TempDir; -#[derive(Args)] +#[derive(Parser)] /// This command builds the binaries needed by the smoketests, then runs them. This prevents /// race conditions when running tests in parallel with nextest, where multiple test processes /// might try to build the same binaries simultaneously. -pub struct SmoketestsArgs { +struct SmoketestsArgs { #[command(subcommand)] cmd: Option, @@ -53,7 +58,7 @@ enum SmoketestCmd { CheckModList, } -pub fn run(args: SmoketestsArgs) -> Result<()> { +fn run(args: SmoketestsArgs) -> Result<()> { match args.cmd { Some(SmoketestCmd::Prepare) => { build_cli()?; @@ -292,7 +297,7 @@ fn set_env(cmd: &mut Command, server: Option, dotnet: bool, auth_host: b } fn check_smoketests_mod_rs_complete() -> Result<()> { - util::ensure_repo_root()?; + ensure_repo_root()?; let expected_dir = Path::new("crates/smoketests/tests/smoketests"); let mut expected = std::collections::BTreeSet::::new(); @@ -352,3 +357,8 @@ fn check_smoketests_mod_rs_complete() -> Result<()> { Ok(()) } + +fn main() -> Result<()> { + env_logger::init(); + run(SmoketestsArgs::parse()) +} diff --git a/tools/ci/commands/test/Cargo.toml b/tools/ci/commands/test/Cargo.toml new file mode 100644 index 00000000000..e714cc869fc --- /dev/null +++ b/tools/ci/commands/test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-test" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/test/src/main.rs b/tools/ci/commands/test/src/main.rs new file mode 100644 index 00000000000..117d937e4ae --- /dev/null +++ b/tools/ci/commands/test/src/main.rs @@ -0,0 +1,131 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use duct::{cmd, Expression}; +use std::ffi::OsStr; +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} +fn main() -> Result<()> { + env_logger::init(); + pnpm(["build"]).dir("crates/bindings-typescript").run()?; + + // TODO: This doesn't work on at least user Linux machines, because something here apparently uses `sudo`? + + // Exclude smoketests from `cargo test --all` since they require pre-built binaries. + // Smoketests have their own dedicated command: `cargo ci smoketests` + cmd!( + "cargo", + "test", + "--all", + "--exclude", + "spacetimedb-smoketests", + "--exclude", + "spacetimedb-sdk", + "--exclude", + "spacetimedb", + "--", + "--test-threads=2", + "--skip", + "unreal" + ) + .run()?; + // Bindings snapshot tests rely on the unstable feature, + // as they compile and test APIs which are gated behind that feature, + // e.g. procedures, HTTP handlers. + cmd!( + "cargo", + "test", + "-p", + "spacetimedb", + "--features", + "unstable", + "--", + "--test-threads=2", + ) + .run()?; + // The SDK test harness uses the same child-process server guard as smoketests, + // which expects release CLI/standalone binaries to already exist. + cmd!( + "cargo", + "build", + "--release", + "-p", + "spacetimedb-cli", + "-p", + "spacetimedb-standalone", + "--features", + "spacetimedb-standalone/allow_loopback_http_for_tests", + ) + .run()?; + // SDK procedure tests intentionally make localhost HTTP requests. + cmd!( + "cargo", + "test", + "-p", + "spacetimedb-sdk", + "--features", + "allow_loopback_http_for_tests", + "--", + "--test-threads=2", + "--skip", + "unreal" + ) + .run()?; + // Run the same SDK suite against wasm/browser test clients. + cmd!( + "cargo", + "test", + "-p", + "spacetimedb-sdk", + "--features", + "allow_loopback_http_for_tests,browser", + "--", + "--test-threads=2", + "--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. + + // The fallocate tests have been flakely when running in parallel + 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()?; + + Ok(()) +} diff --git a/tools/ci/commands/typescript-test/Cargo.toml b/tools/ci/commands/typescript-test/Cargo.toml new file mode 100644 index 00000000000..565466bcbe2 --- /dev/null +++ b/tools/ci/commands/typescript-test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-typescript-test" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs new file mode 100644 index 00000000000..74dbe2c7770 --- /dev/null +++ b/tools/ci/commands/typescript-test/src/main.rs @@ -0,0 +1,45 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use duct::{cmd, Expression}; +use std::ffi::OsStr; +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} +fn run_typescript_tests() -> Result<()> { + pnpm(["build"]).dir("crates/bindings-typescript").run()?; + pnpm(["test"]).dir("crates/bindings-typescript").run()?; + pnpm(["generate"]).dir("templates/chat-react-ts").run()?; + let diff_status = cmd!( + "bash", + "tools/check-diff.sh", + "templates/chat-react-ts/src/module_bindings" + ) + .run()?; + if !diff_status.status.success() { + bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); + } + pnpm(["build"]).dir("templates/chat-react-ts").run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("templates") + .run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("crates/bindings-typescript") + .run()?; + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + run_typescript_tests() +} diff --git a/tools/ci/commands/update-flow/Cargo.toml b/tools/ci/commands/update-flow/Cargo.toml new file mode 100644 index 00000000000..e7ce2e6b461 --- /dev/null +++ b/tools/ci/commands/update-flow/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ci-update-flow" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +tempfile.workspace = true +clap.workspace = true +log.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/update-flow/src/main.rs b/tools/ci/commands/update-flow/src/main.rs new file mode 100644 index 00000000000..8a809c42feb --- /dev/null +++ b/tools/ci/commands/update-flow/src/main.rs @@ -0,0 +1,69 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use clap::Parser; +use duct::cmd; + +#[derive(Parser)] +struct Args { + #[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 = "false", + long_help = "Whether to enable github token authentication feature when building the update binary. By default this is disabled." + )] + github_token_auth: bool, +} + +fn main() -> Result<()> { + env_logger::init(); + let Args { + target, + github_token_auth, + } = Args::parse(); + let mut common_args = vec![]; + if let Some(target) = target.as_ref() { + common_args.push("--target"); + common_args.push(target); + log::info!("checking update flow for target: {target}"); + } else { + log::info!("checking update flow"); + } + if github_token_auth { + common_args.push("--features"); + common_args.push("github-token-auth"); + } + + cmd( + "cargo", + ["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`. + // 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_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_arg, "--yes"].into_iter()), + ) + .run()?; + + let mut spacetime_path = root_dir.path().join("spacetime"); + if !std::env::consts::EXE_EXTENSION.is_empty() { + spacetime_path.set_extension(std::env::consts::EXE_EXTENSION); + } + cmd(spacetime_path, [&root_arg, "help"]).run()?; + + Ok(()) +} diff --git a/tools/ci/commands/version-upgrade-check/Cargo.toml b/tools/ci/commands/version-upgrade-check/Cargo.toml new file mode 100644 index 00000000000..68dec2068e4 --- /dev/null +++ b/tools/ci/commands/version-upgrade-check/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-version-upgrade-check" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/version-upgrade-check/src/main.rs b/tools/ci/commands/version-upgrade-check/src/main.rs new file mode 100644 index 00000000000..fed14c6bed3 --- /dev/null +++ b/tools/ci/commands/version-upgrade-check/src/main.rs @@ -0,0 +1,22 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use duct::cmd; +fn run_version_upgrade_check() -> Result<()> { + cmd!( + "cargo", + "bump-versions", + "123.456.789", + "--rust-and-cli", + "--csharp", + "--typescript", + "--cpp", + "--accept-snapshots" + ) + .run()?; + Ok(()) +} + +fn main() -> Result<()> { + env_logger::init(); + run_version_upgrade_check() +} diff --git a/tools/ci/commands/wasm-bindings/Cargo.toml b/tools/ci/commands/wasm-bindings/Cargo.toml new file mode 100644 index 00000000000..be9456b7754 --- /dev/null +++ b/tools/ci/commands/wasm-bindings/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-wasm-bindings" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +env_logger.workspace = true diff --git a/tools/ci/commands/wasm-bindings/src/main.rs b/tools/ci/commands/wasm-bindings/src/main.rs new file mode 100644 index 00000000000..1f708d7341e --- /dev/null +++ b/tools/ci/commands/wasm-bindings/src/main.rs @@ -0,0 +1,50 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::Result; +use duct::{cmd, Expression}; +use std::ffi::OsStr; +fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} +use std::path::Path; +fn main() -> Result<()> { + env_logger::init(); + pnpm([ + "install", + "--filter", + "./crates/bindings-typescript...", + "--filter", + "./modules/module-test-ts...", + ]) + .run()?; + pnpm(["build"]).dir("crates/bindings-typescript").run()?; + cmd!("cargo", "test", "-p", "spacetimedb-codegen").run()?; + // Pre-build the CLI so that it _doesn't_ get `cargo update`d, since that may break the build. + cmd!("cargo", "build", "-p", "spacetimedb-cli").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 + cmd!("cargo", "update").run()?; + let cli_path = Path::new(env!("CARGO_MANIFEST_DIR")) + .ancestors() + .nth(2) + .unwrap() + .join("target/debug/spacetimedb-cli") + .with_extension(std::env::consts::EXE_EXTENSION); + cmd!(cli_path, "build", "--module-path", "modules/module-test",).run()?; + + Ok(()) +} diff --git a/tools/ci/common/Cargo.toml b/tools/ci/common/Cargo.toml new file mode 100644 index 00000000000..f4e1d4195e4 --- /dev/null +++ b/tools/ci/common/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "ci-common" +version = "0.1.0" +edition.workspace = true + +[dependencies] diff --git a/tools/ci/common/src/lib.rs b/tools/ci/common/src/lib.rs new file mode 100644 index 00000000000..cdb99009889 --- /dev/null +++ b/tools/ci/common/src/lib.rs @@ -0,0 +1,10 @@ +use std::path::PathBuf; + +pub fn repo_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .and_then(|p| p.parent()) + .and_then(|p| p.parent()) + .expect("failed to find repo root") + .to_path_buf() +} diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs deleted file mode 100644 index 3fadcdaf0cc..00000000000 --- a/tools/ci/src/ci_docs.rs +++ /dev/null @@ -1,85 +0,0 @@ -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); - - 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 mut 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()); - if let Some(value_names) = arg.get_value_names().filter(|_| arg.get_action().takes_values()) { - for value_name in value_names { - names.push_str(&format!(" <{value_name}>")); - } - } - let help = arg - .get_long_help() - .or_else(|| arg.get_help()) - .map(|help| help.to_string()) - .unwrap_or_else(|| { - eprintln!("Warning: argument `{}` is missing help text", arg.get_id()); - "".to_string() - }); - if help.is_empty() { - options.push_str(&format!("- `{names}`:\n")); - } else { - options.push_str(&format!( - "- `{}`: {}\n{}", - names, - help, - if help.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 41d91e19a8d..a41a1bb3d02 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,907 +1,122 @@ #![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use duct::cmd; -use anyhow::{bail, Context, Result}; -use clap::{CommandFactory, Parser, Subcommand}; -use duct::{cmd, Expression}; -use serde_json::Value; -use std::collections::BTreeSet; -use std::ffi::OsString; -use std::path::Path; -use std::path::PathBuf; -use std::{env, fs}; - -const README_PATH: &str = "tools/ci/README.md"; - -mod ci_docs; -mod cla_assistant; -mod codeowners_check; -mod keynote_bench; -mod smoketest; -mod util; - -use util::ensure_repo_root; - -/// On Windows, `pnpm` is installed as a `.cmd` shim which `CreateProcess` cannot -/// find without going through the shell. Wrapping with `cmd /c` fixes this. -/// On Unix, we invoke `pnpm` directly. -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} - -/// 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 = "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, default_value = "other-workflows")] - skip: Vec, -} - -fn check_global_json_policy() -> Result<()> { - ensure_repo_root()?; - - let root_json = Path::new("global.json"); - let root_contents = fs::read_to_string(root_json)?; - - let globals = git_tracked_files(":(glob)**/global.json")?; - - let mut ok = true; - for p in globals { - let meta = fs::symlink_metadata(&p)?; - let is_symlink = meta.file_type().is_symlink(); - let is_template_global_json = p.strip_prefix(".").unwrap_or(&p).starts_with(Path::new("templates")); - if is_template_global_json && is_symlink { - eprintln!( - "Error: {} is a symlink. Template files must not be symlinks; they are copied literally and this will break if the CLI is built under Windows where symlinks are not supported.", - p.display() - ); - ok = false; - } - - let contents = fs::read_to_string(&p)?; - if contents != root_contents { - eprintln!("Error: {} does not match the root global.json contents", p.display()); - ok = false; - } else if !is_template_global_json || !is_symlink { - println!("OK: {}", p.display()); - } - } - - if !ok { - bail!("global.json policy check failed"); - } - - Ok(()) +struct Command { + name: &'static str, + package: &'static str, } - -fn package_json_pnpm_version(package_manager: &str) -> Option<&str> { - package_manager.strip_prefix("pnpm@") -} - -fn git_tracked_files(pathspec: &str) -> Result> { - let output = cmd!("git", "ls-files", pathspec).read()?; - Ok(output.lines().map(PathBuf::from).collect()) -} - -fn package_json_string_value(package_json: &Value, key: &str) -> Option { - package_json.get(key)?.as_str().map(str::to_owned) -} - -fn package_json_engines_pnpm(package_json: &Value) -> Option { - package_json.get("engines")?.get("pnpm")?.as_str().map(str::to_owned) -} - -fn read_package_json(path: &Path) -> Result { - let contents = fs::read_to_string(path)?; - Ok(serde_json::from_str(&contents)?) -} - -fn is_npm_package_json(package_json: &Value) -> bool { - [ - "bin", - "dependencies", - "devDependencies", - "exports", - "main", - "optionalDependencies", - "packageManager", - "peerDependencies", - "scripts", - "type", - ] - .iter() - .any(|key| package_json.get(key).is_some()) -} - -fn is_template_path(path: &Path) -> bool { - path.starts_with("templates") -} - -fn minimum_release_age(path: &Path) -> Result { - let workspace = fs::read_to_string(path)?; - workspace - .lines() - .find_map(|line| { - let line = line.trim(); - let value = line.strip_prefix("minimumReleaseAge:")?.trim(); - value.parse::().ok() - }) - .ok_or_else(|| anyhow::anyhow!("{} is missing minimumReleaseAge", path.display())) -} - -fn npmrc_minimum_release_age(path: &Path, expected_minimum_release_age: u64) -> Result { - let contents = fs::read_to_string(path).map_err(|err| { - if err.kind() == std::io::ErrorKind::NotFound { - anyhow::anyhow!( - "{} is tracked but missing from the working tree. Restore it with:\nminimum-release-age={}", - path.display(), - expected_minimum_release_age - ) - } else { - anyhow::anyhow!( - "failed to read {} while checking pnpm minimum package age: {err}", - path.display() - ) - } - })?; - contents - .lines() - .find_map(|line| { - let line = line.trim(); - let value = line.strip_prefix("minimum-release-age=")?.trim(); - value.parse::().ok() - }) - .ok_or_else(|| { - anyhow::anyhow!( - "{} must contain `minimum-release-age={}` to match root pnpm-workspace.yaml", - path.display(), - expected_minimum_release_age - ) - }) -} - -fn check_pnpm_release_age_policy() -> Result<()> { - ensure_repo_root()?; - - let root_package_json_path = Path::new("package.json"); - let root_package_json = read_package_json(root_package_json_path)?; - let package_manager = package_json_string_value(&root_package_json, "packageManager") - .ok_or_else(|| anyhow::anyhow!("package.json is missing packageManager"))?; - let package_manager_version = package_json_pnpm_version(&package_manager) - .ok_or_else(|| anyhow::anyhow!("packageManager must be pnpm@, found {package_manager:?}"))?; - - let expected_engine_pnpm = format!(">={package_manager_version}"); - let engine_pnpm = package_json_engines_pnpm(&root_package_json) - .ok_or_else(|| anyhow::anyhow!("package.json engines is missing pnpm"))?; - if engine_pnpm != expected_engine_pnpm { - bail!("package.json engines.pnpm must be {expected_engine_pnpm:?}, found {engine_pnpm:?}"); - } - - for package_json_path in git_tracked_files(":(glob)**/package.json")? { - let package_json = read_package_json(&package_json_path)?; - let Some(found_package_manager) = package_json_string_value(&package_json, "packageManager") else { - continue; - }; - if found_package_manager != package_manager { - bail!( - "{} packageManager must match root package.json: expected {:?}, found {:?}", - package_json_path.display(), - package_manager, - found_package_manager - ); - } - } - - let root_workspace_path = Path::new("pnpm-workspace.yaml"); - let root_minimum_release_age = minimum_release_age(root_workspace_path)?; - for workspace_path in git_tracked_files(":(glob)**/pnpm-workspace.yaml")? { - let found_minimum_release_age = minimum_release_age(&workspace_path)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimumReleaseAge must match root pnpm-workspace.yaml: expected {}, found {}", - workspace_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for npmrc_path in git_tracked_files(":(glob)**/.npmrc")? { - // Template package roots are copied into projects created by `spacetime init`. - // They must not embed this repo's package-age policy; smoketests enforce it - // at the pnpm process boundary instead. - if is_template_path(&npmrc_path) { - continue; - } - let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", - npmrc_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for package_json_path in git_tracked_files(":(glob)**/package.json")? { - // Template package roots are copied into projects created by `spacetime init`. - // They must not require adjacent .npmrc files for this repo's package-age - // policy; smoketests enforce it at the pnpm process boundary instead. - if is_template_path(&package_json_path) { - continue; - } - let package_json = read_package_json(&package_json_path)?; - if !is_npm_package_json(&package_json) { - continue; - } - let package_dir = package_json_path - .parent() - .expect("git-tracked package.json path should have a parent"); - let npmrc_path = package_dir.join(".npmrc"); - if !npmrc_path.is_file() { - bail!( - "{} is required because {} is an npm/pnpm package manifest.\nAdd {} containing:\nminimum-release-age={}", - npmrc_path.display(), - package_json_path.display(), - npmrc_path.display(), - root_minimum_release_age - ); - } - let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", - npmrc_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for workflow_path in git_tracked_files(".github/workflows/*")? { - let contents = fs::read_to_string(&workflow_path)?; - if contents.contains("pnpm/action-setup@v4") { - bail!( - "{} must use ./.github/actions/setup-pnpm instead of pnpm/action-setup@v4", - workflow_path.display() - ); - } - } - - Ok(()) -} - -/// Codex plugin ships a copy of `skills/`, because plugin installers do not follow symlinks, -/// this checks if the copy is in sync -fn check_codex_plugin_skills_sync() -> Result<()> { - let source = Path::new("skills"); - let copy = Path::new("codex-plugin/plugins/spacetimedb/skills"); - - if fs::symlink_metadata(copy) - .with_context(|| format!("reading {}", copy.display()))? - .file_type() - .is_symlink() - { - bail!( - "{} must be a real directory, not a symlink, plugin installers do not follow symlinks", - copy.display() - ); - } - - fn walk(root: &Path, dir: &Path, out: &mut BTreeSet) -> Result<()> { - for entry in fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { - let entry = entry?; - if entry.file_type()?.is_dir() { - walk(root, &entry.path(), out)?; - } else { - let rel = entry - .path() - .strip_prefix(root) - .expect("walked path should be under its root") - .to_path_buf(); - out.insert(rel); - } - } - Ok(()) - } - - let mut source_files = BTreeSet::new(); - walk(source, source, &mut source_files)?; - let mut copy_files = BTreeSet::new(); - walk(copy, copy, &mut copy_files)?; - - let mut drift = Vec::new(); - for rel in &source_files { - if !copy_files.contains(rel) { - drift.push(format!("missing from copy: {}", rel.display())); - } else if fs::read(source.join(rel))? != fs::read(copy.join(rel))? { - drift.push(format!("differs: {}", rel.display())); - } - } - for rel in ©_files { - if !source_files.contains(rel) { - drift.push(format!("extraneous in copy: {}", rel.display())); - } - } - - if !drift.is_empty() { - bail!( - "codex-plugin skills copy is out of sync with skills/:\n {}\nRun: node codex-plugin/scripts/check-skills-sync.ts --fix", - drift.join("\n ") - ); - } - Ok(()) -} - -#[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 the codebase - /// - /// Runs rustfmt, clippy, csharpier, TypeScript lint, 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, - /// Deprecated; use `cargo regen csharp dlls`. - /// - /// Builds and packs C# DLLs and NuGet packages for local Unity workflows. - Dlls, - /// Runs smoketests - /// - /// Executes the smoketests suite with some default exclusions. - Smoketests(smoketest::SmoketestsArgs), - /// Runs the keynote benchmark as a CI performance regression gate. - /// - /// Assumes release SpacetimeDB binaries and the TypeScript SDK are already built, runs the - /// keynote SpacetimeDB benchmark for 60 seconds against the TypeScript and Rust modules, and - /// fails if throughput is below 275K TPS for TypeScript or 300K TPS for Rust. - KeynoteBench, - /// 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, - 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 = "false", - long_help = "Whether to enable github token authentication feature when building the update binary. By default this is disabled." - )] - github_token_auth: bool, +const COMMANDS: &[Command] = &[ + Command { + name: "test", + package: "ci-test", }, - /// Generates CLI documentation and checks for changes - CliDocs { - #[arg( - long, - long_help = "specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is located)" - )] - spacetime_path: Option, + Command { + name: "lint", + package: "ci-lint", }, - SelfDocs { - #[arg( - long, - default_value_t = false, - long_help = "Only check for changes, do not generate the docs" - )] - check: bool, + Command { + name: "wasm-bindings", + package: "ci-wasm-bindings", }, - - /// Verify that any non-root global.json files are symlinks to the root global.json. - GlobalJsonPolicy, - /// Checks that publishable crates satisfy publish constraints. - PublishChecks, - /// Runs TypeScript workspace tests and template build checks. - TypescriptTest, - /// Verifies that the repository version upgrade tool still works. - VersionUpgradeCheck, - /// Builds the docs site. - Docs, - /// Workflows should leave here if they should not be run as part of a no-subcommand invocation of `cargo ci`. - OtherWorkflows { - #[command(subcommand)] - cmd: OtherWorkflowsCmd, + Command { + name: "dlls", + package: "ci-dlls", }, -} - -#[derive(Subcommand)] -enum OtherWorkflowsCmd { - /// Checks that sensitive CODEOWNERS-controlled files have the required approvals. - CodeownersCheck { - /// Git ref to compare against, usually origin/. - #[arg(long)] - base_ref: String, - /// Pull request number to inspect for approval state. - #[arg(long)] - pr_number: u64, + Command { + name: "smoketests", + package: "ci-smoketests", }, - /// Interacts with CLA Assistant. - ClaAssistant { - #[command(subcommand)] - cmd: cla_assistant::ClaAssistantCmd, + Command { + name: "keynote-bench", + package: "ci-keynote-bench", }, -} - -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}"); - cmd!("cargo", "ci", &subcmd).run()?; - } - Ok(()) -} - -fn tracked_rs_files_under(path: &str) -> Result> { - let output = cmd!("git", "ls-files", "--", path).read()?; - Ok(output - .lines() - .filter(|line| line.ends_with(".rs")) - .map(PathBuf::from) - .collect()) -} - -fn run_publish_checks() -> Result<()> { - cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; - cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; - - let crates = cmd!( - "venv/bin/python3", - "tools/find-publish-list.py", - "--recursive", - "--directories", - "--quiet", - "spacetimedb", - "spacetimedb-sdk" - ) - .read()?; - - let mut failed = Vec::new(); - for crate_dir in crates.split_whitespace() { - if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { - eprintln!("crate publish checks failed for {crate_dir}: {err}"); - failed.push(crate_dir.to_string()); - } - } - - if !failed.is_empty() { - bail!("crate publish checks failed for: {}", failed.join(", ")); - } - - Ok(()) -} + Command { + name: "update-flow", + package: "ci-update-flow", + }, + Command { + name: "cli-docs", + package: "ci-cli-docs", + }, + Command { + name: "self-docs", + package: "ci-self-docs", + }, + Command { + name: "global-json-policy", + package: "ci-global-json-policy", + }, + Command { + name: "publish-checks", + package: "ci-publish-checks", + }, + Command { + name: "typescript-test", + package: "ci-typescript-test", + }, + Command { + name: "version-upgrade-check", + package: "ci-version-upgrade-check", + }, + Command { + name: "docs", + package: "ci-docs-build", + }, + Command { + name: "other-workflows", + package: "ci-other-workflows", + }, +]; +const DEFAULT_SKIP: &[&str] = &["other-workflows"]; -fn run_typescript_tests() -> Result<()> { - pnpm(["build"]).dir("crates/bindings-typescript").run()?; - pnpm(["test"]).dir("crates/bindings-typescript").run()?; - pnpm(["generate"]).dir("templates/chat-react-ts").run()?; - let diff_status = cmd!( - "bash", - "tools/check-diff.sh", - "templates/chat-react-ts/src/module_bindings" - ) - .run()?; - if !diff_status.status.success() { - bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); +fn print_help() { + println!("Usage: cargo ci [--skip ] [COMMAND] [ARGS]..."); + println!(); + println!("Commands:"); + for command in COMMANDS { + println!(" {}", command.name); } - pnpm(["build"]).dir("templates/chat-react-ts").run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("templates") - .run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("crates/bindings-typescript") - .run()?; - Ok(()) } - -fn run_docs_build() -> Result<()> { - pnpm(["install"]).dir("docs").run()?; - pnpm(["build"]).dir("docs").run()?; - Ok(()) +fn command_for(name: &str) -> Option<&'static Command> { + COMMANDS.iter().find(|candidate| candidate.name == name) } - -fn run_version_upgrade_check() -> Result<()> { - cmd!( - "cargo", - "bump-versions", - "123.456.789", - "--rust-and-cli", - "--csharp", - "--typescript", - "--cpp", - "--accept-snapshots" - ) - .run()?; +fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { + let mut cargo_args = vec!["run", "--package", command.package, "--"]; + cargo_args.extend(forwarded.iter().map(String::as_str)); + cmd("cargo", cargo_args).run()?; Ok(()) } - fn main() -> Result<()> { - env_logger::init(); - - let cli = Cli::parse(); - - match cli.cmd { - Some(CiCmd::Test) => { - pnpm(["build"]).dir("crates/bindings-typescript").run()?; - - // TODO: This doesn't work on at least user Linux machines, because something here apparently uses `sudo`? - - // Exclude smoketests from `cargo test --all` since they require pre-built binaries. - // Smoketests have their own dedicated command: `cargo ci smoketests` - cmd!( - "cargo", - "test", - "--all", - "--exclude", - "spacetimedb-smoketests", - "--exclude", - "spacetimedb-sdk", - "--exclude", - "spacetimedb", - "--", - "--test-threads=2", - "--skip", - "unreal" - ) - .run()?; - // Bindings snapshot tests rely on the unstable feature, - // as they compile and test APIs which are gated behind that feature, - // e.g. procedures, HTTP handlers. - cmd!( - "cargo", - "test", - "-p", - "spacetimedb", - "--features", - "unstable", - "--", - "--test-threads=2", - ) - .run()?; - // The SDK test harness uses the same child-process server guard as smoketests, - // which expects release CLI/standalone binaries to already exist. - cmd!( - "cargo", - "build", - "--release", - "-p", - "spacetimedb-cli", - "-p", - "spacetimedb-standalone", - "--features", - "spacetimedb-standalone/allow_loopback_http_for_tests", - ) - .run()?; - // SDK procedure tests intentionally make localhost HTTP requests. - cmd!( - "cargo", - "test", - "-p", - "spacetimedb-sdk", - "--features", - "allow_loopback_http_for_tests", - "--", - "--test-threads=2", - "--skip", - "unreal" - ) - .run()?; - // Run the same SDK suite against wasm/browser test clients. - cmd!( - "cargo", - "test", - "-p", - "spacetimedb-sdk", - "--features", - "allow_loopback_http_for_tests,browser", - "--", - "--test-threads=2", - "--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. - - // The fallocate tests have been flakely when running in parallel - 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) => { - ensure_repo_root()?; - check_pnpm_release_age_policy()?; - check_codex_plugin_skills_sync()?; - // `cargo fmt --all` only checks files that Cargo discovers through workspace/package targets. - // However, we also keep Rust sources in a locations that are tracked but not part of our workspace, - // so this approach properly catches all the files, where `cargo fmt` does not. - let mut files = Vec::new(); - files.extend(tracked_rs_files_under(".")?); - const RUSTFMT_BATCH_SIZE: usize = 200; - for batch in files.chunks(RUSTFMT_BATCH_SIZE) { - let mut args = Vec::::with_capacity(batch.len() + 1); - args.push("--check".into()); - args.extend(batch.iter().map(|path| path.as_os_str().to_os_string())); - cmd("rustfmt", args).run()?; - } - cmd!( - "cargo", - "clippy", - "--timings", - "--all", - "--tests", - "--benches", - "--", - "-D", - "warnings", - ) - .run()?; - cmd!( - "cargo", - "clippy", - "--timings", - "--no-default-features", - "--features=browser", - "-pspacetimedb-sdk", - "--tests", - "--benches", - "--", - "-D", - "warnings", - ) - .run()?; - cmd!("dotnet", "tool", "restore").dir("crates/bindings-csharp").run()?; - cmd!("dotnet", "csharpier", "--check", ".") - .dir("crates/bindings-csharp") - .run()?; - pnpm(["lint"]).run()?; - cmd!("cargo", "test", "--doc", "--target", "wasm32-unknown-unknown") - .dir("crates/bindings") - .run()?; - cmd!("cargo", "test", "--doc").dir("crates/bindings").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`. - cmd!("cargo", "doc") - .dir("crates/bindings") - // Make `cargo doc` exit with error on warnings, most notably broken links - .env("RUSTDOCFLAGS", "--deny warnings") - .run()?; - } - - Some(CiCmd::WasmBindings) => { - pnpm([ - "install", - "--filter", - "./crates/bindings-typescript...", - "--filter", - "./modules/module-test-ts...", - ]) - .run()?; - pnpm(["build"]).dir("crates/bindings-typescript").run()?; - cmd!("cargo", "test", "-p", "spacetimedb-codegen").run()?; - // Pre-build the CLI so that it _doesn't_ get `cargo update`d, since that may break the build. - cmd!("cargo", "build", "-p", "spacetimedb-cli").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 - cmd!("cargo", "update").run()?; - let cli_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(2) - .unwrap() - .join("target/debug/spacetimedb-cli") - .with_extension(std::env::consts::EXE_EXTENSION); - cmd!(cli_path, "build", "--module-path", "modules/module-test",).run()?; - } - - Some(CiCmd::Dlls) => { - eprintln!("warning: `cargo ci dlls` is deprecated; use `cargo regen csharp dlls` instead"); - cmd!("cargo", "regen", "csharp", "dlls").run()?; - } - - Some(CiCmd::Smoketests(args)) => { - ensure_repo_root()?; - smoketest::run(args)?; - } - - Some(CiCmd::KeynoteBench) => { - ensure_repo_root()?; - keynote_bench::run()?; - } - - Some(CiCmd::UpdateFlow { - target, - github_token_auth, - }) => { - let mut common_args = vec![]; - if let Some(target) = target.as_ref() { - common_args.push("--target"); - common_args.push(target); - log::info!("checking update flow for target: {target}"); - } else { - log::info!("checking update flow"); - } - if github_token_auth { - common_args.push("--features"); - common_args.push("github-token-auth"); - } - - cmd( - "cargo", - ["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`. - // 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_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_arg, "--yes"].into_iter()), - ) - .run()?; - - let mut spacetime_path = root_dir.path().join("spacetime"); - if !std::env::consts::EXE_EXTENSION.is_empty() { - spacetime_path.set_extension(std::env::consts::EXE_EXTENSION); - } - cmd(spacetime_path, [&root_arg, "help"]).run()?; - } - - 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" - ); - } - - pnpm(["install", "--recursive"]).run()?; - pnpm(["generate-cli-docs"]).dir("docs").run()?; - let out = cmd!("git", "status", "--porcelain", "--", "docs").read()?; - if out.is_empty() { - log::info!("No docs changes detected"); - } else { - anyhow::bail!("CLI docs are out of date:\n{out}"); + let mut raw = std::env::args().skip(1).collect::>(); + if raw.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + print_help(); + return Ok(()); + } + let mut skips = DEFAULT_SKIP.iter().map(|s| s.to_string()).collect::>(); + let mut i = 0; + while i < raw.len() { + if raw[i] == "--skip" { + if i + 1 >= raw.len() { + bail!("--skip requires a command name"); } + skips.push(raw.remove(i + 1)); + raw.remove(i); + } else { + i += 1; } - - 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()); + } + if raw.is_empty() { + for command in COMMANDS { + if !skips.iter().any(|skip| skip == command.name) { + run_command(command, &[])?; } } - - Some(CiCmd::GlobalJsonPolicy) => { - check_global_json_policy()?; - } - - Some(CiCmd::OtherWorkflows { - cmd: OtherWorkflowsCmd::CodeownersCheck { base_ref, pr_number }, - }) => { - codeowners_check::run(&base_ref, pr_number)?; - } - - Some(CiCmd::PublishChecks) => { - run_publish_checks()?; - } - - Some(CiCmd::TypescriptTest) => { - run_typescript_tests()?; - } - - Some(CiCmd::VersionUpgradeCheck) => { - run_version_upgrade_check()?; - } - - Some(CiCmd::Docs) => { - run_docs_build()?; - } - - Some(CiCmd::OtherWorkflows { - cmd: OtherWorkflowsCmd::ClaAssistant { cmd }, - }) => { - cla_assistant::run(cmd)?; - } - - None => run_all_clap_subcommands(&cli.skip)?, + return Ok(()); } - - Ok(()) + let command_name = raw.remove(0); + let Some(command) = command_for(&command_name) else { + bail!("unknown cargo ci command `{command_name}`"); + }; + run_command(command, &raw) } diff --git a/tools/ci/src/util.rs b/tools/ci/src/util.rs deleted file mode 100644 index ac35a7a465b..00000000000 --- a/tools/ci/src/util.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![allow(clippy::disallowed_macros)] - -use anyhow::{bail, Result}; -use std::path::Path; - -pub fn ensure_repo_root() -> Result<()> { - if !Path::new("Cargo.toml").exists() { - bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); - } - Ok(()) -} From 0272d77f8ddbd950d24a530fe5d13fbea3b33087 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 7 Aug 2026 13:06:52 -0400 Subject: [PATCH 02/37] Fix wasm bindings CI command path --- Cargo.lock | 1 + tools/ci/commands/wasm-bindings/Cargo.toml | 1 + tools/ci/commands/wasm-bindings/src/main.rs | 6 +----- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f63db3e9e7f..2578284b603 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1061,6 +1061,7 @@ name = "ci-wasm-bindings" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "env_logger 0.10.2", ] diff --git a/tools/ci/commands/wasm-bindings/Cargo.toml b/tools/ci/commands/wasm-bindings/Cargo.toml index be9456b7754..de3fd439244 100644 --- a/tools/ci/commands/wasm-bindings/Cargo.toml +++ b/tools/ci/commands/wasm-bindings/Cargo.toml @@ -7,3 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/wasm-bindings/src/main.rs b/tools/ci/commands/wasm-bindings/src/main.rs index 1f708d7341e..96a803e55ba 100644 --- a/tools/ci/commands/wasm-bindings/src/main.rs +++ b/tools/ci/commands/wasm-bindings/src/main.rs @@ -16,7 +16,6 @@ where cmd("pnpm", args) } } -use std::path::Path; fn main() -> Result<()> { env_logger::init(); pnpm([ @@ -38,10 +37,7 @@ fn main() -> Result<()> { // // For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714 cmd!("cargo", "update").run()?; - let cli_path = Path::new(env!("CARGO_MANIFEST_DIR")) - .ancestors() - .nth(2) - .unwrap() + let cli_path = ci_common::repo_root() .join("target/debug/spacetimedb-cli") .with_extension(std::env::consts::EXE_EXTENSION); cmd!(cli_path, "build", "--module-path", "modules/module-test",).run()?; From 2c10c7605b0b597a6fe8dd79dbaecdb7cfb7608b Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 7 Aug 2026 13:37:59 -0400 Subject: [PATCH 03/37] Replace gh run watch in Internal Tests --- .github/workflows/internal-tests.yml | 13 +-- tools/ci/README.md | 1 + tools/ci/commands/other-workflows/src/main.rs | 4 + .../ci/commands/other-workflows/src/watch.rs | 99 +++++++++++++++++++ 4 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 tools/ci/commands/other-workflows/src/watch.rs diff --git a/.github/workflows/internal-tests.yml b/.github/workflows/internal-tests.yml index ba4cb230da6..0d38053977d 100644 --- a/.github/workflows/internal-tests.yml +++ b/.github/workflows/internal-tests.yml @@ -93,17 +93,10 @@ jobs: RUN_URL: ${{ steps.dispatch.outputs.run_url }} run: | set -euo pipefail - echo "Waiting for workflow result... ${RUN_URL}" - set +e - # Due to our limited-scope token permissions, `gh run watch` spams errors about not being able to get annotations. They look worrying but they're benign, so we filter them out. - gh run watch "$RUN_ID" \ + cargo ci other-workflows watch \ --repo "$TARGET_OWNER/$TARGET_REPO" \ - --exit-status \ - --interval 30 2>&1 \ - | grep -Fv "requesting annotations returned 403 Forbidden as the token does not have sufficient permissions" - watch_status="${PIPESTATUS[0]}" - set -e - exit "$watch_status" + --run-id "$RUN_ID" \ + --run-url "$RUN_URL" - name: Cancel invoked run if workflow cancelled if: ${{ cancelled() && steps.dispatch.outputs.run_id }} diff --git a/tools/ci/README.md b/tools/ci/README.md index ebc5a4fbbcd..9ed2edcc24b 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -181,6 +181,7 @@ Usage: ci-other-workflows Commands: codeowners-check Checks that sensitive CODEOWNERS-controlled files have the required approvals cla-assistant Interacts with CLA Assistant + watch Waits for a GitHub Actions workflow run to complete help Print this message or the help of the given subcommand(s) Options: diff --git a/tools/ci/commands/other-workflows/src/main.rs b/tools/ci/commands/other-workflows/src/main.rs index 9dd466e41e2..3b61e44f0e3 100644 --- a/tools/ci/commands/other-workflows/src/main.rs +++ b/tools/ci/commands/other-workflows/src/main.rs @@ -12,6 +12,7 @@ fn ensure_repo_root() -> Result<()> { mod cla_assistant; mod codeowners_check; +mod watch; #[derive(Parser)] struct Args { @@ -32,11 +33,14 @@ enum OtherWorkflowsCmd { #[command(subcommand)] cmd: cla_assistant::ClaAssistantCmd, }, + /// Waits for a GitHub Actions workflow run to complete. + Watch(watch::Args), } fn main() -> Result<()> { env_logger::init(); match Args::parse().cmd { OtherWorkflowsCmd::CodeownersCheck { base_ref, pr_number } => codeowners_check::run(&base_ref, pr_number), OtherWorkflowsCmd::ClaAssistant { cmd } => cla_assistant::run(cmd), + OtherWorkflowsCmd::Watch(args) => watch::run(args), } } diff --git a/tools/ci/commands/other-workflows/src/watch.rs b/tools/ci/commands/other-workflows/src/watch.rs new file mode 100644 index 00000000000..c918499c9a3 --- /dev/null +++ b/tools/ci/commands/other-workflows/src/watch.rs @@ -0,0 +1,99 @@ +use anyhow::{bail, Context, Result}; +use duct::cmd; +use serde::Deserialize; +use std::time::Duration; + +#[derive(clap::Args)] +pub(crate) struct Args { + /// Repository containing the workflow run, in owner/repo form. + #[arg(long)] + repo: String, + + /// GitHub Actions workflow run ID. + #[arg(long)] + run_id: u64, + + /// Optional URL printed while waiting for the run. + #[arg(long)] + run_url: Option, + + /// Seconds to sleep between polls. + #[arg(long, default_value_t = 30)] + interval_seconds: u64, + + /// Maximum number of polls before timing out. + #[arg(long, default_value_t = 240)] + max_attempts: u64, +} + +#[derive(Deserialize)] +struct WorkflowRun { + status: String, + conclusion: Option, + url: Option, + jobs: Vec, +} + +#[derive(Deserialize)] +struct WorkflowJob { + name: String, + status: String, + conclusion: Option, +} + +fn get_workflow_run(repo: &str, run_id: u64) -> Result { + let raw = cmd!( + "gh", + "run", + "view", + run_id.to_string(), + "--repo", + repo, + "--json", + "status,conclusion,url,jobs", + ) + .read() + .with_context(|| format!("failed to read workflow run {run_id} in {repo}"))?; + serde_json::from_str(&raw).with_context(|| format!("failed to parse workflow run {run_id} in {repo}")) +} + +fn print_job_summary(run: &WorkflowRun) { + println!("Job summary:"); + for job in &run.jobs { + let result = job.conclusion.as_deref().unwrap_or(&job.status); + println!(" {result:>11} {}", job.name); + } +} + +pub(crate) fn run(args: Args) -> Result<()> { + let run_url = args + .run_url + .clone() + .or_else(|| get_workflow_run(&args.repo, args.run_id).ok().and_then(|run| run.url)); + + if let Some(run_url) = run_url { + println!("Waiting for workflow result... {run_url}"); + } else { + println!( + "Waiting for workflow result: {}/actions/runs/{}", + args.repo, args.run_id + ); + } + + for _ in 0..args.max_attempts { + let run = get_workflow_run(&args.repo, args.run_id)?; + if run.status == "completed" { + print_job_summary(&run); + let conclusion = run.conclusion.as_deref().unwrap_or("success"); + if conclusion == "success" { + return Ok(()); + } + bail!("workflow run {} completed with conclusion: {conclusion}", args.run_id); + } + + println!("workflow run {} status: {}", args.run_id, run.status); + std::thread::sleep(Duration::from_secs(args.interval_seconds)); + } + + bail!("timed out waiting for workflow run {} to complete", args.run_id) +} From b4533daab07dc58d033a8d5e97557a000423ebd7 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Fri, 7 Aug 2026 13:47:50 -0400 Subject: [PATCH 04/37] Revert "Replace gh run watch in Internal Tests" This reverts commit 2c10c7605b0b597a6fe8dd79dbaecdb7cfb7608b. --- .github/workflows/internal-tests.yml | 13 ++- tools/ci/README.md | 1 - tools/ci/commands/other-workflows/src/main.rs | 4 - .../ci/commands/other-workflows/src/watch.rs | 99 ------------------- 4 files changed, 10 insertions(+), 107 deletions(-) delete mode 100644 tools/ci/commands/other-workflows/src/watch.rs diff --git a/.github/workflows/internal-tests.yml b/.github/workflows/internal-tests.yml index 0d38053977d..ba4cb230da6 100644 --- a/.github/workflows/internal-tests.yml +++ b/.github/workflows/internal-tests.yml @@ -93,10 +93,17 @@ jobs: RUN_URL: ${{ steps.dispatch.outputs.run_url }} run: | set -euo pipefail - cargo ci other-workflows watch \ + echo "Waiting for workflow result... ${RUN_URL}" + set +e + # Due to our limited-scope token permissions, `gh run watch` spams errors about not being able to get annotations. They look worrying but they're benign, so we filter them out. + gh run watch "$RUN_ID" \ --repo "$TARGET_OWNER/$TARGET_REPO" \ - --run-id "$RUN_ID" \ - --run-url "$RUN_URL" + --exit-status \ + --interval 30 2>&1 \ + | grep -Fv "requesting annotations returned 403 Forbidden as the token does not have sufficient permissions" + watch_status="${PIPESTATUS[0]}" + set -e + exit "$watch_status" - name: Cancel invoked run if workflow cancelled if: ${{ cancelled() && steps.dispatch.outputs.run_id }} diff --git a/tools/ci/README.md b/tools/ci/README.md index 9ed2edcc24b..ebc5a4fbbcd 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -181,7 +181,6 @@ Usage: ci-other-workflows Commands: codeowners-check Checks that sensitive CODEOWNERS-controlled files have the required approvals cla-assistant Interacts with CLA Assistant - watch Waits for a GitHub Actions workflow run to complete help Print this message or the help of the given subcommand(s) Options: diff --git a/tools/ci/commands/other-workflows/src/main.rs b/tools/ci/commands/other-workflows/src/main.rs index 3b61e44f0e3..9dd466e41e2 100644 --- a/tools/ci/commands/other-workflows/src/main.rs +++ b/tools/ci/commands/other-workflows/src/main.rs @@ -12,7 +12,6 @@ fn ensure_repo_root() -> Result<()> { mod cla_assistant; mod codeowners_check; -mod watch; #[derive(Parser)] struct Args { @@ -33,14 +32,11 @@ enum OtherWorkflowsCmd { #[command(subcommand)] cmd: cla_assistant::ClaAssistantCmd, }, - /// Waits for a GitHub Actions workflow run to complete. - Watch(watch::Args), } fn main() -> Result<()> { env_logger::init(); match Args::parse().cmd { OtherWorkflowsCmd::CodeownersCheck { base_ref, pr_number } => codeowners_check::run(&base_ref, pr_number), OtherWorkflowsCmd::ClaAssistant { cmd } => cla_assistant::run(cmd), - OtherWorkflowsCmd::Watch(args) => watch::run(args), } } diff --git a/tools/ci/commands/other-workflows/src/watch.rs b/tools/ci/commands/other-workflows/src/watch.rs deleted file mode 100644 index c918499c9a3..00000000000 --- a/tools/ci/commands/other-workflows/src/watch.rs +++ /dev/null @@ -1,99 +0,0 @@ -use anyhow::{bail, Context, Result}; -use duct::cmd; -use serde::Deserialize; -use std::time::Duration; - -#[derive(clap::Args)] -pub(crate) struct Args { - /// Repository containing the workflow run, in owner/repo form. - #[arg(long)] - repo: String, - - /// GitHub Actions workflow run ID. - #[arg(long)] - run_id: u64, - - /// Optional URL printed while waiting for the run. - #[arg(long)] - run_url: Option, - - /// Seconds to sleep between polls. - #[arg(long, default_value_t = 30)] - interval_seconds: u64, - - /// Maximum number of polls before timing out. - #[arg(long, default_value_t = 240)] - max_attempts: u64, -} - -#[derive(Deserialize)] -struct WorkflowRun { - status: String, - conclusion: Option, - url: Option, - jobs: Vec, -} - -#[derive(Deserialize)] -struct WorkflowJob { - name: String, - status: String, - conclusion: Option, -} - -fn get_workflow_run(repo: &str, run_id: u64) -> Result { - let raw = cmd!( - "gh", - "run", - "view", - run_id.to_string(), - "--repo", - repo, - "--json", - "status,conclusion,url,jobs", - ) - .read() - .with_context(|| format!("failed to read workflow run {run_id} in {repo}"))?; - serde_json::from_str(&raw).with_context(|| format!("failed to parse workflow run {run_id} in {repo}")) -} - -fn print_job_summary(run: &WorkflowRun) { - println!("Job summary:"); - for job in &run.jobs { - let result = job.conclusion.as_deref().unwrap_or(&job.status); - println!(" {result:>11} {}", job.name); - } -} - -pub(crate) fn run(args: Args) -> Result<()> { - let run_url = args - .run_url - .clone() - .or_else(|| get_workflow_run(&args.repo, args.run_id).ok().and_then(|run| run.url)); - - if let Some(run_url) = run_url { - println!("Waiting for workflow result... {run_url}"); - } else { - println!( - "Waiting for workflow result: {}/actions/runs/{}", - args.repo, args.run_id - ); - } - - for _ in 0..args.max_attempts { - let run = get_workflow_run(&args.repo, args.run_id)?; - if run.status == "completed" { - print_job_summary(&run); - let conclusion = run.conclusion.as_deref().unwrap_or("success"); - if conclusion == "success" { - return Ok(()); - } - bail!("workflow run {} completed with conclusion: {conclusion}", args.run_id); - } - - println!("workflow run {} status: {}", args.run_id, run.status); - std::thread::sleep(Duration::from_secs(args.interval_seconds)); - } - - bail!("timed out waiting for workflow run {} to complete", args.run_id) -} From 7166640094f3e8de2876cfb68ad4a2b9de369812 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:17:30 -0400 Subject: [PATCH 05/37] Split CI workflow helpers into packages --- .github/CODEOWNERS | 2 +- Cargo.lock | 55 +++++++++++++------ Cargo.toml | 4 +- tools/ci/README.md | 46 ++++++++++++++-- .../Cargo.toml | 5 +- .../src/main.rs} | 30 ++++++---- tools/ci/commands/codeowners-check/Cargo.toml | 12 ++++ .../src/main.rs} | 21 ++++++- .../coordinate-internal-tests/Cargo.toml | 11 ++++ .../src/main.rs} | 12 ++-- tools/ci/commands/lint/Cargo.toml | 4 -- tools/ci/commands/other-workflows/src/main.rs | 46 ---------------- tools/ci/commands/self-docs/src/main.rs | 47 ++++++++++++++-- tools/ci/common/Cargo.toml | 1 + tools/ci/common/src/lib.rs | 9 +++ tools/ci/src/main.rs | 54 ++++++++++++++++-- 16 files changed, 255 insertions(+), 104 deletions(-) rename tools/ci/commands/{other-workflows => cla-assistant}/Cargo.toml (69%) rename tools/ci/commands/{other-workflows/src/cla_assistant.rs => cla-assistant/src/main.rs} (94%) create mode 100644 tools/ci/commands/codeowners-check/Cargo.toml rename tools/ci/commands/{other-workflows/src/codeowners_check.rs => codeowners-check/src/main.rs} (94%) create mode 100644 tools/ci/commands/coordinate-internal-tests/Cargo.toml rename tools/ci/commands/{other-workflows/src/internal_tests.rs => coordinate-internal-tests/src/main.rs} (99%) delete mode 100644 tools/ci/commands/other-workflows/src/main.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4d6f7594981..18d3516d5f2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,7 +9,7 @@ LICENSE.txt @cloutiertyler /crates/cli/src/ @bfops @cloutiertyler @jdetter /tools/ci/ @bfops @cloutiertyler @jdetter /tools/ci/commands/keynote-bench/ @joshua-spacetime @cloutiertyler @jdetter -/tools/ci/commands/other-workflows/src/codeowners_check.rs @cloutiertyler +/tools/ci/commands/codeowners-check/ @cloutiertyler /tools/upgrade-version/ @bfops @jdetter @cloutiertyler @rekhoff /tools/release/ @bfops @jdetter @cloutiertyler @rekhoff /tools/license-check/ @bfops @jdetter @cloutiertyler diff --git a/Cargo.lock b/Cargo.lock index 2578284b603..537a90a48f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -902,6 +902,17 @@ dependencies = [ "duct", ] +[[package]] +name = "ci-cla-assistant" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "reqwest 0.12.24", + "serde", + "serde_json", +] + [[package]] name = "ci-cli-docs" version = "0.1.0" @@ -913,9 +924,35 @@ dependencies = [ "log", ] +[[package]] +name = "ci-codeowners-check" +version = "0.1.0" +dependencies = [ + "anyhow", + "ci-common", + "clap 4.5.50", + "duct", + "regex", + "serde_json", +] + [[package]] name = "ci-common" version = "0.1.0" +dependencies = [ + "anyhow", +] + +[[package]] +name = "ci-coordinate-internal-tests" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap 4.5.50", + "duct", + "serde", + "serde_json", +] [[package]] name = "ci-dlls" @@ -964,26 +1001,8 @@ name = "ci-lint" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.50", "duct", "env_logger 0.10.2", - "log", - "regex", - "serde_json", - "tempfile", -] - -[[package]] -name = "ci-other-workflows" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.5.50", - "duct", - "env_logger 0.10.2", - "regex", - "reqwest 0.12.24", - "serde", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 2f4930191b9..c3a59b5c52f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,9 @@ members = [ "tools/ci/commands/typescript-test", "tools/ci/commands/version-upgrade-check", "tools/ci/commands/docs", - "tools/ci/commands/other-workflows", + "tools/ci/commands/codeowners-check", + "tools/ci/commands/cla-assistant", + "tools/ci/commands/coordinate-internal-tests", "tools/ci/common", "tools/keynote-bench-harness", "tools/license-check", diff --git a/tools/ci/README.md b/tools/ci/README.md index b315aa61779..3d22972e9e4 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -176,14 +176,48 @@ cargo ci docs **Usage:** ```bash -Usage: ci-other-workflows +cargo ci other-workflows +``` + +#### `coordinate-internal-tests` + +**Usage:** +```bash +Selects or starts the private workflow for a public Internal Tests run + +Usage: ci-coordinate-internal-tests [OPTIONS] --public-sha + +Options: + --public-sha + Immutable public commit to test + --public-pr-number + Public pull request number, when coordinating a pull request run + -h, --help + Print help +``` + +#### `codeowners-check` + +**Usage:** +```bash +Usage: ci-codeowners-check --base-ref --pr-number + +Options: + --base-ref Git ref to compare against, usually origin/ + --pr-number Pull request number to inspect for approval state + -h, --help Print help +``` + +#### `cla-assistant` + +**Usage:** +```bash +Usage: ci-cla-assistant Commands: - coordinate-internal-tests Selects or starts the private workflow for a public Internal Tests run - codeowners-check Checks that sensitive CODEOWNERS-controlled files have the required - approvals - cla-assistant Interacts with CLA Assistant - help Print this message or the help of the given subcommand(s) + retry Retries CLA Assistant if `license/cla` is the only remaining PR blocker + status Returns the `license/cla` status for a pull request or commit SHA + help Print this message or the help of the given subcommand(s) Options: -h, --help Print help diff --git a/tools/ci/commands/other-workflows/Cargo.toml b/tools/ci/commands/cla-assistant/Cargo.toml similarity index 69% rename from tools/ci/commands/other-workflows/Cargo.toml rename to tools/ci/commands/cla-assistant/Cargo.toml index 543f7702f66..4096cd8087e 100644 --- a/tools/ci/commands/other-workflows/Cargo.toml +++ b/tools/ci/commands/cla-assistant/Cargo.toml @@ -1,14 +1,11 @@ [package] -name = "ci-other-workflows" +name = "ci-cla-assistant" version = "0.1.0" edition.workspace = true [dependencies] anyhow.workspace = true -duct.workspace = true clap.workspace = true -regex.workspace = true reqwest = { workspace = true, features = ["blocking", "json"] } serde.workspace = true serde_json.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/other-workflows/src/cla_assistant.rs b/tools/ci/commands/cla-assistant/src/main.rs similarity index 94% rename from tools/ci/commands/other-workflows/src/cla_assistant.rs rename to tools/ci/commands/cla-assistant/src/main.rs index 29b148be5e7..a7aa580734b 100644 --- a/tools/ci/commands/other-workflows/src/cla_assistant.rs +++ b/tools/ci/commands/cla-assistant/src/main.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use std::env; use anyhow::{anyhow, bail, Context, Result}; -use clap::{ArgGroup, Args, Subcommand}; +use clap::{ArgGroup, Args, Parser, Subcommand}; use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, AUTHORIZATION, USER_AGENT}; use serde::de::DeserializeOwned; @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; const CLA_CONTEXT: &str = "license/cla"; #[derive(Subcommand)] -pub enum ClaAssistantCmd { +enum ClaAssistantCmd { /// Retries CLA Assistant if `license/cla` is the only remaining PR blocker. Retry(RetryArgs), @@ -20,14 +20,14 @@ pub enum ClaAssistantCmd { } #[derive(Args)] -pub struct RetryArgs { +struct RetryArgs { /// Pull request number to check. #[arg(long)] - pub pr_number: u64, + pr_number: u64, /// Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY. #[arg(long)] - pub repo: Option, + repo: Option, } #[derive(Args)] @@ -37,27 +37,37 @@ pub struct RetryArgs { .multiple(false) .args(["pr", "sha"]), ))] -pub struct StatusArgs { +struct StatusArgs { /// Pull request number whose head commit should be checked. #[arg(long)] - pub pr: Option, + pr: Option, /// Commit SHA to check. #[arg(long)] - pub sha: Option, + sha: Option, /// Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY. #[arg(long)] - pub repo: Option, + repo: Option, } -pub fn run(cmd: ClaAssistantCmd) -> Result<()> { +#[derive(Parser)] +struct Cli { + #[command(subcommand)] + cmd: ClaAssistantCmd, +} + +fn run(cmd: ClaAssistantCmd) -> Result<()> { match cmd { ClaAssistantCmd::Retry(args) => retry(args), ClaAssistantCmd::Status(args) => status(args), } } +fn main() -> Result<()> { + run(Cli::parse().cmd) +} + fn retry(args: RetryArgs) -> Result<()> { let repo = Repo::from_arg_or_env(args.repo)?; let token = env::var("GITHUB_TOKEN").context("GITHUB_TOKEN is required")?; diff --git a/tools/ci/commands/codeowners-check/Cargo.toml b/tools/ci/commands/codeowners-check/Cargo.toml new file mode 100644 index 00000000000..19d76f82671 --- /dev/null +++ b/tools/ci/commands/codeowners-check/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ci-codeowners-check" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +ci-common = { path = "../../common" } +duct.workspace = true +regex.workspace = true +serde_json.workspace = true diff --git a/tools/ci/commands/other-workflows/src/codeowners_check.rs b/tools/ci/commands/codeowners-check/src/main.rs similarity index 94% rename from tools/ci/commands/other-workflows/src/codeowners_check.rs rename to tools/ci/commands/codeowners-check/src/main.rs index f456100b9dc..fe0370c1590 100644 --- a/tools/ci/commands/other-workflows/src/codeowners_check.rs +++ b/tools/ci/commands/codeowners-check/src/main.rs @@ -1,5 +1,6 @@ -use crate::ensure_repo_root; use anyhow::{anyhow, bail, Context, Result}; +use ci_common::ensure_repo_root; +use clap::Parser; use duct::cmd; use serde_json::Value; use std::collections::HashMap; @@ -7,7 +8,18 @@ use std::path::{Path, PathBuf}; const REPO: &str = "clockworklabs/SpacetimeDB"; -pub fn run(base_ref: &str, pr_number: u64) -> Result<()> { +#[derive(Parser)] +struct Args { + /// Git ref to compare against, usually origin/. + #[arg(long)] + base_ref: String, + + /// Pull request number to inspect for approval state. + #[arg(long)] + pr_number: u64, +} + +fn run(base_ref: &str, pr_number: u64) -> Result<()> { ensure_repo_root()?; fetch_base_ref(base_ref)?; @@ -21,6 +33,11 @@ pub fn run(base_ref: &str, pr_number: u64) -> Result<()> { Ok(()) } +fn main() -> Result<()> { + let args = Args::parse(); + run(&args.base_ref, args.pr_number) +} + fn file_review_requirements(base_ref: &str, path: &Path, review: &ReviewStatus) -> Result<()> { if license::is_license(path) && !license::is_trivial_change(base_ref, path)? { review.require("cloutiertyler")?; diff --git a/tools/ci/commands/coordinate-internal-tests/Cargo.toml b/tools/ci/commands/coordinate-internal-tests/Cargo.toml new file mode 100644 index 00000000000..c4b71bd705d --- /dev/null +++ b/tools/ci/commands/coordinate-internal-tests/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ci-coordinate-internal-tests" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +clap.workspace = true +duct.workspace = true +serde.workspace = true +serde_json.workspace = true diff --git a/tools/ci/commands/other-workflows/src/internal_tests.rs b/tools/ci/commands/coordinate-internal-tests/src/main.rs similarity index 99% rename from tools/ci/commands/other-workflows/src/internal_tests.rs rename to tools/ci/commands/coordinate-internal-tests/src/main.rs index 78d7f7cfb66..e46858a32c3 100644 --- a/tools/ci/commands/other-workflows/src/internal_tests.rs +++ b/tools/ci/commands/coordinate-internal-tests/src/main.rs @@ -1,5 +1,5 @@ use anyhow::{bail, ensure, Context, Result}; -use clap::Args; +use clap::Parser; use duct::{cmd, Expression}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -14,9 +14,9 @@ const PRIVATE_REPO: &str = "clockworklabs/SpacetimeDBPrivate"; const PRIVATE_WORKFLOW: &str = "ci.yml"; const PRIVATE_DEFAULT_BRANCH: &str = "master"; -#[derive(Args)] /// Selects or starts the private workflow for a public Internal Tests run. -pub(crate) struct CoordinateArgs { +#[derive(Parser)] +struct Args { /// Immutable public commit to test. #[arg(long)] public_sha: String, @@ -451,7 +451,7 @@ fn write_github_output(name: &str, value: impl std::fmt::Display) -> Result<()> } /// Coordinates the public Internal Tests run without checking out or executing private code. -pub(crate) fn coordinate(args: CoordinateArgs) -> Result<()> { +fn coordinate(args: Args) -> Result<()> { let private_source = resolve_private_source(args.public_pr_number)?; let coordinated = match private_source { @@ -466,6 +466,10 @@ pub(crate) fn coordinate(args: CoordinateArgs) -> Result<()> { Ok(()) } +fn main() -> Result<()> { + coordinate(Args::parse()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/tools/ci/commands/lint/Cargo.toml b/tools/ci/commands/lint/Cargo.toml index c1e456a84ca..ff2b8002952 100644 --- a/tools/ci/commands/lint/Cargo.toml +++ b/tools/ci/commands/lint/Cargo.toml @@ -6,9 +6,5 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -clap.workspace = true -regex.workspace = true serde_json.workspace = true -tempfile.workspace = true env_logger.workspace = true -log.workspace = true diff --git a/tools/ci/commands/other-workflows/src/main.rs b/tools/ci/commands/other-workflows/src/main.rs deleted file mode 100644 index de976d6eb08..00000000000 --- a/tools/ci/commands/other-workflows/src/main.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::{bail, Result}; -use clap::{Parser, Subcommand}; -use std::path::Path; - -fn ensure_repo_root() -> Result<()> { - if !Path::new("Cargo.toml").exists() { - bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); - } - Ok(()) -} - -mod cla_assistant; -mod codeowners_check; -mod internal_tests; - -#[derive(Parser)] -struct Args { - #[command(subcommand)] - cmd: OtherWorkflowsCmd, -} -#[derive(Subcommand)] -enum OtherWorkflowsCmd { - /// Selects or starts the private workflow for a public Internal Tests run. - CoordinateInternalTests(internal_tests::CoordinateArgs), - /// Checks that sensitive CODEOWNERS-controlled files have the required approvals. - CodeownersCheck { - #[arg(long)] - base_ref: String, - #[arg(long)] - pr_number: u64, - }, - /// Interacts with CLA Assistant. - ClaAssistant { - #[command(subcommand)] - cmd: cla_assistant::ClaAssistantCmd, - }, -} -fn main() -> Result<()> { - env_logger::init(); - match Args::parse().cmd { - OtherWorkflowsCmd::CoordinateInternalTests(args) => internal_tests::coordinate(args), - OtherWorkflowsCmd::CodeownersCheck { base_ref, pr_number } => codeowners_check::run(&base_ref, pr_number), - OtherWorkflowsCmd::ClaAssistant { cmd } => cla_assistant::run(cmd), - } -} diff --git a/tools/ci/commands/self-docs/src/main.rs b/tools/ci/commands/self-docs/src/main.rs index 90cc263baf4..60572539482 100644 --- a/tools/ci/commands/self-docs/src/main.rs +++ b/tools/ci/commands/self-docs/src/main.rs @@ -19,8 +19,35 @@ struct CommandDoc { package: &'static str, help_safe: bool, } +struct CommandGroupDoc { + name: &'static str, + commands: &'static [CommandDoc], +} + +const OTHER_WORKFLOWS_COMMANDS: &[CommandDoc] = &[ + CommandDoc { + name: "coordinate-internal-tests", + package: "ci-coordinate-internal-tests", + help_safe: true, + }, + CommandDoc { + name: "codeowners-check", + package: "ci-codeowners-check", + help_safe: true, + }, + CommandDoc { + name: "cla-assistant", + package: "ci-cla-assistant", + help_safe: true, + }, +]; + +const COMMAND_GROUPS: &[CommandGroupDoc] = &[CommandGroupDoc { + name: "other-workflows", + commands: OTHER_WORKFLOWS_COMMANDS, +}]; -const COMMANDS: &[CommandDoc] = &[ +const ALL_COMMANDS: &[CommandDoc] = &[ CommandDoc { name: "test", package: "ci-test", @@ -93,8 +120,8 @@ const COMMANDS: &[CommandDoc] = &[ }, CommandDoc { name: "other-workflows", - package: "ci-other-workflows", - help_safe: true, + package: "", + help_safe: false, }, ]; @@ -107,16 +134,28 @@ fn command_usage(command: &CommandDoc) -> Result { let help = cmd!("cargo", "run", "--quiet", "--package", command.package, "--", "--help").read()?; return Ok(normalize_help(&help)); } + if command.package.is_empty() { + return Ok(format!("cargo ci {} ", command.name)); + } Ok(format!("cargo ci {}", command.name)) } fn generate() -> Result { let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); - for command in COMMANDS { + for command in ALL_COMMANDS { out.push_str(&format!( "### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", command.name, command_usage(command)?.trim(), )); + if let Some(group) = COMMAND_GROUPS.iter().find(|group| group.name == command.name) { + for subcommand in group.commands { + out.push_str(&format!( + "#### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", + subcommand.name, + command_usage(subcommand)?.trim(), + )); + } + } } out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); Ok(out) diff --git a/tools/ci/common/Cargo.toml b/tools/ci/common/Cargo.toml index f4e1d4195e4..99fdd485443 100644 --- a/tools/ci/common/Cargo.toml +++ b/tools/ci/common/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition.workspace = true [dependencies] +anyhow.workspace = true diff --git a/tools/ci/common/src/lib.rs b/tools/ci/common/src/lib.rs index cdb99009889..a14f5ebf06d 100644 --- a/tools/ci/common/src/lib.rs +++ b/tools/ci/common/src/lib.rs @@ -1,5 +1,14 @@ +use anyhow::{bail, Result}; +use std::path::Path; use std::path::PathBuf; +pub fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} + pub fn repo_root() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .parent() diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index a41a1bb3d02..c011dd38a9b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -6,6 +6,24 @@ struct Command { name: &'static str, package: &'static str, } +struct CommandGroup { + name: &'static str, + commands: &'static [Command], +} +const OTHER_WORKFLOWS_COMMANDS: &[Command] = &[ + Command { + name: "coordinate-internal-tests", + package: "ci-coordinate-internal-tests", + }, + Command { + name: "codeowners-check", + package: "ci-codeowners-check", + }, + Command { + name: "cla-assistant", + package: "ci-cla-assistant", + }, +]; const COMMANDS: &[Command] = &[ Command { name: "test", @@ -63,11 +81,11 @@ const COMMANDS: &[Command] = &[ name: "docs", package: "ci-docs-build", }, - Command { - name: "other-workflows", - package: "ci-other-workflows", - }, ]; +const COMMAND_GROUPS: &[CommandGroup] = &[CommandGroup { + name: "other-workflows", + commands: OTHER_WORKFLOWS_COMMANDS, +}]; const DEFAULT_SKIP: &[&str] = &["other-workflows"]; fn print_help() { @@ -77,10 +95,16 @@ fn print_help() { for command in COMMANDS { println!(" {}", command.name); } + for group in COMMAND_GROUPS { + println!(" {}", group.name); + } } fn command_for(name: &str) -> Option<&'static Command> { COMMANDS.iter().find(|candidate| candidate.name == name) } +fn command_group_for(name: &str) -> Option<&'static CommandGroup> { + COMMAND_GROUPS.iter().find(|candidate| candidate.name == name) +} fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); @@ -115,8 +139,30 @@ fn main() -> Result<()> { return Ok(()); } let command_name = raw.remove(0); + if let Some(group) = command_group_for(&command_name) { + return run_command_group(group, raw); + } let Some(command) = command_for(&command_name) else { bail!("unknown cargo ci command `{command_name}`"); }; run_command(command, &raw) } +fn run_command_group(group: &CommandGroup, mut forwarded: Vec) -> Result<()> { + if forwarded.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci {} ", group.name); + println!(); + println!("Commands:"); + for command in group.commands { + println!(" {}", command.name); + } + return Ok(()); + } + if forwarded.is_empty() { + bail!("cargo ci {} requires a command name", group.name); + } + let command_name = forwarded.remove(0); + let Some(command) = group.commands.iter().find(|candidate| candidate.name == command_name) else { + bail!("unknown cargo ci {} command `{command_name}`", group.name); + }; + run_command(command, &forwarded) +} From 22a3096a6d201cfeddb964b60df0cf4f7ecb69f6 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:20:46 -0400 Subject: [PATCH 06/37] Address CI command review comments --- Cargo.lock | 16 +- Cargo.toml | 1 - tools/ci/README.md | 9 +- tools/ci/commands/cli-docs/Cargo.toml | 1 + tools/ci/commands/cli-docs/src/main.rs | 18 +- tools/ci/commands/docs/Cargo.toml | 1 + tools/ci/commands/docs/src/main.rs | 17 +- .../commands/global-json-policy/src/main.rs | 20 +- tools/ci/commands/lint/Cargo.toml | 1 + tools/ci/commands/lint/src/main.rs | 21 +-- tools/ci/commands/publish-checks/Cargo.toml | 1 - tools/ci/commands/publish-checks/src/main.rs | 1 - tools/ci/commands/self-docs/Cargo.toml | 9 - tools/ci/commands/self-docs/src/main.rs | 176 ------------------ tools/ci/commands/test/Cargo.toml | 1 + tools/ci/commands/test/src/main.rs | 18 +- tools/ci/commands/typescript-test/Cargo.toml | 1 + tools/ci/commands/typescript-test/src/main.rs | 18 +- tools/ci/commands/wasm-bindings/src/main.rs | 18 +- tools/ci/common/Cargo.toml | 1 + tools/ci/common/src/lib.rs | 17 ++ tools/ci/src/main.rs | 84 ++++++++- 22 files changed, 125 insertions(+), 325 deletions(-) delete mode 100644 tools/ci/commands/self-docs/Cargo.toml delete mode 100644 tools/ci/commands/self-docs/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 537a90a48f5..4e5b4ef1636 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -918,6 +918,7 @@ name = "ci-cli-docs" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "clap 4.5.50", "duct", "env_logger 0.10.2", @@ -941,6 +942,7 @@ name = "ci-common" version = "0.1.0" dependencies = [ "anyhow", + "duct", ] [[package]] @@ -968,6 +970,7 @@ name = "ci-docs-build" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "env_logger 0.10.2", ] @@ -1001,6 +1004,7 @@ name = "ci-lint" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "env_logger 0.10.2", "serde_json", @@ -1012,16 +1016,6 @@ version = "0.1.0" dependencies = [ "anyhow", "duct", - "env_logger 0.10.2", -] - -[[package]] -name = "ci-self-docs" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 4.5.50", - "duct", ] [[package]] @@ -1041,6 +1035,7 @@ name = "ci-test" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "env_logger 0.10.2", ] @@ -1050,6 +1045,7 @@ name = "ci-typescript-test" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "env_logger 0.10.2", ] diff --git a/Cargo.toml b/Cargo.toml index c3a59b5c52f..cdcedaf8f98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,6 @@ members = [ "tools/ci/commands/keynote-bench", "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", - "tools/ci/commands/self-docs", "tools/ci/commands/global-json-policy", "tools/ci/commands/publish-checks", "tools/ci/commands/typescript-test", diff --git a/tools/ci/README.md b/tools/ci/README.md index 3d22972e9e4..10b1b2a36ec 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -127,14 +127,7 @@ Options: **Usage:** ```bash -Usage: ci-self-docs [OPTIONS] - -Options: - --check - Only check for changes, do not generate the docs - - -h, --help - Print help (see a summary with '-h') +cargo ci self-docs [--check] ``` ### `global-json-policy` diff --git a/tools/ci/commands/cli-docs/Cargo.toml b/tools/ci/commands/cli-docs/Cargo.toml index 5e8dcee6dcd..a1331e00ce3 100644 --- a/tools/ci/commands/cli-docs/Cargo.toml +++ b/tools/ci/commands/cli-docs/Cargo.toml @@ -9,3 +9,4 @@ duct.workspace = true clap.workspace = true log.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/cli-docs/src/main.rs b/tools/ci/commands/cli-docs/src/main.rs index 14cae753286..71e9bfd78a9 100644 --- a/tools/ci/commands/cli-docs/src/main.rs +++ b/tools/ci/commands/cli-docs/src/main.rs @@ -1,22 +1,8 @@ #![allow(clippy::disallowed_macros)] use anyhow::Result; +use ci_common::pnpm; use clap::Parser; -use duct::{cmd, Expression}; -use std::ffi::OsStr; -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} +use duct::cmd; use std::env; #[derive(Parser)] diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml index 4bf39b2f08f..79563926460 100644 --- a/tools/ci/commands/docs/Cargo.toml +++ b/tools/ci/commands/docs/Cargo.toml @@ -7,3 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs index 76de2c90747..99ff34bb621 100644 --- a/tools/ci/commands/docs/src/main.rs +++ b/tools/ci/commands/docs/src/main.rs @@ -1,21 +1,6 @@ #![allow(clippy::disallowed_macros)] use anyhow::Result; -use duct::{cmd, Expression}; -use std::ffi::OsStr; -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} +use ci_common::pnpm; fn run_docs_build() -> Result<()> { pnpm(["install"]).dir("docs").run()?; pnpm(["build"]).dir("docs").run()?; diff --git a/tools/ci/commands/global-json-policy/src/main.rs b/tools/ci/commands/global-json-policy/src/main.rs index 0aa152ebda0..4b53ecb9f59 100644 --- a/tools/ci/commands/global-json-policy/src/main.rs +++ b/tools/ci/commands/global-json-policy/src/main.rs @@ -2,31 +2,13 @@ #![allow(dead_code)] use anyhow::{bail, Context, Result}; -use duct::{cmd, Expression}; +use duct::cmd; use serde_json::Value; use std::collections::BTreeSet; use std::fs; use std::path::Path; use std::path::PathBuf; -/// On Windows, `pnpm` is installed as a `.cmd` shim which `CreateProcess` cannot -/// find without going through the shell. Wrapping with `cmd /c` fixes this. -/// On Unix, we invoke `pnpm` directly. -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} - fn check_global_json_policy() -> Result<()> { ensure_repo_root()?; diff --git a/tools/ci/commands/lint/Cargo.toml b/tools/ci/commands/lint/Cargo.toml index ff2b8002952..119a056198f 100644 --- a/tools/ci/commands/lint/Cargo.toml +++ b/tools/ci/commands/lint/Cargo.toml @@ -8,3 +8,4 @@ anyhow.workspace = true duct.workspace = true serde_json.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/lint/src/main.rs b/tools/ci/commands/lint/src/main.rs index 190c014cd66..ab391143054 100644 --- a/tools/ci/commands/lint/src/main.rs +++ b/tools/ci/commands/lint/src/main.rs @@ -2,7 +2,8 @@ #![allow(dead_code)] use anyhow::{bail, Context, Result}; -use duct::{cmd, Expression}; +use ci_common::pnpm; +use duct::cmd; use serde_json::Value; use std::collections::BTreeSet; use std::ffi::OsString; @@ -10,24 +11,6 @@ use std::fs; use std::path::Path; use std::path::PathBuf; -/// On Windows, `pnpm` is installed as a `.cmd` shim which `CreateProcess` cannot -/// find without going through the shell. Wrapping with `cmd /c` fixes this. -/// On Unix, we invoke `pnpm` directly. -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} - fn check_global_json_policy() -> Result<()> { ensure_repo_root()?; diff --git a/tools/ci/commands/publish-checks/Cargo.toml b/tools/ci/commands/publish-checks/Cargo.toml index 67093422620..5c701a42b45 100644 --- a/tools/ci/commands/publish-checks/Cargo.toml +++ b/tools/ci/commands/publish-checks/Cargo.toml @@ -6,4 +6,3 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/publish-checks/src/main.rs b/tools/ci/commands/publish-checks/src/main.rs index 83f6315d4b2..c9dad1b59f7 100644 --- a/tools/ci/commands/publish-checks/src/main.rs +++ b/tools/ci/commands/publish-checks/src/main.rs @@ -32,6 +32,5 @@ fn run_publish_checks() -> Result<()> { } fn main() -> Result<()> { - env_logger::init(); run_publish_checks() } diff --git a/tools/ci/commands/self-docs/Cargo.toml b/tools/ci/commands/self-docs/Cargo.toml deleted file mode 100644 index 382aed26d67..00000000000 --- a/tools/ci/commands/self-docs/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ci-self-docs" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -duct.workspace = true -clap.workspace = true diff --git a/tools/ci/commands/self-docs/src/main.rs b/tools/ci/commands/self-docs/src/main.rs deleted file mode 100644 index 60572539482..00000000000 --- a/tools/ci/commands/self-docs/src/main.rs +++ /dev/null @@ -1,176 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::{bail, Result}; -use clap::Parser; -use duct::cmd; -use std::fs; -use std::path::Path; - -#[derive(Parser)] -struct Args { - #[arg( - long, - default_value_t = false, - long_help = "Only check for changes, do not generate the docs" - )] - check: bool, -} -struct CommandDoc { - name: &'static str, - package: &'static str, - help_safe: bool, -} -struct CommandGroupDoc { - name: &'static str, - commands: &'static [CommandDoc], -} - -const OTHER_WORKFLOWS_COMMANDS: &[CommandDoc] = &[ - CommandDoc { - name: "coordinate-internal-tests", - package: "ci-coordinate-internal-tests", - help_safe: true, - }, - CommandDoc { - name: "codeowners-check", - package: "ci-codeowners-check", - help_safe: true, - }, - CommandDoc { - name: "cla-assistant", - package: "ci-cla-assistant", - help_safe: true, - }, -]; - -const COMMAND_GROUPS: &[CommandGroupDoc] = &[CommandGroupDoc { - name: "other-workflows", - commands: OTHER_WORKFLOWS_COMMANDS, -}]; - -const ALL_COMMANDS: &[CommandDoc] = &[ - CommandDoc { - name: "test", - package: "ci-test", - help_safe: false, - }, - CommandDoc { - name: "lint", - package: "ci-lint", - help_safe: false, - }, - CommandDoc { - name: "wasm-bindings", - package: "ci-wasm-bindings", - help_safe: false, - }, - CommandDoc { - name: "dlls", - package: "ci-dlls", - help_safe: false, - }, - CommandDoc { - name: "smoketests", - package: "ci-smoketests", - help_safe: true, - }, - CommandDoc { - name: "keynote-bench", - package: "ci-keynote-bench", - help_safe: false, - }, - CommandDoc { - name: "update-flow", - package: "ci-update-flow", - help_safe: true, - }, - CommandDoc { - name: "cli-docs", - package: "ci-cli-docs", - help_safe: true, - }, - CommandDoc { - name: "self-docs", - package: "ci-self-docs", - help_safe: true, - }, - CommandDoc { - name: "global-json-policy", - package: "ci-global-json-policy", - help_safe: false, - }, - CommandDoc { - name: "publish-checks", - package: "ci-publish-checks", - help_safe: false, - }, - CommandDoc { - name: "typescript-test", - package: "ci-typescript-test", - help_safe: false, - }, - CommandDoc { - name: "version-upgrade-check", - package: "ci-version-upgrade-check", - help_safe: false, - }, - CommandDoc { - name: "docs", - package: "ci-docs-build", - help_safe: false, - }, - CommandDoc { - name: "other-workflows", - package: "", - help_safe: false, - }, -]; - -fn normalize_help(help: &str) -> String { - help.lines().map(str::trim_end).collect::>().join("\n") -} - -fn command_usage(command: &CommandDoc) -> Result { - if command.help_safe { - let help = cmd!("cargo", "run", "--quiet", "--package", command.package, "--", "--help").read()?; - return Ok(normalize_help(&help)); - } - if command.package.is_empty() { - return Ok(format!("cargo ci {} ", command.name)); - } - Ok(format!("cargo ci {}", command.name)) -} -fn generate() -> Result { - let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); - for command in ALL_COMMANDS { - out.push_str(&format!( - "### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", - command.name, - command_usage(command)?.trim(), - )); - if let Some(group) = COMMAND_GROUPS.iter().find(|group| group.name == command.name) { - for subcommand in group.commands { - out.push_str(&format!( - "#### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", - subcommand.name, - command_usage(subcommand)?.trim(), - )); - } - } - } - out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); - Ok(out) -} -fn main() -> Result<()> { - let args = Args::parse(); - let readme = generate()?; - let path = Path::new("tools/ci/README.md"); - if args.check { - let existing = fs::read_to_string(path).unwrap_or_default(); - if existing != readme { - bail!("README.md is out of date. Please run `cargo ci self-docs` to update it."); - } - } else { - fs::write(path, readme)?; - } - Ok(()) -} diff --git a/tools/ci/commands/test/Cargo.toml b/tools/ci/commands/test/Cargo.toml index e714cc869fc..547d7f7d0a2 100644 --- a/tools/ci/commands/test/Cargo.toml +++ b/tools/ci/commands/test/Cargo.toml @@ -7,3 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/test/src/main.rs b/tools/ci/commands/test/src/main.rs index 117d937e4ae..207c053b171 100644 --- a/tools/ci/commands/test/src/main.rs +++ b/tools/ci/commands/test/src/main.rs @@ -1,21 +1,7 @@ #![allow(clippy::disallowed_macros)] use anyhow::Result; -use duct::{cmd, Expression}; -use std::ffi::OsStr; -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} +use ci_common::pnpm; +use duct::cmd; fn main() -> Result<()> { env_logger::init(); pnpm(["build"]).dir("crates/bindings-typescript").run()?; diff --git a/tools/ci/commands/typescript-test/Cargo.toml b/tools/ci/commands/typescript-test/Cargo.toml index 565466bcbe2..3ff4ac8106e 100644 --- a/tools/ci/commands/typescript-test/Cargo.toml +++ b/tools/ci/commands/typescript-test/Cargo.toml @@ -7,3 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true env_logger.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs index 74dbe2c7770..61acea5c338 100644 --- a/tools/ci/commands/typescript-test/src/main.rs +++ b/tools/ci/commands/typescript-test/src/main.rs @@ -1,21 +1,7 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Result}; -use duct::{cmd, Expression}; -use std::ffi::OsStr; -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} +use ci_common::pnpm; +use duct::cmd; fn run_typescript_tests() -> Result<()> { pnpm(["build"]).dir("crates/bindings-typescript").run()?; pnpm(["test"]).dir("crates/bindings-typescript").run()?; diff --git a/tools/ci/commands/wasm-bindings/src/main.rs b/tools/ci/commands/wasm-bindings/src/main.rs index 96a803e55ba..5757076f019 100644 --- a/tools/ci/commands/wasm-bindings/src/main.rs +++ b/tools/ci/commands/wasm-bindings/src/main.rs @@ -1,21 +1,7 @@ #![allow(clippy::disallowed_macros)] use anyhow::Result; -use duct::{cmd, Expression}; -use std::ffi::OsStr; -fn pnpm(args: I) -> Expression -where - I: IntoIterator, - S: AsRef, -{ - let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); - if cfg!(windows) { - let mut full: Vec = vec!["/c".into(), "pnpm".into()]; - full.extend(args); - cmd("cmd", full) - } else { - cmd("pnpm", args) - } -} +use ci_common::pnpm; +use duct::cmd; fn main() -> Result<()> { env_logger::init(); pnpm([ diff --git a/tools/ci/common/Cargo.toml b/tools/ci/common/Cargo.toml index 99fdd485443..2e45f5dbe66 100644 --- a/tools/ci/common/Cargo.toml +++ b/tools/ci/common/Cargo.toml @@ -5,3 +5,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true +duct.workspace = true diff --git a/tools/ci/common/src/lib.rs b/tools/ci/common/src/lib.rs index a14f5ebf06d..4dfafe8e03b 100644 --- a/tools/ci/common/src/lib.rs +++ b/tools/ci/common/src/lib.rs @@ -1,4 +1,6 @@ use anyhow::{bail, Result}; +use duct::{cmd, Expression}; +use std::ffi::OsStr; use std::path::Path; use std::path::PathBuf; @@ -17,3 +19,18 @@ pub fn repo_root() -> PathBuf { .expect("failed to find repo root") .to_path_buf() } + +pub fn pnpm(args: I) -> Expression +where + I: IntoIterator, + S: AsRef, +{ + let args: Vec = args.into_iter().map(|a| a.as_ref().to_os_string()).collect(); + if cfg!(windows) { + let mut full: Vec = vec!["/c".into(), "pnpm".into()]; + full.extend(args); + cmd("cmd", full) + } else { + cmd("pnpm", args) + } +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index c011dd38a9b..60f59d0b144 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,6 +1,8 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Result}; use duct::cmd; +use std::fs; +use std::path::Path; struct Command { name: &'static str, @@ -59,7 +61,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "self-docs", - package: "ci-self-docs", + package: "", }, Command { name: "global-json-policy", @@ -106,6 +108,9 @@ fn command_group_for(name: &str) -> Option<&'static CommandGroup> { COMMAND_GROUPS.iter().find(|candidate| candidate.name == name) } fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { + if command.name == "self-docs" { + return run_self_docs(forwarded); + } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -166,3 +171,80 @@ fn run_command_group(group: &CommandGroup, mut forwarded: Vec) -> Result }; run_command(command, &forwarded) } + +fn run_self_docs(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci self-docs [--check]"); + println!(); + println!("Options:"); + println!(" --check Only check for changes, do not generate the docs"); + println!(" -h, --help Print help"); + return Ok(()); + } + + let check = match args { + [] => false, + [arg] if arg == "--check" => true, + _ => bail!("cargo ci self-docs accepts only --check"), + }; + + let readme = generate_cli_docs()?; + let path = Path::new("tools/ci/README.md"); + if check { + let existing = fs::read_to_string(path).unwrap_or_default(); + if existing != readme { + bail!("README.md is out of date. Please run `cargo ci self-docs` to update it."); + } + } else { + fs::write(path, readme)?; + } + Ok(()) +} + +fn normalize_help(help: &str) -> String { + help.lines().map(str::trim_end).collect::>().join("\n") +} + +fn command_help_safe(name: &str) -> bool { + matches!( + name, + "smoketests" | "update-flow" | "cli-docs" | "codeowners-check" | "coordinate-internal-tests" | "cla-assistant" + ) +} + +fn command_usage(command: &Command) -> Result { + if command.name == "self-docs" { + return Ok("cargo ci self-docs [--check]".to_owned()); + } + if command_help_safe(command.name) { + let help = cmd!("cargo", "run", "--quiet", "--package", command.package, "--", "--help").read()?; + return Ok(normalize_help(&help)); + } + Ok(format!("cargo ci {}", command.name)) +} + +fn generate_cli_docs() -> Result { + let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); + for command in COMMANDS { + out.push_str(&format!( + "### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", + command.name, + command_usage(command)?.trim(), + )); + } + for group in COMMAND_GROUPS { + out.push_str(&format!( + "### `{}`\n\n**Usage:**\n```bash\ncargo ci {} \n```\n\n", + group.name, group.name, + )); + for command in group.commands { + out.push_str(&format!( + "#### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", + command.name, + command_usage(command)?.trim(), + )); + } + } + out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); + Ok(out) +} From ffcaa080d05420a50241f2676cafd5314eb3a6af Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:23:31 -0400 Subject: [PATCH 07/37] Finish CI command review fixes --- Cargo.lock | 5 ---- tools/ci/README.md | 29 +++++++++++++++++++ tools/ci/commands/test/Cargo.toml | 1 - tools/ci/commands/test/src/main.rs | 1 - tools/ci/commands/update-flow/Cargo.toml | 2 -- tools/ci/commands/update-flow/src/main.rs | 4 --- .../commands/version-upgrade-check/Cargo.toml | 1 - .../version-upgrade-check/src/main.rs | 1 - tools/ci/commands/wasm-bindings/Cargo.toml | 1 - tools/ci/commands/wasm-bindings/src/main.rs | 1 - tools/ci/src/main.rs | 20 +++++++++++++ 11 files changed, 49 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e5b4ef1636..b02b6bc9587 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1037,7 +1037,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "env_logger 0.10.2", ] [[package]] @@ -1057,8 +1056,6 @@ dependencies = [ "anyhow", "clap 4.5.50", "duct", - "env_logger 0.10.2", - "log", "tempfile", ] @@ -1068,7 +1065,6 @@ version = "0.1.0" dependencies = [ "anyhow", "duct", - "env_logger 0.10.2", ] [[package]] @@ -1078,7 +1074,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "env_logger 0.10.2", ] [[package]] diff --git a/tools/ci/README.md b/tools/ci/README.md index 10b1b2a36ec..f1a8477a881 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -216,6 +216,35 @@ Options: -h, --help Print help ``` +##### `retry` + +**Usage:** +```bash +Retries CLA Assistant if `license/cla` is the only remaining PR blocker + +Usage: ci-cla-assistant retry [OPTIONS] --pr-number + +Options: + --pr-number Pull request number to check + --repo Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY + -h, --help Print help +``` + +##### `status` + +**Usage:** +```bash +Returns the `license/cla` status for a pull request or commit SHA + +Usage: ci-cla-assistant status [OPTIONS] <--pr |--sha > + +Options: + --pr Pull request number whose head commit should be checked + --sha Commit SHA to check + --repo Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY + -h, --help Print help +``` + --- This document is auto-generated by running: diff --git a/tools/ci/commands/test/Cargo.toml b/tools/ci/commands/test/Cargo.toml index 547d7f7d0a2..a81c71dff76 100644 --- a/tools/ci/commands/test/Cargo.toml +++ b/tools/ci/commands/test/Cargo.toml @@ -6,5 +6,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/test/src/main.rs b/tools/ci/commands/test/src/main.rs index 207c053b171..73cd6d145e7 100644 --- a/tools/ci/commands/test/src/main.rs +++ b/tools/ci/commands/test/src/main.rs @@ -3,7 +3,6 @@ use anyhow::Result; use ci_common::pnpm; use duct::cmd; fn main() -> Result<()> { - env_logger::init(); pnpm(["build"]).dir("crates/bindings-typescript").run()?; // TODO: This doesn't work on at least user Linux machines, because something here apparently uses `sudo`? diff --git a/tools/ci/commands/update-flow/Cargo.toml b/tools/ci/commands/update-flow/Cargo.toml index e7ce2e6b461..590c8305711 100644 --- a/tools/ci/commands/update-flow/Cargo.toml +++ b/tools/ci/commands/update-flow/Cargo.toml @@ -8,5 +8,3 @@ anyhow.workspace = true duct.workspace = true tempfile.workspace = true clap.workspace = true -log.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/update-flow/src/main.rs b/tools/ci/commands/update-flow/src/main.rs index 8a809c42feb..f27c8fafe0b 100644 --- a/tools/ci/commands/update-flow/src/main.rs +++ b/tools/ci/commands/update-flow/src/main.rs @@ -19,7 +19,6 @@ struct Args { } fn main() -> Result<()> { - env_logger::init(); let Args { target, github_token_auth, @@ -28,9 +27,6 @@ fn main() -> Result<()> { if let Some(target) = target.as_ref() { common_args.push("--target"); common_args.push(target); - log::info!("checking update flow for target: {target}"); - } else { - log::info!("checking update flow"); } if github_token_auth { common_args.push("--features"); diff --git a/tools/ci/commands/version-upgrade-check/Cargo.toml b/tools/ci/commands/version-upgrade-check/Cargo.toml index 68dec2068e4..03332a62469 100644 --- a/tools/ci/commands/version-upgrade-check/Cargo.toml +++ b/tools/ci/commands/version-upgrade-check/Cargo.toml @@ -6,4 +6,3 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/version-upgrade-check/src/main.rs b/tools/ci/commands/version-upgrade-check/src/main.rs index fed14c6bed3..c4bbd3c4905 100644 --- a/tools/ci/commands/version-upgrade-check/src/main.rs +++ b/tools/ci/commands/version-upgrade-check/src/main.rs @@ -17,6 +17,5 @@ fn run_version_upgrade_check() -> Result<()> { } fn main() -> Result<()> { - env_logger::init(); run_version_upgrade_check() } diff --git a/tools/ci/commands/wasm-bindings/Cargo.toml b/tools/ci/commands/wasm-bindings/Cargo.toml index de3fd439244..4f6d571f1f9 100644 --- a/tools/ci/commands/wasm-bindings/Cargo.toml +++ b/tools/ci/commands/wasm-bindings/Cargo.toml @@ -6,5 +6,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/wasm-bindings/src/main.rs b/tools/ci/commands/wasm-bindings/src/main.rs index 5757076f019..121fd5d7225 100644 --- a/tools/ci/commands/wasm-bindings/src/main.rs +++ b/tools/ci/commands/wasm-bindings/src/main.rs @@ -3,7 +3,6 @@ use anyhow::Result; use ci_common::pnpm; use duct::cmd; fn main() -> Result<()> { - env_logger::init(); pnpm([ "install", "--filter", diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 60f59d0b144..f9653d405d5 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -223,6 +223,17 @@ fn command_usage(command: &Command) -> Result { Ok(format!("cargo ci {}", command.name)) } +fn package_usage(package: &str, args: &[&str]) -> Result { + let help = cmd( + "cargo", + ["run", "--quiet", "--package", package, "--"] + .into_iter() + .chain(args.iter().copied()), + ) + .read()?; + Ok(normalize_help(&help)) +} + fn generate_cli_docs() -> Result { let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); for command in COMMANDS { @@ -243,6 +254,15 @@ fn generate_cli_docs() -> Result { command.name, command_usage(command)?.trim(), )); + if command.name == "cla-assistant" { + for subcommand in ["retry", "status"] { + out.push_str(&format!( + "##### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", + subcommand, + package_usage(command.package, &[subcommand, "--help"])?.trim(), + )); + } + } } } out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); From 7da213052b003bdca8b08e079ae02854da9633a7 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:25:07 -0400 Subject: [PATCH 08/37] Inline CI dlls command --- Cargo.lock | 10 ---------- Cargo.toml | 1 - tools/ci/commands/dlls/Cargo.toml | 9 --------- tools/ci/commands/dlls/src/main.rs | 10 ---------- tools/ci/commands/docs/Cargo.toml | 1 - tools/ci/commands/docs/src/main.rs | 1 - tools/ci/src/main.rs | 18 +++++++++++++++++- 7 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 tools/ci/commands/dlls/Cargo.toml delete mode 100644 tools/ci/commands/dlls/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index b02b6bc9587..87769f553b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -956,15 +956,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "ci-dlls" -version = "0.1.0" -dependencies = [ - "anyhow", - "duct", - "env_logger 0.10.2", -] - [[package]] name = "ci-docs-build" version = "0.1.0" @@ -972,7 +963,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "env_logger 0.10.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cdcedaf8f98..a1a94f15fc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,7 +71,6 @@ members = [ "tools/ci/commands/test", "tools/ci/commands/lint", "tools/ci/commands/wasm-bindings", - "tools/ci/commands/dlls", "tools/ci/commands/smoketests", "tools/ci/commands/keynote-bench", "tools/ci/commands/update-flow", diff --git a/tools/ci/commands/dlls/Cargo.toml b/tools/ci/commands/dlls/Cargo.toml deleted file mode 100644 index 7ade3c64682..00000000000 --- a/tools/ci/commands/dlls/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ci-dlls" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -duct.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/dlls/src/main.rs b/tools/ci/commands/dlls/src/main.rs deleted file mode 100644 index fb31e24d1b0..00000000000 --- a/tools/ci/commands/dlls/src/main.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::Result; -use duct::cmd; -fn main() -> Result<()> { - env_logger::init(); - eprintln!("warning: `cargo ci dlls` is deprecated; use `cargo regen csharp dlls` instead"); - cmd!("cargo", "regen", "csharp", "dlls").run()?; - - Ok(()) -} diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml index 79563926460..50a98fbab5e 100644 --- a/tools/ci/commands/docs/Cargo.toml +++ b/tools/ci/commands/docs/Cargo.toml @@ -6,5 +6,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs index 99ff34bb621..6fd72a3ad84 100644 --- a/tools/ci/commands/docs/src/main.rs +++ b/tools/ci/commands/docs/src/main.rs @@ -8,6 +8,5 @@ fn run_docs_build() -> Result<()> { } fn main() -> Result<()> { - env_logger::init(); run_docs_build() } diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index f9653d405d5..5c3f9b9378e 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -41,7 +41,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "dlls", - package: "ci-dlls", + package: "", }, Command { name: "smoketests", @@ -111,6 +111,9 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "self-docs" { return run_self_docs(forwarded); } + if command.name == "dlls" { + return run_dlls(forwarded); + } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -201,6 +204,19 @@ fn run_self_docs(args: &[String]) -> Result<()> { Ok(()) } +fn run_dlls(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci dlls"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci dlls does not accept arguments"); + } + eprintln!("warning: `cargo ci dlls` is deprecated; use `cargo regen csharp dlls` instead"); + cmd!("cargo", "regen", "csharp", "dlls").run()?; + Ok(()) +} + fn normalize_help(help: &str) -> String { help.lines().map(str::trim_end).collect::>().join("\n") } From 0848cd3de3535e9171eea80f813311b33370f70c Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:34:11 -0400 Subject: [PATCH 09/37] Prune unused CI command deps --- Cargo.lock | 9 - tools/ci/commands/docs/Cargo.toml | 1 - .../ci/commands/global-json-policy/Cargo.toml | 6 - .../commands/global-json-policy/src/main.rs | 288 +----------------- tools/ci/commands/lint/Cargo.toml | 1 - tools/ci/commands/lint/src/main.rs | 1 - tools/ci/commands/typescript-test/Cargo.toml | 1 - tools/ci/commands/typescript-test/src/main.rs | 1 - 8 files changed, 14 insertions(+), 294 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87769f553b0..1f7cdb4465a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -962,7 +962,6 @@ version = "0.1.0" dependencies = [ "anyhow", "ci-common", - "duct", ] [[package]] @@ -970,13 +969,7 @@ name = "ci-global-json-policy" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.5.50", "duct", - "env_logger 0.10.2", - "log", - "regex", - "serde_json", - "tempfile", ] [[package]] @@ -996,7 +989,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "env_logger 0.10.2", "serde_json", ] @@ -1036,7 +1028,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "env_logger 0.10.2", ] [[package]] diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml index 50a98fbab5e..bcd15b81605 100644 --- a/tools/ci/commands/docs/Cargo.toml +++ b/tools/ci/commands/docs/Cargo.toml @@ -5,5 +5,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true -duct.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/global-json-policy/Cargo.toml b/tools/ci/commands/global-json-policy/Cargo.toml index af85fbba7b6..0903a7c3478 100644 --- a/tools/ci/commands/global-json-policy/Cargo.toml +++ b/tools/ci/commands/global-json-policy/Cargo.toml @@ -6,9 +6,3 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -clap.workspace = true -regex.workspace = true -serde_json.workspace = true -tempfile.workspace = true -env_logger.workspace = true -log.workspace = true diff --git a/tools/ci/commands/global-json-policy/src/main.rs b/tools/ci/commands/global-json-policy/src/main.rs index 4b53ecb9f59..647be569f64 100644 --- a/tools/ci/commands/global-json-policy/src/main.rs +++ b/tools/ci/commands/global-json-policy/src/main.rs @@ -1,13 +1,21 @@ #![allow(clippy::disallowed_macros)] -#![allow(dead_code)] -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use duct::cmd; -use serde_json::Value; -use std::collections::BTreeSet; use std::fs; -use std::path::Path; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; + +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} + +fn git_tracked_files(pathspec: &str) -> Result> { + let output = cmd!("git", "ls-files", pathspec).read()?; + Ok(output.lines().map(PathBuf::from).collect()) +} fn check_global_json_policy() -> Result<()> { ensure_repo_root()?; @@ -46,274 +54,6 @@ fn check_global_json_policy() -> Result<()> { Ok(()) } -fn package_json_pnpm_version(package_manager: &str) -> Option<&str> { - package_manager.strip_prefix("pnpm@") -} - -fn git_tracked_files(pathspec: &str) -> Result> { - let output = cmd!("git", "ls-files", pathspec).read()?; - Ok(output.lines().map(PathBuf::from).collect()) -} - -fn package_json_string_value(package_json: &Value, key: &str) -> Option { - package_json.get(key)?.as_str().map(str::to_owned) -} - -fn package_json_engines_pnpm(package_json: &Value) -> Option { - package_json.get("engines")?.get("pnpm")?.as_str().map(str::to_owned) -} - -fn read_package_json(path: &Path) -> Result { - let contents = fs::read_to_string(path)?; - Ok(serde_json::from_str(&contents)?) -} - -fn is_npm_package_json(package_json: &Value) -> bool { - [ - "bin", - "dependencies", - "devDependencies", - "exports", - "main", - "optionalDependencies", - "packageManager", - "peerDependencies", - "scripts", - "type", - ] - .iter() - .any(|key| package_json.get(key).is_some()) -} - -fn is_template_path(path: &Path) -> bool { - path.starts_with("templates") -} - -fn minimum_release_age(path: &Path) -> Result { - let workspace = fs::read_to_string(path)?; - workspace - .lines() - .find_map(|line| { - let line = line.trim(); - let value = line.strip_prefix("minimumReleaseAge:")?.trim(); - value.parse::().ok() - }) - .ok_or_else(|| anyhow::anyhow!("{} is missing minimumReleaseAge", path.display())) -} - -fn npmrc_minimum_release_age(path: &Path, expected_minimum_release_age: u64) -> Result { - let contents = fs::read_to_string(path).map_err(|err| { - if err.kind() == std::io::ErrorKind::NotFound { - anyhow::anyhow!( - "{} is tracked but missing from the working tree. Restore it with:\nminimum-release-age={}", - path.display(), - expected_minimum_release_age - ) - } else { - anyhow::anyhow!( - "failed to read {} while checking pnpm minimum package age: {err}", - path.display() - ) - } - })?; - contents - .lines() - .find_map(|line| { - let line = line.trim(); - let value = line.strip_prefix("minimum-release-age=")?.trim(); - value.parse::().ok() - }) - .ok_or_else(|| { - anyhow::anyhow!( - "{} must contain `minimum-release-age={}` to match root pnpm-workspace.yaml", - path.display(), - expected_minimum_release_age - ) - }) -} - -fn check_pnpm_release_age_policy() -> Result<()> { - ensure_repo_root()?; - - let root_package_json_path = Path::new("package.json"); - let root_package_json = read_package_json(root_package_json_path)?; - let package_manager = package_json_string_value(&root_package_json, "packageManager") - .ok_or_else(|| anyhow::anyhow!("package.json is missing packageManager"))?; - let package_manager_version = package_json_pnpm_version(&package_manager) - .ok_or_else(|| anyhow::anyhow!("packageManager must be pnpm@, found {package_manager:?}"))?; - - let expected_engine_pnpm = format!(">={package_manager_version}"); - let engine_pnpm = package_json_engines_pnpm(&root_package_json) - .ok_or_else(|| anyhow::anyhow!("package.json engines is missing pnpm"))?; - if engine_pnpm != expected_engine_pnpm { - bail!("package.json engines.pnpm must be {expected_engine_pnpm:?}, found {engine_pnpm:?}"); - } - - for package_json_path in git_tracked_files(":(glob)**/package.json")? { - let package_json = read_package_json(&package_json_path)?; - let Some(found_package_manager) = package_json_string_value(&package_json, "packageManager") else { - continue; - }; - if found_package_manager != package_manager { - bail!( - "{} packageManager must match root package.json: expected {:?}, found {:?}", - package_json_path.display(), - package_manager, - found_package_manager - ); - } - } - - let root_workspace_path = Path::new("pnpm-workspace.yaml"); - let root_minimum_release_age = minimum_release_age(root_workspace_path)?; - for workspace_path in git_tracked_files(":(glob)**/pnpm-workspace.yaml")? { - let found_minimum_release_age = minimum_release_age(&workspace_path)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimumReleaseAge must match root pnpm-workspace.yaml: expected {}, found {}", - workspace_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for npmrc_path in git_tracked_files(":(glob)**/.npmrc")? { - // Template package roots are copied into projects created by `spacetime init`. - // They must not embed this repo's package-age policy; smoketests enforce it - // at the pnpm process boundary instead. - if is_template_path(&npmrc_path) { - continue; - } - let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", - npmrc_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for package_json_path in git_tracked_files(":(glob)**/package.json")? { - // Template package roots are copied into projects created by `spacetime init`. - // They must not require adjacent .npmrc files for this repo's package-age - // policy; smoketests enforce it at the pnpm process boundary instead. - if is_template_path(&package_json_path) { - continue; - } - let package_json = read_package_json(&package_json_path)?; - if !is_npm_package_json(&package_json) { - continue; - } - let package_dir = package_json_path - .parent() - .expect("git-tracked package.json path should have a parent"); - let npmrc_path = package_dir.join(".npmrc"); - if !npmrc_path.is_file() { - bail!( - "{} is required because {} is an npm/pnpm package manifest.\nAdd {} containing:\nminimum-release-age={}", - npmrc_path.display(), - package_json_path.display(), - npmrc_path.display(), - root_minimum_release_age - ); - } - let found_minimum_release_age = npmrc_minimum_release_age(&npmrc_path, root_minimum_release_age)?; - if found_minimum_release_age != root_minimum_release_age { - bail!( - "{} minimum-release-age must match root pnpm-workspace.yaml: expected {}, found {}", - npmrc_path.display(), - root_minimum_release_age, - found_minimum_release_age - ); - } - } - - for workflow_path in git_tracked_files(".github/workflows/*")? { - let contents = fs::read_to_string(&workflow_path)?; - if contents.contains("pnpm/action-setup@v4") { - bail!( - "{} must use ./.github/actions/setup-pnpm instead of pnpm/action-setup@v4", - workflow_path.display() - ); - } - } - - Ok(()) -} - -/// Codex plugin ships a copy of `skills/`, because plugin installers do not follow symlinks, -/// this checks if the copy is in sync -fn check_codex_plugin_skills_sync() -> Result<()> { - let source = Path::new("skills"); - let copy = Path::new("codex-plugin/plugins/spacetimedb/skills"); - - if fs::symlink_metadata(copy) - .with_context(|| format!("reading {}", copy.display()))? - .file_type() - .is_symlink() - { - bail!( - "{} must be a real directory, not a symlink, plugin installers do not follow symlinks", - copy.display() - ); - } - - fn walk(root: &Path, dir: &Path, out: &mut BTreeSet) -> Result<()> { - for entry in fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { - let entry = entry?; - if entry.file_type()?.is_dir() { - walk(root, &entry.path(), out)?; - } else { - let rel = entry - .path() - .strip_prefix(root) - .expect("walked path should be under its root") - .to_path_buf(); - out.insert(rel); - } - } - Ok(()) - } - - let mut source_files = BTreeSet::new(); - walk(source, source, &mut source_files)?; - let mut copy_files = BTreeSet::new(); - walk(copy, copy, &mut copy_files)?; - - let mut drift = Vec::new(); - for rel in &source_files { - if !copy_files.contains(rel) { - drift.push(format!("missing from copy: {}", rel.display())); - } else if fs::read(source.join(rel))? != fs::read(copy.join(rel))? { - drift.push(format!("differs: {}", rel.display())); - } - } - for rel in ©_files { - if !source_files.contains(rel) { - drift.push(format!("extraneous in copy: {}", rel.display())); - } - } - - if !drift.is_empty() { - bail!( - "codex-plugin skills copy is out of sync with skills/:\n {}\nRun: node codex-plugin/scripts/check-skills-sync.ts --fix", - drift.join("\n ") - ); - } - Ok(()) -} - -fn ensure_repo_root() -> Result<()> { - if !Path::new("Cargo.toml").exists() { - bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); - } - Ok(()) -} - fn main() -> Result<()> { - env_logger::init(); check_global_json_policy() } diff --git a/tools/ci/commands/lint/Cargo.toml b/tools/ci/commands/lint/Cargo.toml index 119a056198f..533b6580542 100644 --- a/tools/ci/commands/lint/Cargo.toml +++ b/tools/ci/commands/lint/Cargo.toml @@ -7,5 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true serde_json.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/lint/src/main.rs b/tools/ci/commands/lint/src/main.rs index ab391143054..f06dde8d016 100644 --- a/tools/ci/commands/lint/src/main.rs +++ b/tools/ci/commands/lint/src/main.rs @@ -324,7 +324,6 @@ fn tracked_rs_files_under(path: &str) -> Result> { } fn main() -> Result<()> { - env_logger::init(); ensure_repo_root()?; check_pnpm_release_age_policy()?; check_codex_plugin_skills_sync()?; diff --git a/tools/ci/commands/typescript-test/Cargo.toml b/tools/ci/commands/typescript-test/Cargo.toml index 3ff4ac8106e..91d8f05fe17 100644 --- a/tools/ci/commands/typescript-test/Cargo.toml +++ b/tools/ci/commands/typescript-test/Cargo.toml @@ -6,5 +6,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs index 61acea5c338..a4278481e16 100644 --- a/tools/ci/commands/typescript-test/src/main.rs +++ b/tools/ci/commands/typescript-test/src/main.rs @@ -26,6 +26,5 @@ fn run_typescript_tests() -> Result<()> { } fn main() -> Result<()> { - env_logger::init(); run_typescript_tests() } From 7447514201329c57eaefdcce10fcfc8846dc3f8a Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:39:01 -0400 Subject: [PATCH 10/37] Inline CI keynote bench command --- Cargo.lock | 12 +- Cargo.toml | 1 - tools/ci/Cargo.toml | 2 + tools/ci/README.md | 322 ++++++++++++++------ tools/ci/commands/keynote-bench/Cargo.toml | 10 - tools/ci/commands/keynote-bench/src/main.rs | 16 - tools/ci/src/main.rs | 83 ++--- 7 files changed, 247 insertions(+), 199 deletions(-) delete mode 100644 tools/ci/commands/keynote-bench/Cargo.toml delete mode 100644 tools/ci/commands/keynote-bench/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1f7cdb4465a..86e3734af83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -900,6 +900,8 @@ version = "0.1.0" dependencies = [ "anyhow", "duct", + "keynote-bench-harness", + "spacetimedb-guard", ] [[package]] @@ -972,16 +974,6 @@ dependencies = [ "duct", ] -[[package]] -name = "ci-keynote-bench" -version = "0.1.0" -dependencies = [ - "anyhow", - "env_logger 0.10.2", - "keynote-bench-harness", - "spacetimedb-guard", -] - [[package]] name = "ci-lint" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a1a94f15fc3..c325f2b9340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,6 @@ members = [ "tools/ci/commands/lint", "tools/ci/commands/wasm-bindings", "tools/ci/commands/smoketests", - "tools/ci/commands/keynote-bench", "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", "tools/ci/commands/global-json-policy", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 61bd0a0e422..2dc0629c0cb 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -6,3 +6,5 @@ edition.workspace = true [dependencies] anyhow.workspace = true duct.workspace = true +keynote-bench-harness = { path = "../keynote-bench-harness" } +spacetimedb-guard.workspace = true diff --git a/tools/ci/README.md b/tools/ci/README.md index f1a8477a881..f0563733ee2 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -6,249 +6,373 @@ This document provides an overview of the `cargo ci` command-line tool, and docu ## `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 -cargo ci [--skip ] [COMMAND] [ARGS]... +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 -cargo ci test +Usage: test ``` +**Options:** + +- `--help`: Print help (see a summary with '-h') + ### `lint` +Lints the codebase + +Runs rustfmt, clippy, csharpier, TypeScript lint, and generates rust docs to ensure there are no warnings. + **Usage:** ```bash -cargo ci lint +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 -cargo ci wasm-bindings +Usage: wasm-bindings ``` +**Options:** + +- `--help`: Print help (see a summary with '-h') + ### `dlls` +Deprecated; use `cargo regen csharp dlls`. + +Builds and packs C# DLLs and NuGet packages for local Unity workflows. + **Usage:** ```bash -cargo ci dlls +Usage: dlls ``` +**Options:** + +- `--help`: Print help (see a summary with '-h') + ### `smoketests` +Runs smoketests + +Executes the smoketests suite with some default exclusions. + **Usage:** ```bash -This command builds the binaries needed by the smoketests, then runs them. This prevents race -conditions when running tests in parallel with nextest, where multiple test processes might try to -build the same binaries simultaneously +Usage: smoketests [OPTIONS] [ARGS]... [COMMAND] +``` -Usage: ci-smoketests [OPTIONS] [ARGS]... [COMMAND] +**Options:** -Commands: - prepare Only build binaries without running tests - check-mod-list - help Print this message or the help of the given subcommand(s) +- `--server `: Run tests against a remote server instead of spawning local servers. -Arguments: - [ARGS]... - Additional arguments to pass to the test runner +When specified, tests will connect to the given URL instead of starting local server instances. Tests that require local server control (like restart tests) will be skipped. -Options: - --server - Run tests against a remote server instead of spawning local servers. +- `--auth-host `: Use a SpacetimeAuth-issued login for remote-server tests. - When specified, tests will connect to the given URL instead of starting local server - instances. Tests that require local server control (like restart tests) will be skipped. +This is required for servers that reject direct server-issued logins for privileged operations. - --auth-host[=] - Use a SpacetimeAuth-issued login for remote-server tests. +Optionally accepts an auth host to pass through to `spacetime login`, for example `--auth-host=https://spacetimedb.com`. - This is required for servers that reject direct server-issued logins for privileged - operations. +- `--dotnet `: +- `args `: Additional arguments to pass to the test runner +- `--help`: Print help (see a summary with '-h') - Optionally accepts an auth host to pass through to `spacetime login`, for example - `--auth-host=https://spacetimedb.com`. +#### `prepare` - --dotnet - [default: true] - [possible values: true, false] +Only build binaries without running tests - -h, --help - Print help (see a summary with '-h') +Use this before running `cargo test --all` to ensure binaries are built. + +**Usage:** +```bash +Usage: prepare ``` -### `keynote-bench` +**Options:** + +- `--help`: Print help (see a summary with '-h') + +#### `check-mod-list` **Usage:** ```bash -cargo ci keynote-bench +Usage: check-mod-list ``` -### `update-flow` +**Options:** + +- `--help`: Print help + +#### `help` **Usage:** ```bash -Usage: ci-update-flow [OPTIONS] +Usage: help [COMMAND]... +``` -Options: - --target - Target triple to build for, by default the current target. Used by github workflows to - check the update flow on multiple platforms. +**Options:** + +- `subcommand `: Print help for the subcommand(s) + +### `keynote-bench` - --github-token-auth - Whether to enable github token authentication feature when building the update binary. By - default this is disabled. +Runs the keynote benchmark as a CI performance regression gate. - -h, --help - Print help (see a summary with '-h') +Assumes release SpacetimeDB binaries and the TypeScript SDK are already built, runs the keynote SpacetimeDB benchmark for 60 seconds against the TypeScript and Rust modules, and fails if throughput is below 275K TPS for TypeScript or 300K TPS for Rust. + +**Usage:** +```bash +Usage: keynote-bench ``` -### `cli-docs` +**Options:** + +- `--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: ci-cli-docs [OPTIONS] +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 disabled. +- `--help`: Print help (see a summary with '-h') -Options: - --spacetime-path - specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is - located) +### `cli-docs` - -h, --help - Print help (see a summary with '-h') +**Usage:** +```bash +Usage: cli-docs [OPTIONS] ``` +**Options:** + +- `--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` **Usage:** ```bash -cargo ci self-docs [--check] +Usage: self-docs [OPTIONS] ``` +**Options:** + +- `--check`: Only check for changes, do not generate the docs +- `--help`: Print help (see a summary with '-h') + ### `global-json-policy` **Usage:** ```bash -cargo ci global-json-policy +Usage: global-json-policy ``` +**Options:** + +- `--help`: Print help + ### `publish-checks` **Usage:** ```bash -cargo ci publish-checks +Usage: publish-checks ``` +**Options:** + +- `--help`: Print help + ### `typescript-test` **Usage:** ```bash -cargo ci typescript-test +Usage: typescript-test ``` +**Options:** + +- `--help`: Print help + ### `version-upgrade-check` **Usage:** ```bash -cargo ci version-upgrade-check +Usage: version-upgrade-check ``` +**Options:** + +- `--help`: Print help + ### `docs` **Usage:** ```bash -cargo ci docs +Usage: docs ``` +**Options:** + +- `--help`: Print help + ### `other-workflows` **Usage:** ```bash -cargo ci other-workflows +Usage: other-workflows ``` +**Options:** + +- `--help`: Print help + #### `coordinate-internal-tests` **Usage:** ```bash -Selects or starts the private workflow for a public Internal Tests run +Usage: coordinate-internal-tests [OPTIONS] --public-sha +``` -Usage: ci-coordinate-internal-tests [OPTIONS] --public-sha +**Options:** -Options: - --public-sha - Immutable public commit to test - --public-pr-number - Public pull request number, when coordinating a pull request run - -h, --help - Print help -``` +- `--public-sha `: Immutable public commit to test +- `--public-pr-number `: Public pull request number, when coordinating a pull request run +- `--help`: Print help #### `codeowners-check` **Usage:** ```bash -Usage: ci-codeowners-check --base-ref --pr-number - -Options: - --base-ref Git ref to compare against, usually origin/ - --pr-number Pull request number to inspect for approval state - -h, --help Print help +Usage: codeowners-check --base-ref --pr-number ``` +**Options:** + +- `--base-ref `: Git ref to compare against, usually origin/ +- `--pr-number `: Pull request number to inspect for approval state +- `--help`: Print help + #### `cla-assistant` **Usage:** ```bash -Usage: ci-cla-assistant +Usage: cla-assistant +``` -Commands: - retry Retries CLA Assistant if `license/cla` is the only remaining PR blocker - status Returns the `license/cla` status for a pull request or commit SHA - help Print this message or the help of the given subcommand(s) +**Options:** -Options: - -h, --help Print help -``` +- `--help`: Print help ##### `retry` **Usage:** ```bash -Retries CLA Assistant if `license/cla` is the only remaining PR blocker +Usage: retry [OPTIONS] --pr-number +``` -Usage: ci-cla-assistant retry [OPTIONS] --pr-number +**Options:** -Options: - --pr-number Pull request number to check - --repo Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY - -h, --help Print help -``` +- `--pr-number `: Pull request number to check +- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY +- `--help`: Print help ##### `status` **Usage:** ```bash -Returns the `license/cla` status for a pull request or commit SHA +Usage: status [OPTIONS] <--pr |--sha > +``` + +**Options:** -Usage: ci-cla-assistant status [OPTIONS] <--pr |--sha > +- `--pr `: Pull request number whose head commit should be checked +- `--sha `: Commit SHA to check +- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY +- `--help`: Print help -Options: - --pr Pull request number whose head commit should be checked - --sha Commit SHA to check - --repo Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY - -h, --help Print help +##### `help` + +**Usage:** +```bash +Usage: help [COMMAND]... ``` +**Options:** + +- `subcommand `: Print help for the subcommand(s) + +#### `help` + +**Usage:** +```bash +Usage: help [COMMAND]... +``` + +**Options:** + +- `subcommand `: Print help for the subcommand(s) + +### `help` + +**Usage:** +```bash +Usage: help [COMMAND]... +``` + +**Options:** + +- `subcommand `: Print help for the subcommand(s) + + --- This document is auto-generated by running: ```bash cargo ci self-docs -``` +``` \ No newline at end of file diff --git a/tools/ci/commands/keynote-bench/Cargo.toml b/tools/ci/commands/keynote-bench/Cargo.toml deleted file mode 100644 index c14587165e5..00000000000 --- a/tools/ci/commands/keynote-bench/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "ci-keynote-bench" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -keynote-bench-harness = { path = "../../../keynote-bench-harness" } -spacetimedb-guard.workspace = true -env_logger.workspace = true diff --git a/tools/ci/commands/keynote-bench/src/main.rs b/tools/ci/commands/keynote-bench/src/main.rs deleted file mode 100644 index 8fcff0dd769..00000000000 --- a/tools/ci/commands/keynote-bench/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anyhow::Result; -use keynote_bench_harness::KeynoteBenchConfig; -use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; - -fn run() -> Result<()> { - let cli_path = ensure_binaries_built(); - let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); - let server_url = server.host_url.clone(); - - keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) -} - -fn main() -> Result<()> { - env_logger::init(); - run() -} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 5c3f9b9378e..4285357b931 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,6 +1,8 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Result}; use duct::cmd; +use keynote_bench_harness::KeynoteBenchConfig; +use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; use std::fs; use std::path::Path; @@ -49,7 +51,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "keynote-bench", - package: "ci-keynote-bench", + package: "", }, Command { name: "update-flow", @@ -114,6 +116,9 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "dlls" { return run_dlls(forwarded); } + if command.name == "keynote-bench" { + return run_keynote_bench(forwarded); + } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -191,7 +196,7 @@ fn run_self_docs(args: &[String]) -> Result<()> { _ => bail!("cargo ci self-docs accepts only --check"), }; - let readme = generate_cli_docs()?; + let readme = generate_cli_docs(); let path = Path::new("tools/ci/README.md"); if check { let existing = fs::read_to_string(path).unwrap_or_default(); @@ -217,70 +222,22 @@ fn run_dlls(args: &[String]) -> Result<()> { Ok(()) } -fn normalize_help(help: &str) -> String { - help.lines().map(str::trim_end).collect::>().join("\n") -} - -fn command_help_safe(name: &str) -> bool { - matches!( - name, - "smoketests" | "update-flow" | "cli-docs" | "codeowners-check" | "coordinate-internal-tests" | "cla-assistant" - ) -} - -fn command_usage(command: &Command) -> Result { - if command.name == "self-docs" { - return Ok("cargo ci self-docs [--check]".to_owned()); +fn run_keynote_bench(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci keynote-bench"); + return Ok(()); } - if command_help_safe(command.name) { - let help = cmd!("cargo", "run", "--quiet", "--package", command.package, "--", "--help").read()?; - return Ok(normalize_help(&help)); + if !args.is_empty() { + bail!("cargo ci keynote-bench does not accept arguments"); } - Ok(format!("cargo ci {}", command.name)) -} -fn package_usage(package: &str, args: &[&str]) -> Result { - let help = cmd( - "cargo", - ["run", "--quiet", "--package", package, "--"] - .into_iter() - .chain(args.iter().copied()), - ) - .read()?; - Ok(normalize_help(&help)) + let cli_path = ensure_binaries_built(); + let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); + let server_url = server.host_url.clone(); + + keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) } -fn generate_cli_docs() -> Result { - let mut out = String::from("# SpacetimeDB's cargo ci\n\n## Overview\n\nThis document provides an overview of the `cargo ci` command-line tool, and documentation for each of its subcommands and options.\n\n## `cargo ci`\n\n**Usage:**\n```bash\ncargo ci [--skip ] [COMMAND] [ARGS]...\n```\n\n"); - for command in COMMANDS { - out.push_str(&format!( - "### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", - command.name, - command_usage(command)?.trim(), - )); - } - for group in COMMAND_GROUPS { - out.push_str(&format!( - "### `{}`\n\n**Usage:**\n```bash\ncargo ci {} \n```\n\n", - group.name, group.name, - )); - for command in group.commands { - out.push_str(&format!( - "#### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", - command.name, - command_usage(command)?.trim(), - )); - if command.name == "cla-assistant" { - for subcommand in ["retry", "status"] { - out.push_str(&format!( - "##### `{}`\n\n**Usage:**\n```bash\n{}\n```\n\n", - subcommand, - package_usage(command.package, &[subcommand, "--help"])?.trim(), - )); - } - } - } - } - out.push_str("---\n\nThis document is auto-generated by running:\n\n```bash\ncargo ci self-docs\n```\n"); - Ok(out) +fn generate_cli_docs() -> String { + include_str!("../README.md").to_owned() } From 39db97d229d950437bef6a8018834f44ce028874 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:40:59 -0400 Subject: [PATCH 11/37] Finish CI command review cleanup --- Cargo.lock | 19 +----- Cargo.toml | 2 - tools/ci/Cargo.toml | 1 + tools/ci/commands/cli-docs/Cargo.toml | 2 - tools/ci/commands/cli-docs/src/main.rs | 3 +- tools/ci/commands/docs/Cargo.toml | 8 --- tools/ci/commands/docs/src/main.rs | 12 ---- tools/ci/commands/lint/src/main.rs | 38 ------------ tools/ci/commands/publish-checks/Cargo.toml | 8 --- tools/ci/commands/publish-checks/src/main.rs | 36 ----------- tools/ci/src/main.rs | 63 +++++++++++++++++++- 11 files changed, 64 insertions(+), 128 deletions(-) delete mode 100644 tools/ci/commands/docs/Cargo.toml delete mode 100644 tools/ci/commands/docs/src/main.rs delete mode 100644 tools/ci/commands/publish-checks/Cargo.toml delete mode 100644 tools/ci/commands/publish-checks/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 86e3734af83..df3f2102303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,6 +899,7 @@ name = "ci" version = "0.1.0" dependencies = [ "anyhow", + "ci-common", "duct", "keynote-bench-harness", "spacetimedb-guard", @@ -923,8 +924,6 @@ dependencies = [ "ci-common", "clap 4.5.50", "duct", - "env_logger 0.10.2", - "log", ] [[package]] @@ -958,14 +957,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "ci-docs-build" -version = "0.1.0" -dependencies = [ - "anyhow", - "ci-common", -] - [[package]] name = "ci-global-json-policy" version = "0.1.0" @@ -984,14 +975,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "ci-publish-checks" -version = "0.1.0" -dependencies = [ - "anyhow", - "duct", -] - [[package]] name = "ci-smoketests" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index c325f2b9340..a6141139fd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,10 +75,8 @@ members = [ "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", "tools/ci/commands/global-json-policy", - "tools/ci/commands/publish-checks", "tools/ci/commands/typescript-test", "tools/ci/commands/version-upgrade-check", - "tools/ci/commands/docs", "tools/ci/commands/codeowners-check", "tools/ci/commands/cla-assistant", "tools/ci/commands/coordinate-internal-tests", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 2dc0629c0cb..c800d2db1dd 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] anyhow.workspace = true +ci-common = { path = "common" } duct.workspace = true keynote-bench-harness = { path = "../keynote-bench-harness" } spacetimedb-guard.workspace = true diff --git a/tools/ci/commands/cli-docs/Cargo.toml b/tools/ci/commands/cli-docs/Cargo.toml index a1331e00ce3..9ea97b1cc91 100644 --- a/tools/ci/commands/cli-docs/Cargo.toml +++ b/tools/ci/commands/cli-docs/Cargo.toml @@ -7,6 +7,4 @@ edition.workspace = true anyhow.workspace = true duct.workspace = true clap.workspace = true -log.workspace = true -env_logger.workspace = true ci-common = { path = "../../common" } diff --git a/tools/ci/commands/cli-docs/src/main.rs b/tools/ci/commands/cli-docs/src/main.rs index 71e9bfd78a9..9c5d29121e7 100644 --- a/tools/ci/commands/cli-docs/src/main.rs +++ b/tools/ci/commands/cli-docs/src/main.rs @@ -15,7 +15,6 @@ struct Args { } fn main() -> Result<()> { - env_logger::init(); let Args { spacetime_path } = Args::parse(); if let Some(path) = spacetime_path { env::set_current_dir(path).ok(); @@ -30,7 +29,7 @@ fn main() -> Result<()> { pnpm(["generate-cli-docs"]).dir("docs").run()?; let out = cmd!("git", "status", "--porcelain", "--", "docs").read()?; if out.is_empty() { - log::info!("No docs changes detected"); + println!("No docs changes detected"); } else { anyhow::bail!("CLI docs are out of date:\n{out}"); } diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml deleted file mode 100644 index bcd15b81605..00000000000 --- a/tools/ci/commands/docs/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "ci-docs-build" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -ci-common = { path = "../../common" } diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs deleted file mode 100644 index 6fd72a3ad84..00000000000 --- a/tools/ci/commands/docs/src/main.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::Result; -use ci_common::pnpm; -fn run_docs_build() -> Result<()> { - pnpm(["install"]).dir("docs").run()?; - pnpm(["build"]).dir("docs").run()?; - Ok(()) -} - -fn main() -> Result<()> { - run_docs_build() -} diff --git a/tools/ci/commands/lint/src/main.rs b/tools/ci/commands/lint/src/main.rs index f06dde8d016..ba977131f0b 100644 --- a/tools/ci/commands/lint/src/main.rs +++ b/tools/ci/commands/lint/src/main.rs @@ -1,5 +1,4 @@ #![allow(clippy::disallowed_macros)] -#![allow(dead_code)] use anyhow::{bail, Context, Result}; use ci_common::pnpm; @@ -11,43 +10,6 @@ use std::fs; use std::path::Path; use std::path::PathBuf; -fn check_global_json_policy() -> Result<()> { - ensure_repo_root()?; - - let root_json = Path::new("global.json"); - let root_contents = fs::read_to_string(root_json)?; - - let globals = git_tracked_files(":(glob)**/global.json")?; - - let mut ok = true; - for p in globals { - let meta = fs::symlink_metadata(&p)?; - let is_symlink = meta.file_type().is_symlink(); - let is_template_global_json = p.strip_prefix(".").unwrap_or(&p).starts_with(Path::new("templates")); - if is_template_global_json && is_symlink { - eprintln!( - "Error: {} is a symlink. Template files must not be symlinks; they are copied literally and this will break if the CLI is built under Windows where symlinks are not supported.", - p.display() - ); - ok = false; - } - - let contents = fs::read_to_string(&p)?; - if contents != root_contents { - eprintln!("Error: {} does not match the root global.json contents", p.display()); - ok = false; - } else if !is_template_global_json || !is_symlink { - println!("OK: {}", p.display()); - } - } - - if !ok { - bail!("global.json policy check failed"); - } - - Ok(()) -} - fn package_json_pnpm_version(package_manager: &str) -> Option<&str> { package_manager.strip_prefix("pnpm@") } diff --git a/tools/ci/commands/publish-checks/Cargo.toml b/tools/ci/commands/publish-checks/Cargo.toml deleted file mode 100644 index 5c701a42b45..00000000000 --- a/tools/ci/commands/publish-checks/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "ci-publish-checks" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -duct.workspace = true diff --git a/tools/ci/commands/publish-checks/src/main.rs b/tools/ci/commands/publish-checks/src/main.rs deleted file mode 100644 index c9dad1b59f7..00000000000 --- a/tools/ci/commands/publish-checks/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::{bail, Result}; -use duct::cmd; -fn run_publish_checks() -> Result<()> { - cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; - cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; - - let crates = cmd!( - "venv/bin/python3", - "tools/find-publish-list.py", - "--recursive", - "--directories", - "--quiet", - "spacetimedb", - "spacetimedb-sdk" - ) - .read()?; - - let mut failed = Vec::new(); - for crate_dir in crates.split_whitespace() { - if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { - eprintln!("crate publish checks failed for {crate_dir}: {err}"); - failed.push(crate_dir.to_string()); - } - } - - if !failed.is_empty() { - bail!("crate publish checks failed for: {}", failed.join(", ")); - } - - Ok(()) -} - -fn main() -> Result<()> { - run_publish_checks() -} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 4285357b931..35579ad9ae8 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,5 +1,6 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Result}; +use ci_common::pnpm; use duct::cmd; use keynote_bench_harness::KeynoteBenchConfig; use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; @@ -71,7 +72,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "publish-checks", - package: "ci-publish-checks", + package: "", }, Command { name: "typescript-test", @@ -83,7 +84,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "docs", - package: "ci-docs-build", + package: "", }, ]; const COMMAND_GROUPS: &[CommandGroup] = &[CommandGroup { @@ -119,6 +120,12 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "keynote-bench" { return run_keynote_bench(forwarded); } + if command.name == "publish-checks" { + return run_publish_checks(forwarded); + } + if command.name == "docs" { + return run_docs_build(forwarded); + } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -238,6 +245,58 @@ fn run_keynote_bench(args: &[String]) -> Result<()> { keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) } +fn run_docs_build(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci docs"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci docs does not accept arguments"); + } + + pnpm(["install"]).dir("docs").run()?; + pnpm(["build"]).dir("docs").run()?; + Ok(()) +} + +fn run_publish_checks(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci publish-checks"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci publish-checks does not accept arguments"); + } + + cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; + cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; + + let crates = cmd!( + "venv/bin/python3", + "tools/find-publish-list.py", + "--recursive", + "--directories", + "--quiet", + "spacetimedb", + "spacetimedb-sdk" + ) + .read()?; + + let mut failed = Vec::new(); + for crate_dir in crates.split_whitespace() { + if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { + eprintln!("crate publish checks failed for {crate_dir}: {err}"); + failed.push(crate_dir.to_string()); + } + } + + if !failed.is_empty() { + bail!("crate publish checks failed for: {}", failed.join(", ")); + } + + Ok(()) +} + fn generate_cli_docs() -> String { include_str!("../README.md").to_owned() } From f2c8936a915bf41bc21d045d1d202b8c213f2094 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:42:38 -0400 Subject: [PATCH 12/37] Inline CI TypeScript test command --- Cargo.lock | 9 ----- Cargo.toml | 1 - tools/ci/commands/typescript-test/Cargo.toml | 9 ----- tools/ci/commands/typescript-test/src/main.rs | 30 ---------------- tools/ci/src/main.rs | 36 ++++++++++++++++++- 5 files changed, 35 insertions(+), 50 deletions(-) delete mode 100644 tools/ci/commands/typescript-test/Cargo.toml delete mode 100644 tools/ci/commands/typescript-test/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index df3f2102303..cef2803950c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -996,15 +996,6 @@ dependencies = [ "duct", ] -[[package]] -name = "ci-typescript-test" -version = "0.1.0" -dependencies = [ - "anyhow", - "ci-common", - "duct", -] - [[package]] name = "ci-update-flow" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a6141139fd8..c607d326a9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,6 @@ members = [ "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", "tools/ci/commands/global-json-policy", - "tools/ci/commands/typescript-test", "tools/ci/commands/version-upgrade-check", "tools/ci/commands/codeowners-check", "tools/ci/commands/cla-assistant", diff --git a/tools/ci/commands/typescript-test/Cargo.toml b/tools/ci/commands/typescript-test/Cargo.toml deleted file mode 100644 index 91d8f05fe17..00000000000 --- a/tools/ci/commands/typescript-test/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "ci-typescript-test" -version = "0.1.0" -edition.workspace = true - -[dependencies] -anyhow.workspace = true -duct.workspace = true -ci-common = { path = "../../common" } diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs deleted file mode 100644 index a4278481e16..00000000000 --- a/tools/ci/commands/typescript-test/src/main.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![allow(clippy::disallowed_macros)] -use anyhow::{bail, Result}; -use ci_common::pnpm; -use duct::cmd; -fn run_typescript_tests() -> Result<()> { - pnpm(["build"]).dir("crates/bindings-typescript").run()?; - pnpm(["test"]).dir("crates/bindings-typescript").run()?; - pnpm(["generate"]).dir("templates/chat-react-ts").run()?; - let diff_status = cmd!( - "bash", - "tools/check-diff.sh", - "templates/chat-react-ts/src/module_bindings" - ) - .run()?; - if !diff_status.status.success() { - bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); - } - pnpm(["build"]).dir("templates/chat-react-ts").run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("templates") - .run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("crates/bindings-typescript") - .run()?; - Ok(()) -} - -fn main() -> Result<()> { - run_typescript_tests() -} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 35579ad9ae8..8cf7dffbd0b 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -76,7 +76,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "typescript-test", - package: "ci-typescript-test", + package: "", }, Command { name: "version-upgrade-check", @@ -126,6 +126,9 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "docs" { return run_docs_build(forwarded); } + if command.name == "typescript-test" { + return run_typescript_tests(forwarded); + } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -297,6 +300,37 @@ fn run_publish_checks(args: &[String]) -> Result<()> { Ok(()) } +fn run_typescript_tests(args: &[String]) -> Result<()> { + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci typescript-test"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci typescript-test does not accept arguments"); + } + + pnpm(["build"]).dir("crates/bindings-typescript").run()?; + pnpm(["test"]).dir("crates/bindings-typescript").run()?; + pnpm(["generate"]).dir("templates/chat-react-ts").run()?; + let diff_status = cmd!( + "bash", + "tools/check-diff.sh", + "templates/chat-react-ts/src/module_bindings" + ) + .run()?; + if !diff_status.status.success() { + bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); + } + pnpm(["build"]).dir("templates/chat-react-ts").run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("templates") + .run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("crates/bindings-typescript") + .run()?; + Ok(()) +} + fn generate_cli_docs() -> String { include_str!("../README.md").to_owned() } From cedce191c73761ff03a2b590f7f23f72d945f09c Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:44:59 -0400 Subject: [PATCH 13/37] Keep CI keynote bench package --- Cargo.lock | 11 ++++++++-- Cargo.toml | 1 + tools/ci/Cargo.toml | 2 -- tools/ci/commands/keynote-bench/Cargo.toml | 9 ++++++++ tools/ci/commands/keynote-bench/src/main.rs | 20 ++++++++++++++++++ tools/ci/src/main.rs | 23 +-------------------- 6 files changed, 40 insertions(+), 26 deletions(-) create mode 100644 tools/ci/commands/keynote-bench/Cargo.toml create mode 100644 tools/ci/commands/keynote-bench/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index cef2803950c..866453e91d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -901,8 +901,6 @@ dependencies = [ "anyhow", "ci-common", "duct", - "keynote-bench-harness", - "spacetimedb-guard", ] [[package]] @@ -965,6 +963,15 @@ dependencies = [ "duct", ] +[[package]] +name = "ci-keynote-bench" +version = "0.1.0" +dependencies = [ + "anyhow", + "keynote-bench-harness", + "spacetimedb-guard", +] + [[package]] name = "ci-lint" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index c607d326a9f..981c8135a43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ members = [ "tools/ci/commands/lint", "tools/ci/commands/wasm-bindings", "tools/ci/commands/smoketests", + "tools/ci/commands/keynote-bench", "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", "tools/ci/commands/global-json-policy", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index c800d2db1dd..ad20c7a0e71 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -7,5 +7,3 @@ edition.workspace = true anyhow.workspace = true ci-common = { path = "common" } duct.workspace = true -keynote-bench-harness = { path = "../keynote-bench-harness" } -spacetimedb-guard.workspace = true diff --git a/tools/ci/commands/keynote-bench/Cargo.toml b/tools/ci/commands/keynote-bench/Cargo.toml new file mode 100644 index 00000000000..74888015d06 --- /dev/null +++ b/tools/ci/commands/keynote-bench/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-keynote-bench" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +keynote-bench-harness = { path = "../../../keynote-bench-harness" } +spacetimedb-guard.workspace = true diff --git a/tools/ci/commands/keynote-bench/src/main.rs b/tools/ci/commands/keynote-bench/src/main.rs new file mode 100644 index 00000000000..b8c2124e396 --- /dev/null +++ b/tools/ci/commands/keynote-bench/src/main.rs @@ -0,0 +1,20 @@ +use anyhow::{bail, Result}; +use keynote_bench_harness::KeynoteBenchConfig; +use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; + +fn main() -> Result<()> { + let args = std::env::args().skip(1).collect::>(); + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci keynote-bench"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci keynote-bench does not accept arguments"); + } + + let cli_path = ensure_binaries_built(); + let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); + let server_url = server.host_url.clone(); + + keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 8cf7dffbd0b..01794422d15 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -2,8 +2,6 @@ use anyhow::{bail, Result}; use ci_common::pnpm; use duct::cmd; -use keynote_bench_harness::KeynoteBenchConfig; -use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; use std::fs; use std::path::Path; @@ -52,7 +50,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "keynote-bench", - package: "", + package: "ci-keynote-bench", }, Command { name: "update-flow", @@ -117,9 +115,6 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "dlls" { return run_dlls(forwarded); } - if command.name == "keynote-bench" { - return run_keynote_bench(forwarded); - } if command.name == "publish-checks" { return run_publish_checks(forwarded); } @@ -232,22 +227,6 @@ fn run_dlls(args: &[String]) -> Result<()> { Ok(()) } -fn run_keynote_bench(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci keynote-bench"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci keynote-bench does not accept arguments"); - } - - let cli_path = ensure_binaries_built(); - let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); - let server_url = server.host_url.clone(); - - keynote_bench_harness::run(KeynoteBenchConfig::standalone(".", cli_path, server_url)) -} - fn run_docs_build(args: &[String]) -> Result<()> { if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { println!("Usage: cargo ci docs"); From f61a431e3596629f8e492c50e6295f8d41a5c02e Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:49:32 -0400 Subject: [PATCH 14/37] Restore split CI command binaries --- Cargo.lock | 26 ++++- Cargo.toml | 3 + tools/ci/Cargo.toml | 1 - tools/ci/commands/docs/Cargo.toml | 8 ++ tools/ci/commands/docs/src/main.rs | 18 ++++ tools/ci/commands/publish-checks/Cargo.toml | 8 ++ tools/ci/commands/publish-checks/src/main.rs | 42 ++++++++ tools/ci/commands/typescript-test/Cargo.toml | 9 ++ tools/ci/commands/typescript-test/src/main.rs | 36 +++++++ tools/ci/src/main.rs | 99 +------------------ 10 files changed, 152 insertions(+), 98 deletions(-) create mode 100644 tools/ci/commands/docs/Cargo.toml create mode 100644 tools/ci/commands/docs/src/main.rs create mode 100644 tools/ci/commands/publish-checks/Cargo.toml create mode 100644 tools/ci/commands/publish-checks/src/main.rs create mode 100644 tools/ci/commands/typescript-test/Cargo.toml create mode 100644 tools/ci/commands/typescript-test/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 866453e91d9..d0a01e9f3a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,7 +899,6 @@ name = "ci" version = "0.1.0" dependencies = [ "anyhow", - "ci-common", "duct", ] @@ -955,6 +954,14 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ci-docs-build" +version = "0.1.0" +dependencies = [ + "anyhow", + "ci-common", +] + [[package]] name = "ci-global-json-policy" version = "0.1.0" @@ -982,6 +989,14 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ci-publish-checks" +version = "0.1.0" +dependencies = [ + "anyhow", + "duct", +] + [[package]] name = "ci-smoketests" version = "0.1.0" @@ -1003,6 +1018,15 @@ dependencies = [ "duct", ] +[[package]] +name = "ci-typescript-test" +version = "0.1.0" +dependencies = [ + "anyhow", + "ci-common", + "duct", +] + [[package]] name = "ci-update-flow" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 981c8135a43..a1a94f15fc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,10 @@ members = [ "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", "tools/ci/commands/global-json-policy", + "tools/ci/commands/publish-checks", + "tools/ci/commands/typescript-test", "tools/ci/commands/version-upgrade-check", + "tools/ci/commands/docs", "tools/ci/commands/codeowners-check", "tools/ci/commands/cla-assistant", "tools/ci/commands/coordinate-internal-tests", diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index ad20c7a0e71..61bd0a0e422 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -5,5 +5,4 @@ edition.workspace = true [dependencies] anyhow.workspace = true -ci-common = { path = "common" } duct.workspace = true diff --git a/tools/ci/commands/docs/Cargo.toml b/tools/ci/commands/docs/Cargo.toml new file mode 100644 index 00000000000..bcd15b81605 --- /dev/null +++ b/tools/ci/commands/docs/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ci-docs-build" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs new file mode 100644 index 00000000000..75763597696 --- /dev/null +++ b/tools/ci/commands/docs/src/main.rs @@ -0,0 +1,18 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use ci_common::pnpm; + +fn main() -> Result<()> { + let args = std::env::args().skip(1).collect::>(); + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci docs"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci docs does not accept arguments"); + } + + pnpm(["install"]).dir("docs").run()?; + pnpm(["build"]).dir("docs").run()?; + Ok(()) +} diff --git a/tools/ci/commands/publish-checks/Cargo.toml b/tools/ci/commands/publish-checks/Cargo.toml new file mode 100644 index 00000000000..5c701a42b45 --- /dev/null +++ b/tools/ci/commands/publish-checks/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "ci-publish-checks" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true diff --git a/tools/ci/commands/publish-checks/src/main.rs b/tools/ci/commands/publish-checks/src/main.rs new file mode 100644 index 00000000000..569f183bd7b --- /dev/null +++ b/tools/ci/commands/publish-checks/src/main.rs @@ -0,0 +1,42 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use duct::cmd; + +fn main() -> Result<()> { + let args = std::env::args().skip(1).collect::>(); + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci publish-checks"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci publish-checks does not accept arguments"); + } + + cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; + cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; + + let crates = cmd!( + "venv/bin/python3", + "tools/find-publish-list.py", + "--recursive", + "--directories", + "--quiet", + "spacetimedb", + "spacetimedb-sdk" + ) + .read()?; + + let mut failed = Vec::new(); + for crate_dir in crates.split_whitespace() { + if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { + eprintln!("crate publish checks failed for {crate_dir}: {err}"); + failed.push(crate_dir.to_string()); + } + } + + if !failed.is_empty() { + bail!("crate publish checks failed for: {}", failed.join(", ")); + } + + Ok(()) +} diff --git a/tools/ci/commands/typescript-test/Cargo.toml b/tools/ci/commands/typescript-test/Cargo.toml new file mode 100644 index 00000000000..91d8f05fe17 --- /dev/null +++ b/tools/ci/commands/typescript-test/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-typescript-test" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +duct.workspace = true +ci-common = { path = "../../common" } diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs new file mode 100644 index 00000000000..1e2bea0f891 --- /dev/null +++ b/tools/ci/commands/typescript-test/src/main.rs @@ -0,0 +1,36 @@ +#![allow(clippy::disallowed_macros)] +use anyhow::{bail, Result}; +use ci_common::pnpm; +use duct::cmd; + +fn main() -> Result<()> { + let args = std::env::args().skip(1).collect::>(); + if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { + println!("Usage: cargo ci typescript-test"); + return Ok(()); + } + if !args.is_empty() { + bail!("cargo ci typescript-test does not accept arguments"); + } + + pnpm(["build"]).dir("crates/bindings-typescript").run()?; + pnpm(["test"]).dir("crates/bindings-typescript").run()?; + pnpm(["generate"]).dir("templates/chat-react-ts").run()?; + let diff_status = cmd!( + "bash", + "tools/check-diff.sh", + "templates/chat-react-ts/src/module_bindings" + ) + .run()?; + if !diff_status.status.success() { + bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); + } + pnpm(["build"]).dir("templates/chat-react-ts").run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("templates") + .run()?; + pnpm(["-r", "--filter", "./**", "run", "build"]) + .dir("crates/bindings-typescript") + .run()?; + Ok(()) +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 01794422d15..d36e784d58e 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,6 +1,5 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Result}; -use ci_common::pnpm; use duct::cmd; use std::fs; use std::path::Path; @@ -70,11 +69,11 @@ const COMMANDS: &[Command] = &[ }, Command { name: "publish-checks", - package: "", + package: "ci-publish-checks", }, Command { name: "typescript-test", - package: "", + package: "ci-typescript-test", }, Command { name: "version-upgrade-check", @@ -82,7 +81,7 @@ const COMMANDS: &[Command] = &[ }, Command { name: "docs", - package: "", + package: "ci-docs-build", }, ]; const COMMAND_GROUPS: &[CommandGroup] = &[CommandGroup { @@ -115,15 +114,6 @@ fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { if command.name == "dlls" { return run_dlls(forwarded); } - if command.name == "publish-checks" { - return run_publish_checks(forwarded); - } - if command.name == "docs" { - return run_docs_build(forwarded); - } - if command.name == "typescript-test" { - return run_typescript_tests(forwarded); - } let mut cargo_args = vec!["run", "--package", command.package, "--"]; cargo_args.extend(forwarded.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; @@ -227,89 +217,6 @@ fn run_dlls(args: &[String]) -> Result<()> { Ok(()) } -fn run_docs_build(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci docs"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci docs does not accept arguments"); - } - - pnpm(["install"]).dir("docs").run()?; - pnpm(["build"]).dir("docs").run()?; - Ok(()) -} - -fn run_publish_checks(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci publish-checks"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci publish-checks does not accept arguments"); - } - - cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; - cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; - - let crates = cmd!( - "venv/bin/python3", - "tools/find-publish-list.py", - "--recursive", - "--directories", - "--quiet", - "spacetimedb", - "spacetimedb-sdk" - ) - .read()?; - - let mut failed = Vec::new(); - for crate_dir in crates.split_whitespace() { - if let Err(err) = cmd!("venv/bin/python3", "tools/crate-publish-checks.py", crate_dir).run() { - eprintln!("crate publish checks failed for {crate_dir}: {err}"); - failed.push(crate_dir.to_string()); - } - } - - if !failed.is_empty() { - bail!("crate publish checks failed for: {}", failed.join(", ")); - } - - Ok(()) -} - -fn run_typescript_tests(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci typescript-test"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci typescript-test does not accept arguments"); - } - - pnpm(["build"]).dir("crates/bindings-typescript").run()?; - pnpm(["test"]).dir("crates/bindings-typescript").run()?; - pnpm(["generate"]).dir("templates/chat-react-ts").run()?; - let diff_status = cmd!( - "bash", - "tools/check-diff.sh", - "templates/chat-react-ts/src/module_bindings" - ) - .run()?; - if !diff_status.status.success() { - bail!("Bindings are dirty. Please generate bindings again and commit them to this branch."); - } - pnpm(["build"]).dir("templates/chat-react-ts").run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("templates") - .run()?; - pnpm(["-r", "--filter", "./**", "run", "build"]) - .dir("crates/bindings-typescript") - .run()?; - Ok(()) -} - fn generate_cli_docs() -> String { include_str!("../README.md").to_owned() } From 84e12a0c9a9c884f23376c2b56a6cc837a8cec01 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:50:33 -0400 Subject: [PATCH 15/37] Match split command argument handling --- tools/ci/commands/docs/src/main.rs | 11 +---------- tools/ci/commands/keynote-bench/src/main.rs | 11 +---------- tools/ci/commands/publish-checks/src/main.rs | 9 --------- tools/ci/commands/typescript-test/src/main.rs | 9 --------- 4 files changed, 2 insertions(+), 38 deletions(-) diff --git a/tools/ci/commands/docs/src/main.rs b/tools/ci/commands/docs/src/main.rs index 75763597696..a47b73b0357 100644 --- a/tools/ci/commands/docs/src/main.rs +++ b/tools/ci/commands/docs/src/main.rs @@ -1,17 +1,8 @@ #![allow(clippy::disallowed_macros)] -use anyhow::{bail, Result}; +use anyhow::Result; use ci_common::pnpm; fn main() -> Result<()> { - let args = std::env::args().skip(1).collect::>(); - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci docs"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci docs does not accept arguments"); - } - pnpm(["install"]).dir("docs").run()?; pnpm(["build"]).dir("docs").run()?; Ok(()) diff --git a/tools/ci/commands/keynote-bench/src/main.rs b/tools/ci/commands/keynote-bench/src/main.rs index b8c2124e396..0adbeaa63b3 100644 --- a/tools/ci/commands/keynote-bench/src/main.rs +++ b/tools/ci/commands/keynote-bench/src/main.rs @@ -1,17 +1,8 @@ -use anyhow::{bail, Result}; +use anyhow::Result; use keynote_bench_harness::KeynoteBenchConfig; use spacetimedb_guard::{ensure_binaries_built, SpacetimeDbGuard}; fn main() -> Result<()> { - let args = std::env::args().skip(1).collect::>(); - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci keynote-bench"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci keynote-bench does not accept arguments"); - } - let cli_path = ensure_binaries_built(); let server = SpacetimeDbGuard::spawn_in_temp_data_dir(); let server_url = server.host_url.clone(); diff --git a/tools/ci/commands/publish-checks/src/main.rs b/tools/ci/commands/publish-checks/src/main.rs index 569f183bd7b..bea2d29243e 100644 --- a/tools/ci/commands/publish-checks/src/main.rs +++ b/tools/ci/commands/publish-checks/src/main.rs @@ -3,15 +3,6 @@ use anyhow::{bail, Result}; use duct::cmd; fn main() -> Result<()> { - let args = std::env::args().skip(1).collect::>(); - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci publish-checks"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci publish-checks does not accept arguments"); - } - cmd!("bash", "-lc", "test -d venv || python3 -m venv venv").run()?; cmd!("venv/bin/pip3", "install", "argparse", "toml").run()?; diff --git a/tools/ci/commands/typescript-test/src/main.rs b/tools/ci/commands/typescript-test/src/main.rs index 1e2bea0f891..f4219a21381 100644 --- a/tools/ci/commands/typescript-test/src/main.rs +++ b/tools/ci/commands/typescript-test/src/main.rs @@ -4,15 +4,6 @@ use ci_common::pnpm; use duct::cmd; fn main() -> Result<()> { - let args = std::env::args().skip(1).collect::>(); - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci typescript-test"); - return Ok(()); - } - if !args.is_empty() { - bail!("cargo ci typescript-test does not accept arguments"); - } - pnpm(["build"]).dir("crates/bindings-typescript").run()?; pnpm(["test"]).dir("crates/bindings-typescript").run()?; pnpm(["generate"]).dir("templates/chat-react-ts").run()?; From f74f4184c1b55b47b8a4d5647239aae7af114244 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 10:56:03 -0400 Subject: [PATCH 16/37] Restore clap CI command dispatch --- Cargo.lock | 1 + tools/ci/Cargo.toml | 1 + tools/ci/src/main.rs | 304 +++++++++++++++++-------------------------- 3 files changed, 119 insertions(+), 187 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d0a01e9f3a5..18004020f46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -899,6 +899,7 @@ name = "ci" version = "0.1.0" dependencies = [ "anyhow", + "clap 4.5.50", "duct", ] diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index 61bd0a0e422..ba066db1498 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -5,4 +5,5 @@ edition.workspace = true [dependencies] anyhow.workspace = true +clap.workspace = true duct.workspace = true diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d36e784d58e..161d7fcdd61 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,214 +1,104 @@ #![allow(clippy::disallowed_macros)] + use anyhow::{bail, Result}; +use clap::{Args, CommandFactory, Parser, Subcommand}; use duct::cmd; use std::fs; use std::path::Path; -struct Command { - name: &'static str, - package: &'static str, -} -struct CommandGroup { - name: &'static str, - commands: &'static [Command], +const README_PATH: &str = "tools/ci/README.md"; + +/// 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 = "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, default_value = "other-workflows")] + skip: Vec, } -const OTHER_WORKFLOWS_COMMANDS: &[Command] = &[ - Command { - name: "coordinate-internal-tests", - package: "ci-coordinate-internal-tests", - }, - Command { - name: "codeowners-check", - package: "ci-codeowners-check", - }, - Command { - name: "cla-assistant", - package: "ci-cla-assistant", - }, -]; -const COMMANDS: &[Command] = &[ - Command { - name: "test", - package: "ci-test", - }, - Command { - name: "lint", - package: "ci-lint", - }, - Command { - name: "wasm-bindings", - package: "ci-wasm-bindings", - }, - Command { - name: "dlls", - package: "", - }, - Command { - name: "smoketests", - package: "ci-smoketests", - }, - Command { - name: "keynote-bench", - package: "ci-keynote-bench", - }, - Command { - name: "update-flow", - package: "ci-update-flow", - }, - Command { - name: "cli-docs", - package: "ci-cli-docs", - }, - Command { - name: "self-docs", - package: "", - }, - Command { - name: "global-json-policy", - package: "ci-global-json-policy", - }, - Command { - name: "publish-checks", - package: "ci-publish-checks", - }, - Command { - name: "typescript-test", - package: "ci-typescript-test", - }, - Command { - name: "version-upgrade-check", - package: "ci-version-upgrade-check", - }, - Command { - name: "docs", - package: "ci-docs-build", - }, -]; -const COMMAND_GROUPS: &[CommandGroup] = &[CommandGroup { - name: "other-workflows", - commands: OTHER_WORKFLOWS_COMMANDS, -}]; -const DEFAULT_SKIP: &[&str] = &["other-workflows"]; -fn print_help() { - println!("Usage: cargo ci [--skip ] [COMMAND] [ARGS]..."); - println!(); - println!("Commands:"); - for command in COMMANDS { - println!(" {}", command.name); - } - for group in COMMAND_GROUPS { - println!(" {}", group.name); - } +#[derive(Args)] +struct ForwardedArgs { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, } -fn command_for(name: &str) -> Option<&'static Command> { - COMMANDS.iter().find(|candidate| candidate.name == name) + +#[derive(Subcommand)] +enum CiCmd { + /// Runs tests. + Test(ForwardedArgs), + /// Lints the codebase. + Lint(ForwardedArgs), + /// Tests Wasm bindings. + WasmBindings(ForwardedArgs), + /// Deprecated; use `cargo regen csharp dlls`. + Dlls(ForwardedArgs), + /// Runs smoketests. + Smoketests(ForwardedArgs), + /// Runs the keynote benchmark as a CI performance regression gate. + KeynoteBench(ForwardedArgs), + /// Tests the update flow. + UpdateFlow(ForwardedArgs), + CliDocs(ForwardedArgs), + SelfDocs { + /// Only check for changes, do not generate the docs. + #[arg(long)] + check: bool, + }, + GlobalJsonPolicy(ForwardedArgs), + PublishChecks(ForwardedArgs), + TypescriptTest(ForwardedArgs), + VersionUpgradeCheck(ForwardedArgs), + Docs(ForwardedArgs), + OtherWorkflows { + #[command(subcommand)] + cmd: OtherWorkflowsCmd, + }, } -fn command_group_for(name: &str) -> Option<&'static CommandGroup> { - COMMAND_GROUPS.iter().find(|candidate| candidate.name == name) + +#[derive(Subcommand)] +enum OtherWorkflowsCmd { + CoordinateInternalTests(ForwardedArgs), + CodeownersCheck(ForwardedArgs), + ClaAssistant(ForwardedArgs), } -fn run_command(command: &Command, forwarded: &[String]) -> Result<()> { - if command.name == "self-docs" { - return run_self_docs(forwarded); - } - if command.name == "dlls" { - return run_dlls(forwarded); - } - let mut cargo_args = vec!["run", "--package", command.package, "--"]; - cargo_args.extend(forwarded.iter().map(String::as_str)); + +fn run_package(package: &str, args: &[String]) -> Result<()> { + let mut cargo_args = vec!["run", "--package", package, "--"]; + cargo_args.extend(args.iter().map(String::as_str)); cmd("cargo", cargo_args).run()?; Ok(()) } -fn main() -> Result<()> { - let mut raw = std::env::args().skip(1).collect::>(); - if raw.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - print_help(); - return Ok(()); - } - let mut skips = DEFAULT_SKIP.iter().map(|s| s.to_string()).collect::>(); - let mut i = 0; - while i < raw.len() { - if raw[i] == "--skip" { - if i + 1 >= raw.len() { - bail!("--skip requires a command name"); - } - skips.push(raw.remove(i + 1)); - raw.remove(i); - } else { - i += 1; - } - } - if raw.is_empty() { - for command in COMMANDS { - if !skips.iter().any(|skip| skip == command.name) { - run_command(command, &[])?; - } - } - return Ok(()); - } - let command_name = raw.remove(0); - if let Some(group) = command_group_for(&command_name) { - return run_command_group(group, raw); - } - let Some(command) = command_for(&command_name) else { - bail!("unknown cargo ci command `{command_name}`"); - }; - run_command(command, &raw) -} -fn run_command_group(group: &CommandGroup, mut forwarded: Vec) -> Result<()> { - if forwarded.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci {} ", group.name); - println!(); - println!("Commands:"); - for command in group.commands { - println!(" {}", command.name); - } - return Ok(()); - } - if forwarded.is_empty() { - bail!("cargo ci {} requires a command name", group.name); - } - let command_name = forwarded.remove(0); - let Some(command) = group.commands.iter().find(|candidate| candidate.name == command_name) else { - bail!("unknown cargo ci {} command `{command_name}`", group.name); - }; - run_command(command, &forwarded) -} -fn run_self_docs(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci self-docs [--check]"); - println!(); - println!("Options:"); - println!(" --check Only check for changes, do not generate the docs"); - println!(" -h, --help Print help"); - return Ok(()); - } +fn run_self_docs(check: bool) -> Result<()> { + let readme_content = include_str!("../README.md"); + let path = Path::new(README_PATH); - let check = match args { - [] => false, - [arg] if arg == "--check" => true, - _ => bail!("cargo ci self-docs accepts only --check"), - }; - - let readme = generate_cli_docs(); - let path = Path::new("tools/ci/README.md"); if check { let existing = fs::read_to_string(path).unwrap_or_default(); - if existing != readme { + if existing != readme_content { bail!("README.md is out of date. Please run `cargo ci self-docs` to update it."); } } else { - fs::write(path, readme)?; + fs::write(path, readme_content)?; } Ok(()) } fn run_dlls(args: &[String]) -> Result<()> { - if args.first().is_some_and(|arg| arg == "-h" || arg == "--help") { - println!("Usage: cargo ci dlls"); - return Ok(()); - } if !args.is_empty() { bail!("cargo ci dlls does not accept arguments"); } @@ -217,6 +107,46 @@ fn run_dlls(args: &[String]) -> Result<()> { Ok(()) } -fn generate_cli_docs() -> String { - include_str!("../README.md").to_owned() +fn run_command(cmd: CiCmd) -> Result<()> { + match cmd { + CiCmd::Test(args) => run_package("ci-test", &args.args), + CiCmd::Lint(args) => run_package("ci-lint", &args.args), + CiCmd::WasmBindings(args) => run_package("ci-wasm-bindings", &args.args), + CiCmd::Dlls(args) => run_dlls(&args.args), + CiCmd::Smoketests(args) => run_package("ci-smoketests", &args.args), + CiCmd::KeynoteBench(args) => run_package("ci-keynote-bench", &args.args), + CiCmd::UpdateFlow(args) => run_package("ci-update-flow", &args.args), + CiCmd::CliDocs(args) => run_package("ci-cli-docs", &args.args), + CiCmd::SelfDocs { check } => run_self_docs(check), + CiCmd::GlobalJsonPolicy(args) => run_package("ci-global-json-policy", &args.args), + CiCmd::PublishChecks(args) => run_package("ci-publish-checks", &args.args), + CiCmd::TypescriptTest(args) => run_package("ci-typescript-test", &args.args), + CiCmd::VersionUpgradeCheck(args) => run_package("ci-version-upgrade-check", &args.args), + CiCmd::Docs(args) => run_package("ci-docs-build", &args.args), + CiCmd::OtherWorkflows { cmd } => match cmd { + OtherWorkflowsCmd::CoordinateInternalTests(args) => run_package("ci-coordinate-internal-tests", &args.args), + OtherWorkflowsCmd::CodeownersCheck(args) => run_package("ci-codeowners-check", &args.args), + OtherWorkflowsCmd::ClaAssistant(args) => run_package("ci-cla-assistant", &args.args), + }, + } +} + +fn run_all_clap_subcommands(skip: &[String]) -> Result<()> { + for subcommand in Cli::command().get_subcommands() { + let name = subcommand.get_name(); + if skip.iter().any(|skip| skip == name) { + continue; + } + cmd!("cargo", "ci", name).run()?; + } + Ok(()) +} + +fn main() -> Result<()> { + let cli = Cli::parse(); + + match cli.cmd { + Some(cmd) => run_command(cmd), + None => run_all_clap_subcommands(&cli.skip), + } } From 539edb5293946b3b6ca959473a9141d6cea0980f Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 11:04:58 -0400 Subject: [PATCH 17/37] Address cargo ci review comments --- tools/ci/README.md | 187 ++++++----------------------- tools/ci/commands/lint/src/main.rs | 8 +- tools/ci/src/ci_docs.rs | 85 +++++++++++++ tools/ci/src/main.rs | 14 +-- 4 files changed, 133 insertions(+), 161 deletions(-) create mode 100644 tools/ci/src/ci_docs.rs diff --git a/tools/ci/README.md b/tools/ci/README.md index f0563733ee2..df8ad0980bb 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -10,7 +10,7 @@ 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 +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 @@ -19,7 +19,7 @@ Usage: cargo ci [OPTIONS] [COMMAND] **Options:** -- `--skip `: Skip specified subcommands when running all +- `--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`. @@ -27,55 +27,42 @@ When no subcommand is specified, all subcommands are run in sequence. This optio ### `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 +Usage: test [ARGS]... ``` **Options:** -- `--help`: Print help (see a summary with '-h') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `lint` -Lints the codebase - -Runs rustfmt, clippy, csharpier, TypeScript lint, and generates rust docs to ensure there are no warnings. - **Usage:** ```bash -Usage: lint +Usage: lint [ARGS]... ``` **Options:** -- `--help`: Print help (see a summary with '-h') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `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 +Usage: wasm-bindings [ARGS]... ``` **Options:** -- `--help`: Print help (see a summary with '-h') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `dlls` -Deprecated; use `cargo regen csharp dlls`. - -Builds and packs C# DLLs and NuGet packages for local Unity workflows. - **Usage:** ```bash Usage: dlls @@ -83,115 +70,55 @@ Usage: dlls **Options:** -- `--help`: Print help (see a summary with '-h') +- `--help`: Print help ### `smoketests` -Runs smoketests - -Executes the smoketests suite with some default exclusions. - **Usage:** ```bash -Usage: smoketests [OPTIONS] [ARGS]... [COMMAND] -``` - -**Options:** - -- `--server `: Run tests against a remote server instead of spawning local servers. - -When specified, tests will connect to the given URL instead of starting local server instances. Tests that require local server control (like restart tests) will be skipped. - -- `--auth-host `: Use a SpacetimeAuth-issued login for remote-server tests. - -This is required for servers that reject direct server-issued logins for privileged operations. - -Optionally accepts an auth host to pass through to `spacetime login`, for example `--auth-host=https://spacetimedb.com`. - -- `--dotnet `: -- `args `: Additional arguments to pass to the test runner -- `--help`: Print help (see a summary with '-h') - -#### `prepare` - -Only build binaries without running tests - -Use this before running `cargo test --all` to ensure binaries are built. - -**Usage:** -```bash -Usage: prepare -``` - -**Options:** - -- `--help`: Print help (see a summary with '-h') - -#### `check-mod-list` - -**Usage:** -```bash -Usage: check-mod-list +Usage: smoketests [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help -#### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - ### `keynote-bench` -Runs the keynote benchmark as a CI performance regression gate. - -Assumes release SpacetimeDB binaries and the TypeScript SDK are already built, runs the keynote SpacetimeDB benchmark for 60 seconds against the TypeScript and Rust modules, and fails if throughput is below 275K TPS for TypeScript or 300K TPS for Rust. - **Usage:** ```bash -Usage: keynote-bench +Usage: keynote-bench [ARGS]... ``` **Options:** -- `--help`: Print help (see a summary with '-h') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `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] +Usage: update-flow [ARGS]... ``` **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 disabled. -- `--help`: Print help (see a summary with '-h') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `cli-docs` **Usage:** ```bash -Usage: cli-docs [OPTIONS] +Usage: cli-docs [ARGS]... ``` **Options:** -- `--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') +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help ### `self-docs` @@ -203,61 +130,66 @@ Usage: self-docs [OPTIONS] **Options:** - `--check`: Only check for changes, do not generate the docs -- `--help`: Print help (see a summary with '-h') +- `--help`: Print help ### `global-json-policy` **Usage:** ```bash -Usage: global-json-policy +Usage: global-json-policy [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help ### `publish-checks` **Usage:** ```bash -Usage: publish-checks +Usage: publish-checks [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help ### `typescript-test` **Usage:** ```bash -Usage: typescript-test +Usage: typescript-test [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help ### `version-upgrade-check` **Usage:** ```bash -Usage: version-upgrade-check +Usage: version-upgrade-check [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help ### `docs` **Usage:** ```bash -Usage: docs +Usage: docs [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help ### `other-workflows` @@ -275,77 +207,38 @@ Usage: other-workflows **Usage:** ```bash -Usage: coordinate-internal-tests [OPTIONS] --public-sha +Usage: coordinate-internal-tests [ARGS]... ``` **Options:** -- `--public-sha `: Immutable public commit to test -- `--public-pr-number `: Public pull request number, when coordinating a pull request run +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help #### `codeowners-check` **Usage:** ```bash -Usage: codeowners-check --base-ref --pr-number +Usage: codeowners-check [ARGS]... ``` **Options:** -- `--base-ref `: Git ref to compare against, usually origin/ -- `--pr-number `: Pull request number to inspect for approval state +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help #### `cla-assistant` **Usage:** ```bash -Usage: cla-assistant +Usage: cla-assistant [ARGS]... ``` **Options:** +- `args `: Arguments forwarded to the split CI command package - `--help`: Print help -##### `retry` - -**Usage:** -```bash -Usage: retry [OPTIONS] --pr-number -``` - -**Options:** - -- `--pr-number `: Pull request number to check -- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY -- `--help`: Print help - -##### `status` - -**Usage:** -```bash -Usage: status [OPTIONS] <--pr |--sha > -``` - -**Options:** - -- `--pr `: Pull request number whose head commit should be checked -- `--sha `: Commit SHA to check -- `--repo `: Repository in `owner/name` form. Defaults to GITHUB_REPOSITORY -- `--help`: Print help - -##### `help` - -**Usage:** -```bash -Usage: help [COMMAND]... -``` - -**Options:** - -- `subcommand `: Print help for the subcommand(s) - #### `help` **Usage:** diff --git a/tools/ci/commands/lint/src/main.rs b/tools/ci/commands/lint/src/main.rs index ba977131f0b..d8ada4e7817 100644 --- a/tools/ci/commands/lint/src/main.rs +++ b/tools/ci/commands/lint/src/main.rs @@ -1,7 +1,7 @@ #![allow(clippy::disallowed_macros)] use anyhow::{bail, Context, Result}; -use ci_common::pnpm; +use ci_common::{ensure_repo_root, pnpm}; use duct::cmd; use serde_json::Value; use std::collections::BTreeSet; @@ -270,12 +270,6 @@ fn check_codex_plugin_skills_sync() -> Result<()> { Ok(()) } -fn ensure_repo_root() -> Result<()> { - if !Path::new("Cargo.toml").exists() { - bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); - } - Ok(()) -} fn tracked_rs_files_under(path: &str) -> Result> { let output = cmd!("git", "ls-files", "--", path).read()?; Ok(output diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs new file mode 100644 index 00000000000..3fadcdaf0cc --- /dev/null +++ b/tools/ci/src/ci_docs.rs @@ -0,0 +1,85 @@ +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); + + 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 mut 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()); + if let Some(value_names) = arg.get_value_names().filter(|_| arg.get_action().takes_values()) { + for value_name in value_names { + names.push_str(&format!(" <{value_name}>")); + } + } + let help = arg + .get_long_help() + .or_else(|| arg.get_help()) + .map(|help| help.to_string()) + .unwrap_or_else(|| { + eprintln!("Warning: argument `{}` is missing help text", arg.get_id()); + "".to_string() + }); + if help.is_empty() { + options.push_str(&format!("- `{names}`:\n")); + } else { + options.push_str(&format!( + "- `{}`: {}\n{}", + names, + help, + if help.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 161d7fcdd61..d472db14e51 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -8,6 +8,8 @@ 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. @@ -32,6 +34,7 @@ struct Cli { #[derive(Args)] struct ForwardedArgs { + /// Arguments forwarded to the split CI command package. #[arg(trailing_var_arg = true, allow_hyphen_values = true)] args: Vec, } @@ -45,7 +48,7 @@ enum CiCmd { /// Tests Wasm bindings. WasmBindings(ForwardedArgs), /// Deprecated; use `cargo regen csharp dlls`. - Dlls(ForwardedArgs), + Dlls, /// Runs smoketests. Smoketests(ForwardedArgs), /// Runs the keynote benchmark as a CI performance regression gate. @@ -84,7 +87,7 @@ fn run_package(package: &str, args: &[String]) -> Result<()> { } fn run_self_docs(check: bool) -> Result<()> { - let readme_content = include_str!("../README.md"); + let readme_content = ci_docs::generate_cli_docs(); let path = Path::new(README_PATH); if check { @@ -98,10 +101,7 @@ fn run_self_docs(check: bool) -> Result<()> { Ok(()) } -fn run_dlls(args: &[String]) -> Result<()> { - if !args.is_empty() { - bail!("cargo ci dlls does not accept arguments"); - } +fn run_dlls() -> Result<()> { eprintln!("warning: `cargo ci dlls` is deprecated; use `cargo regen csharp dlls` instead"); cmd!("cargo", "regen", "csharp", "dlls").run()?; Ok(()) @@ -112,7 +112,7 @@ fn run_command(cmd: CiCmd) -> Result<()> { CiCmd::Test(args) => run_package("ci-test", &args.args), CiCmd::Lint(args) => run_package("ci-lint", &args.args), CiCmd::WasmBindings(args) => run_package("ci-wasm-bindings", &args.args), - CiCmd::Dlls(args) => run_dlls(&args.args), + CiCmd::Dlls => run_dlls(), CiCmd::Smoketests(args) => run_package("ci-smoketests", &args.args), CiCmd::KeynoteBench(args) => run_package("ci-keynote-bench", &args.args), CiCmd::UpdateFlow(args) => run_package("ci-update-flow", &args.args), From 430a67874c7ebe4eb75d078cdfd888f92fdef024 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 11:08:50 -0400 Subject: [PATCH 18/37] Allow CLI output in split CI commands --- tools/ci/commands/cla-assistant/src/main.rs | 2 ++ tools/ci/commands/coordinate-internal-tests/src/main.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tools/ci/commands/cla-assistant/src/main.rs b/tools/ci/commands/cla-assistant/src/main.rs index a7aa580734b..ac27b6b423b 100644 --- a/tools/ci/commands/cla-assistant/src/main.rs +++ b/tools/ci/commands/cla-assistant/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::disallowed_macros)] + use std::collections::BTreeMap; use std::env; diff --git a/tools/ci/commands/coordinate-internal-tests/src/main.rs b/tools/ci/commands/coordinate-internal-tests/src/main.rs index e46858a32c3..234a9f107cf 100644 --- a/tools/ci/commands/coordinate-internal-tests/src/main.rs +++ b/tools/ci/commands/coordinate-internal-tests/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::disallowed_macros)] + use anyhow::{bail, ensure, Context, Result}; use clap::Parser; use duct::{cmd, Expression}; From 07cf1ed2959b91fd946a6b0b0c7f8665bd224c84 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 11:17:40 -0400 Subject: [PATCH 19/37] Preserve smoketest runner args --- tools/ci/src/main.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index d472db14e51..dba152f8e02 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -86,6 +86,14 @@ fn run_package(package: &str, args: &[String]) -> Result<()> { Ok(()) } +fn run_smoketests(args: &[String]) -> Result<()> { + let mut args = args.to_vec(); + if args.first().is_some_and(|arg| arg.starts_with("--test-")) { + args.insert(0, "--".to_string()); + } + run_package("ci-smoketests", &args) +} + fn run_self_docs(check: bool) -> Result<()> { let readme_content = ci_docs::generate_cli_docs(); let path = Path::new(README_PATH); @@ -113,7 +121,7 @@ fn run_command(cmd: CiCmd) -> Result<()> { CiCmd::Lint(args) => run_package("ci-lint", &args.args), CiCmd::WasmBindings(args) => run_package("ci-wasm-bindings", &args.args), CiCmd::Dlls => run_dlls(), - CiCmd::Smoketests(args) => run_package("ci-smoketests", &args.args), + CiCmd::Smoketests(args) => run_smoketests(&args.args), CiCmd::KeynoteBench(args) => run_package("ci-keynote-bench", &args.args), CiCmd::UpdateFlow(args) => run_package("ci-update-flow", &args.args), CiCmd::CliDocs(args) => run_package("ci-cli-docs", &args.args), From c2ba67729d35db76c5bd2f91c1e77759208280c6 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 13:59:47 -0400 Subject: [PATCH 20/37] Address cargo ci review followups --- tools/ci/README.md | 4 ++-- tools/ci/commands/cla-assistant/src/main.rs | 8 ++------ tools/ci/src/main.rs | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/ci/README.md b/tools/ci/README.md index df8ad0980bb..108cc61642b 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -10,7 +10,7 @@ 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. +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 @@ -19,7 +19,7 @@ Usage: cargo ci [OPTIONS] [COMMAND] **Options:** -- `--skip `: Skip specified subcommands when running all. +- `--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`. diff --git a/tools/ci/commands/cla-assistant/src/main.rs b/tools/ci/commands/cla-assistant/src/main.rs index ac27b6b423b..037111a7663 100644 --- a/tools/ci/commands/cla-assistant/src/main.rs +++ b/tools/ci/commands/cla-assistant/src/main.rs @@ -59,17 +59,13 @@ struct Cli { cmd: ClaAssistantCmd, } -fn run(cmd: ClaAssistantCmd) -> Result<()> { - match cmd { +fn main() -> Result<()> { + match Cli::parse().cmd { ClaAssistantCmd::Retry(args) => retry(args), ClaAssistantCmd::Status(args) => status(args), } } -fn main() -> Result<()> { - run(Cli::parse().cmd) -} - fn retry(args: RetryArgs) -> Result<()> { let repo = Repo::from_arg_or_env(args.repo)?; let token = env::var("GITHUB_TOKEN").context("GITHUB_TOKEN is required")?; diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index dba152f8e02..9176d192a1c 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -16,14 +16,14 @@ mod ci_docs; /// /// 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. +/// also be run locally #[derive(Parser)] #[command(name = "cargo ci", subcommand_required = false, arg_required_else_help = false)] struct Cli { #[command(subcommand)] cmd: Option, - /// Skip specified subcommands when running all. + /// 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` From d02e783742c9cc9227ecc950dc0684bfcb988ea7 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:01:43 -0400 Subject: [PATCH 21/37] Inline coordinate internal tests entrypoint --- tools/ci/commands/coordinate-internal-tests/src/main.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tools/ci/commands/coordinate-internal-tests/src/main.rs b/tools/ci/commands/coordinate-internal-tests/src/main.rs index 234a9f107cf..ec2f4b37307 100644 --- a/tools/ci/commands/coordinate-internal-tests/src/main.rs +++ b/tools/ci/commands/coordinate-internal-tests/src/main.rs @@ -452,8 +452,8 @@ fn write_github_output(name: &str, value: impl std::fmt::Display) -> Result<()> writeln!(output, "{name}={value}").context("failed to write GITHUB_OUTPUT") } -/// Coordinates the public Internal Tests run without checking out or executing private code. -fn coordinate(args: Args) -> Result<()> { +fn main() -> Result<()> { + let args = Args::parse(); let private_source = resolve_private_source(args.public_pr_number)?; let coordinated = match private_source { @@ -468,10 +468,6 @@ fn coordinate(args: Args) -> Result<()> { Ok(()) } -fn main() -> Result<()> { - coordinate(Args::parse()) -} - #[cfg(test)] mod tests { use super::*; From 3d5d8fa65708711f8f7ace7fd0d3f7da38dab10c Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Sun, 9 Aug 2026 11:02:31 -0700 Subject: [PATCH 22/37] Apply suggestion from @bfops Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com> --- tools/ci/commands/coordinate-internal-tests/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci/commands/coordinate-internal-tests/src/main.rs b/tools/ci/commands/coordinate-internal-tests/src/main.rs index ec2f4b37307..ba6252e3484 100644 --- a/tools/ci/commands/coordinate-internal-tests/src/main.rs +++ b/tools/ci/commands/coordinate-internal-tests/src/main.rs @@ -452,6 +452,7 @@ fn write_github_output(name: &str, value: impl std::fmt::Display) -> Result<()> writeln!(output, "{name}={value}").context("failed to write GITHUB_OUTPUT") } +/// Coordinates the public Internal Tests run without checking out or executing private code. fn main() -> Result<()> { let args = Args::parse(); let private_source = resolve_private_source(args.public_pr_number)?; From 6440cbcc6a4589635c23195b0565ca3418b4b88a Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:05:18 -0400 Subject: [PATCH 23/37] Add cargo ci dep-check --- .github/workflows/ci.yml | 6 ++++++ Cargo.lock | 9 +++++++++ Cargo.toml | 1 + tools/ci/README.md | 12 ++++++++++++ tools/ci/commands/dep-check/Cargo.toml | 9 +++++++++ tools/ci/commands/dep-check/src/main.rs | 13 +++++++++++++ tools/ci/src/main.rs | 3 +++ 7 files changed, 53 insertions(+) create mode 100644 tools/ci/commands/dep-check/Cargo.toml create mode 100644 tools/ci/commands/dep-check/src/main.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c39b4a2458e..8bc22b5c9d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -445,6 +445,12 @@ jobs: - name: Run ci lint run: cargo ci lint + - name: Install cargo-machete + run: cargo install cargo-machete --version 0.9.2 --locked + + - name: Run dependency check + run: cargo ci dep-check + - name: Upload public lint Cargo timing reports if: always() uses: actions/upload-artifact@v4 diff --git a/Cargo.lock b/Cargo.lock index 18004020f46..884c7e9d598 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,6 +955,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ci-dep-check" +version = "0.1.0" +dependencies = [ + "anyhow", + "ci-common", + "duct", +] + [[package]] name = "ci-docs-build" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index a1a94f15fc3..15695cf4b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ members = [ "tools/ci/commands/keynote-bench", "tools/ci/commands/update-flow", "tools/ci/commands/cli-docs", + "tools/ci/commands/dep-check", "tools/ci/commands/global-json-policy", "tools/ci/commands/publish-checks", "tools/ci/commands/typescript-test", diff --git a/tools/ci/README.md b/tools/ci/README.md index 108cc61642b..7529bc82ad1 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -120,6 +120,18 @@ Usage: cli-docs [ARGS]... - `args `: Arguments forwarded to the split CI command package - `--help`: Print help +### `dep-check` + +**Usage:** +```bash +Usage: dep-check [ARGS]... +``` + +**Options:** + +- `args `: Arguments forwarded to the split CI command package +- `--help`: Print help + ### `self-docs` **Usage:** diff --git a/tools/ci/commands/dep-check/Cargo.toml b/tools/ci/commands/dep-check/Cargo.toml new file mode 100644 index 00000000000..3c0e49f52b1 --- /dev/null +++ b/tools/ci/commands/dep-check/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "ci-dep-check" +version = "0.1.0" +edition.workspace = true + +[dependencies] +anyhow.workspace = true +ci-common = { path = "../../common" } +duct.workspace = true diff --git a/tools/ci/commands/dep-check/src/main.rs b/tools/ci/commands/dep-check/src/main.rs new file mode 100644 index 00000000000..d2924bd5a7e --- /dev/null +++ b/tools/ci/commands/dep-check/src/main.rs @@ -0,0 +1,13 @@ +use anyhow::Result; +use ci_common::ensure_repo_root; +use duct::cmd; + +fn main() -> Result<()> { + ensure_repo_root()?; + let mut args = std::env::args_os().skip(1).collect::>(); + if args.is_empty() { + args.push("tools/ci".into()); + } + cmd("cargo-machete", args).run()?; + Ok(()) +} diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 9176d192a1c..ffc752c69b7 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -56,6 +56,8 @@ enum CiCmd { /// Tests the update flow. UpdateFlow(ForwardedArgs), CliDocs(ForwardedArgs), + /// Checks Rust manifests for unused dependencies. + DepCheck(ForwardedArgs), SelfDocs { /// Only check for changes, do not generate the docs. #[arg(long)] @@ -125,6 +127,7 @@ fn run_command(cmd: CiCmd) -> Result<()> { CiCmd::KeynoteBench(args) => run_package("ci-keynote-bench", &args.args), CiCmd::UpdateFlow(args) => run_package("ci-update-flow", &args.args), CiCmd::CliDocs(args) => run_package("ci-cli-docs", &args.args), + CiCmd::DepCheck(args) => run_package("ci-dep-check", &args.args), CiCmd::SelfDocs { check } => run_self_docs(check), CiCmd::GlobalJsonPolicy(args) => run_package("ci-global-json-policy", &args.args), CiCmd::PublishChecks(args) => run_package("ci-publish-checks", &args.args), From c1e9dbd324c35d71d7ff757e29732b5d5a78b605 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:15:02 -0400 Subject: [PATCH 24/37] Render split cargo ci help from child commands --- tools/ci/README.md | 135 +++++++++++++++++++++++++++------------- tools/ci/src/ci_docs.rs | 47 +++++++++++--- tools/ci/src/main.rs | 2 +- 3 files changed, 133 insertions(+), 51 deletions(-) diff --git a/tools/ci/README.md b/tools/ci/README.md index 108cc61642b..aaf5b09477b 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -74,15 +74,46 @@ Usage: dlls ### `smoketests` -**Usage:** -```bash -Usage: smoketests [ARGS]... -``` +**Help:** +```text +This command builds the binaries needed by the smoketests, then runs them. This prevents race +conditions when running tests in parallel with nextest, where multiple test processes might try to +build the same binaries simultaneously -**Options:** +Usage: ci-smoketests [OPTIONS] [ARGS]... [COMMAND] -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help +Commands: + prepare Only build binaries without running tests + check-mod-list + help Print this message or the help of the given subcommand(s) + +Arguments: + [ARGS]... + Additional arguments to pass to the test runner + +Options: + --server + Run tests against a remote server instead of spawning local servers. + + When specified, tests will connect to the given URL instead of starting local server + instances. Tests that require local server control (like restart tests) will be skipped. + + --auth-host[=] + Use a SpacetimeAuth-issued login for remote-server tests. + + This is required for servers that reject direct server-issued logins for privileged + operations. + + Optionally accepts an auth host to pass through to `spacetime login`, for example + `--auth-host=https://spacetimedb.com`. + + --dotnet + [default: true] + [possible values: true, false] + + -h, --help + Print help (see a summary with '-h') +``` ### `keynote-bench` @@ -98,27 +129,37 @@ Usage: keynote-bench [ARGS]... ### `update-flow` -**Usage:** -```bash -Usage: update-flow [ARGS]... -``` +**Help:** +```text +Usage: ci-update-flow [OPTIONS] -**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. -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help + --github-token-auth + Whether to enable github token authentication feature when building the update binary. By + default this is disabled. + + -h, --help + Print help (see a summary with '-h') +``` ### `cli-docs` -**Usage:** -```bash -Usage: cli-docs [ARGS]... -``` +**Help:** +```text +Usage: ci-cli-docs [OPTIONS] -**Options:** +Options: + --spacetime-path + specify a custom path to the SpacetimeDB repository root (where the main Cargo.toml is + located) -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help + -h, --help + Print help (see a summary with '-h') +``` ### `self-docs` @@ -205,39 +246,47 @@ Usage: other-workflows #### `coordinate-internal-tests` -**Usage:** -```bash -Usage: coordinate-internal-tests [ARGS]... -``` +**Help:** +```text +Selects or starts the private workflow for a public Internal Tests run -**Options:** +Usage: ci-coordinate-internal-tests [OPTIONS] --public-sha -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help +Options: + --public-sha + Immutable public commit to test + --public-pr-number + Public pull request number, when coordinating a pull request run + -h, --help + Print help +``` #### `codeowners-check` -**Usage:** -```bash -Usage: codeowners-check [ARGS]... -``` - -**Options:** +**Help:** +```text +Usage: ci-codeowners-check --base-ref --pr-number -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help +Options: + --base-ref Git ref to compare against, usually origin/ + --pr-number Pull request number to inspect for approval state + -h, --help Print help +``` #### `cla-assistant` -**Usage:** -```bash -Usage: cla-assistant [ARGS]... -``` +**Help:** +```text +Usage: ci-cla-assistant -**Options:** +Commands: + retry Retries CLA Assistant if `license/cla` is the only remaining PR blocker + status Returns the `license/cla` status for a pull request or commit SHA + help Print this message or the help of the given subcommand(s) -- `args `: Arguments forwarded to the split CI command package -- `--help`: Print help +Options: + -h, --help Print help +``` #### `help` diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs index 3fadcdaf0cc..8396342aad2 100644 --- a/tools/ci/src/ci_docs.rs +++ b/tools/ci/src/ci_docs.rs @@ -1,13 +1,15 @@ +use anyhow::{Context, Result}; use clap::{Command, CommandFactory}; +use duct::cmd; use crate::Cli; // TODO: use clap_markdown instead of this custom implementation in the future -pub fn generate_cli_docs() -> String { +pub fn generate_cli_docs() -> Result { let mut cli = Cli::command(); - let usage = generate_markdown(&mut cli, 2); + let usage = generate_markdown(&mut cli, 2, &[])?; - format!( + Ok(format!( "\ # SpacetimeDB's cargo ci @@ -24,15 +26,44 @@ This document is auto-generated by running: cargo ci self-docs ```", usage - ) + )) } -fn generate_markdown(cmd: &mut Command, heading_level: usize) -> String { +fn split_command_package(path: &[String]) -> Option<&'static str> { + match path { + [cmd] if cmd == "smoketests" => Some("ci-smoketests"), + [cmd] if cmd == "update-flow" => Some("ci-update-flow"), + [cmd] if cmd == "cli-docs" => Some("ci-cli-docs"), + [group, cmd] if group == "other-workflows" && cmd == "coordinate-internal-tests" => { + Some("ci-coordinate-internal-tests") + } + [group, cmd] if group == "other-workflows" && cmd == "codeowners-check" => Some("ci-codeowners-check"), + [group, cmd] if group == "other-workflows" && cmd == "cla-assistant" => Some("ci-cla-assistant"), + _ => None, + } +} + +fn package_help(package: &str) -> Result { + let help = cmd!("cargo", "run", "--quiet", "--package", package, "--", "--help") + .read() + .with_context(|| format!("failed to render help for {package}"))?; + Ok(help.lines().map(str::trim_end).collect::>().join("\n")) +} + +fn generate_markdown(cmd: &mut Command, heading_level: usize, path: &[String]) -> Result { let mut out = String::new(); let heading = "#".repeat(heading_level); out.push_str(&format!("{} `{}`\n\n", heading, cmd.get_name())); + if let Some(package) = split_command_package(path) { + let help = package_help(package)?; + out.push_str("**Help:**\n```text\n"); + out.push_str(help.trim_end()); + out.push_str("\n```\n\n"); + return Ok(out); + } + if let Some(long_about) = cmd.get_long_about() { out.push_str(&format!("{}\n\n", long_about)); } @@ -78,8 +109,10 @@ fn generate_markdown(cmd: &mut Command, heading_level: usize) -> String { } for sub in cmd.get_subcommands_mut() { - out.push_str(&generate_markdown(sub, heading_level + 1)); + let mut sub_path = path.to_vec(); + sub_path.push(sub.get_name().to_owned()); + out.push_str(&generate_markdown(sub, heading_level + 1, &sub_path)?); } - out + Ok(out) } diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 9176d192a1c..74728e3f972 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -95,7 +95,7 @@ fn run_smoketests(args: &[String]) -> Result<()> { } fn run_self_docs(check: bool) -> Result<()> { - let readme_content = ci_docs::generate_cli_docs(); + let readme_content = ci_docs::generate_cli_docs()?; let path = Path::new(README_PATH); if check { From 47a7090d24db923bdd4daa2cc3485b80d73b956c Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:26:26 -0400 Subject: [PATCH 25/37] Restore dep-check lint workflow --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c39b4a2458e..57aacf41188 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -445,6 +445,12 @@ jobs: - name: Run ci lint run: cargo ci lint + - name: Install cargo-machete + run: cargo install cargo-machete --locked + + - name: Run dependency check + run: cargo ci dep-check + - name: Upload public lint Cargo timing reports if: always() uses: actions/upload-artifact@v4 From 323cd2a2010aac1ac86e124dde4bf6e1ef9565d0 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:29:43 -0400 Subject: [PATCH 26/37] Delegate split cargo ci help --- tools/ci/src/ci_docs.rs | 27 +++++++++---------------- tools/ci/src/main.rs | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/tools/ci/src/ci_docs.rs b/tools/ci/src/ci_docs.rs index 8396342aad2..bd8c7db00f5 100644 --- a/tools/ci/src/ci_docs.rs +++ b/tools/ci/src/ci_docs.rs @@ -29,24 +29,14 @@ cargo ci self-docs )) } -fn split_command_package(path: &[String]) -> Option<&'static str> { - match path { - [cmd] if cmd == "smoketests" => Some("ci-smoketests"), - [cmd] if cmd == "update-flow" => Some("ci-update-flow"), - [cmd] if cmd == "cli-docs" => Some("ci-cli-docs"), - [group, cmd] if group == "other-workflows" && cmd == "coordinate-internal-tests" => { - Some("ci-coordinate-internal-tests") - } - [group, cmd] if group == "other-workflows" && cmd == "codeowners-check" => Some("ci-codeowners-check"), - [group, cmd] if group == "other-workflows" && cmd == "cla-assistant" => Some("ci-cla-assistant"), - _ => None, - } -} +fn command_help(path: &[String]) -> Result { + let mut args = vec!["run", "--quiet", "--package", "ci", "--"]; + args.extend(path.iter().map(String::as_str)); + args.push("--help"); -fn package_help(package: &str) -> Result { - let help = cmd!("cargo", "run", "--quiet", "--package", package, "--", "--help") + let help = cmd("cargo", args) .read() - .with_context(|| format!("failed to render help for {package}"))?; + .with_context(|| format!("failed to render help for `cargo ci {}`", path.join(" ")))?; Ok(help.lines().map(str::trim_end).collect::>().join("\n")) } @@ -56,8 +46,9 @@ fn generate_markdown(cmd: &mut Command, heading_level: usize, path: &[String]) - let heading = "#".repeat(heading_level); out.push_str(&format!("{} `{}`\n\n", heading, cmd.get_name())); - if let Some(package) = split_command_package(path) { - let help = package_help(package)?; + let path_parts = path.iter().map(String::as_str).collect::>(); + if crate::split_command_help_package(&path_parts).is_some() { + let help = command_help(path)?; out.push_str("**Help:**\n```text\n"); out.push_str(help.trim_end()); out.push_str("\n```\n\n"); diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 74728e3f972..3908621996e 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -86,6 +86,46 @@ fn run_package(package: &str, args: &[String]) -> Result<()> { Ok(()) } +pub(crate) fn split_command_help_package(path: &[&str]) -> Option<&'static str> { + match path { + ["smoketests"] => Some("ci-smoketests"), + ["update-flow"] => Some("ci-update-flow"), + ["cli-docs"] => Some("ci-cli-docs"), + ["other-workflows", "coordinate-internal-tests"] => Some("ci-coordinate-internal-tests"), + ["other-workflows", "codeowners-check"] => Some("ci-codeowners-check"), + ["other-workflows", "cla-assistant"] => Some("ci-cla-assistant"), + _ => None, + } +} + +fn split_help_command(args: &[String]) -> Option<(&'static str, Vec)> { + if let Some(help_path) = args.strip_prefix(&["help".to_string()]) { + for path_len in [2, 1] { + if help_path.len() >= path_len { + let path = help_path[..path_len].iter().map(String::as_str).collect::>(); + if let Some(package) = split_command_help_package(&path) { + return Some((package, vec!["--help".to_string()])); + } + } + } + } + + if !args.iter().any(|arg| arg == "--help" || arg == "-h") { + return None; + } + + for path_len in [2, 1] { + if args.len() >= path_len { + let path = args[..path_len].iter().map(String::as_str).collect::>(); + if let Some(package) = split_command_help_package(&path) { + return Some((package, args[path_len..].to_vec())); + } + } + } + + None +} + fn run_smoketests(args: &[String]) -> Result<()> { let mut args = args.to_vec(); if args.first().is_some_and(|arg| arg.starts_with("--test-")) { @@ -151,6 +191,11 @@ fn run_all_clap_subcommands(skip: &[String]) -> Result<()> { } fn main() -> Result<()> { + let args = std::env::args().skip(1).collect::>(); + if let Some((package, args)) = split_help_command(&args) { + return run_package(package, &args); + } + let cli = Cli::parse(); match cli.cmd { From d7e4ff07d264459cd3b6e19987b206ed90bc58a6 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:41:52 -0400 Subject: [PATCH 27/37] Remove unused dependencies for dep-check --- Cargo.lock | 239 +----------------- crates/bench/Cargo.toml | 8 +- crates/bindings-macro/Cargo.toml | 1 - .../test-app/server/Cargo.toml | 2 - .../test-react-router-app/server/Cargo.toml | 2 - .../test-solid-router/server/Cargo.toml | 2 - crates/bindings/Cargo.toml | 5 +- crates/cli/Cargo.toml | 8 - crates/client-api-messages/Cargo.toml | 2 - crates/client-api/Cargo.toml | 13 +- crates/commitlog/Cargo.toml | 1 - crates/core/Cargo.toml | 22 -- crates/datastore/Cargo.toml | 2 - crates/dst/Cargo.toml | 2 - crates/durability/Cargo.toml | 1 - crates/execution/Cargo.toml | 1 - crates/expr/Cargo.toml | 2 - crates/guard/Cargo.toml | 1 - crates/lib/Cargo.toml | 11 +- crates/query/Cargo.toml | 5 - crates/sats/Cargo.toml | 1 - crates/schema/Cargo.toml | 2 - .../add-remove-index-indexed/Cargo.toml | 1 - .../modules/add-remove-index/Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../Cargo.toml | 1 - .../smoketests/modules/call-empty/Cargo.toml | 1 - .../Cargo.toml | 1 - .../client-connection-reject/Cargo.toml | 1 - .../modules/confirmed-reads/Cargo.toml | 1 - .../modules/delete-database/Cargo.toml | 1 - crates/smoketests/modules/dml/Cargo.toml | 1 - .../fail-initial-publish-broken/Cargo.toml | 1 - .../fail-initial-publish-fixed/Cargo.toml | 1 - .../modules/hotswap-basic/Cargo.toml | 1 - .../modules/hotswap-updated/Cargo.toml | 1 - .../smoketests/modules/http-egress/Cargo.toml | 1 - .../modules/http-routes-example/Cargo.toml | 1 - .../modules/http-routes-full-uri/Cargo.toml | 1 - .../http-routes-request-body/Cargo.toml | 1 - .../http-routes-strict-non-root/Cargo.toml | 1 - .../http-routes-strict-root/Cargo.toml | 1 - .../smoketests/modules/http-routes/Cargo.toml | 1 - .../modules/modules-breaking/Cargo.toml | 1 - crates/smoketests/modules/noop/Cargo.toml | 1 - .../smoketests/modules/panic-error/Cargo.toml | 1 - .../modules/permissions-lifecycle/Cargo.toml | 1 - .../modules/permissions-private/Cargo.toml | 1 - crates/smoketests/modules/pg-wire/Cargo.toml | 1 - .../rls-broken-private-table/Cargo.toml | 1 - .../modules/rls-no-filter/Cargo.toml | 1 - .../modules/rls-with-filter/Cargo.toml | 1 - crates/smoketests/modules/rls/Cargo.toml | 1 - .../modules/schedule-volatile/Cargo.toml | 1 - .../smoketests/modules/sql-format/Cargo.toml | 1 - .../smoketests/modules/views-basic/Cargo.toml | 1 - .../modules/views-broken-namespace/Cargo.toml | 1 - .../views-broken-return-type/Cargo.toml | 1 - .../modules/views-callable/Cargo.toml | 1 - crates/snapshot/Cargo.toml | 1 - crates/sql-parser/Cargo.toml | 1 - crates/sqltest/Cargo.toml | 8 +- crates/standalone/Cargo.toml | 10 +- crates/table/Cargo.toml | 1 - crates/testing/Cargo.toml | 4 +- crates/update/Cargo.toml | 1 - modules/benchmarks/Cargo.toml | 2 - modules/keynote-benchmarks/Cargo.toml | 1 - modules/perf-test/Cargo.toml | 1 - modules/sdk-test-case-conversion/Cargo.toml | 1 - .../sdk-test-connect-disconnect/Cargo.toml | 1 - .../sdk-test-procedure-concurrency/Cargo.toml | 2 - modules/sdk-test-procedure/Cargo.toml | 3 - modules/sdk-test-view/Cargo.toml | 3 - modules/sdk-test/Cargo.toml | 1 - sdks/rust/Cargo.toml | 6 +- .../connect_disconnect_client/Cargo.toml | 5 +- sdks/rust/tests/event-table-client/Cargo.toml | 3 + .../procedural-view-pk-client/Cargo.toml | 3 + sdks/rust/tests/procedure-client/Cargo.toml | 3 + .../procedure-concurrency-client/Cargo.toml | 3 + sdks/rust/tests/test-client/Cargo.toml | 3 + sdks/rust/tests/test-counter/Cargo.toml | 1 - sdks/rust/tests/view-client/Cargo.toml | 3 + sdks/rust/tests/view-pk-client/Cargo.toml | 3 + templates/chat-console-rs/Cargo.toml | 3 + tools/replace-spacetimedb/Cargo.toml | 1 - .../src/templates/rust/server/Cargo.toml | 2 - 93 files changed, 54 insertions(+), 401 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 884c7e9d598..6a080cdff18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,12 +156,6 @@ dependencies = [ "backtrace", ] -[[package]] -name = "anymap" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" - [[package]] name = "anymap3" version = "1.0.1" @@ -480,7 +474,6 @@ dependencies = [ name = "benchmarks-module" version = "0.1.0" dependencies = [ - "anyhow", "spacetimedb", ] @@ -654,16 +647,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "byte-unit" -version = "4.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c" -dependencies = [ - "serde", - "utf8-width", -] - [[package]] name = "bytecheck" version = "0.6.12" @@ -2113,15 +2096,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "email_address" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" -dependencies = [ - "serde", -] - [[package]] name = "embedded-io" version = "0.4.0" @@ -3053,17 +3027,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.12" @@ -3509,15 +3472,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "imara-diff" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d34b7d42178945f775e84bc4c36dde7c1c6cdfea656d3354d009056f2bb3d2" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -3876,7 +3830,6 @@ dependencies = [ name = "keynote-benchmarks" version = "0.1.0" dependencies = [ - "log", "spacetimedb", ] @@ -4161,12 +4114,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.2.0" @@ -5475,7 +5422,6 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" name = "perf-test-module" version = "0.1.0" dependencies = [ - "log", "spacetimedb", ] @@ -5544,17 +5490,6 @@ dependencies = [ "serde", ] -[[package]] -name = "pg_interval" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceff720b1579b383347d48e5df5f604042adaa6a06f640b1f1e3c065f40766d" -dependencies = [ - "bytes", - "chrono", - "postgres-types", -] - [[package]] name = "pgwire" version = "0.37.0" @@ -5728,15 +5663,6 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" -[[package]] -name = "portpicker" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be97d76faf1bfab666e1375477b23fde79eccf0276e9b63b92a39d676a889ba9" -dependencies = [ - "rand 0.8.5", -] - [[package]] name = "postcard" version = "1.1.3" @@ -5749,18 +5675,6 @@ dependencies = [ "serde", ] -[[package]] -name = "postgres-derive" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56df96f5394370d1b20e49de146f9e6c25aa9ae750f449c9d665eafecb3ccae6" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.107", -] - [[package]] name = "postgres-protocol" version = "0.6.9" @@ -5789,7 +5703,6 @@ dependencies = [ "bytes", "chrono", "fallible-iterator 0.2.0", - "postgres-derive", "postgres-protocol", "serde_core", "serde_json", @@ -6512,7 +6425,6 @@ version = "0.1.0" dependencies = [ "clap 4.5.50", "ignore", - "memchr", "regex", ] @@ -7331,7 +7243,6 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" name = "sdk-test-case-conversion" version = "0.1.0" dependencies = [ - "log", "spacetimedb", ] @@ -7347,7 +7258,6 @@ name = "sdk-test-module" version = "0.1.0" dependencies = [ "anyhow", - "log", "paste", "spacetimedb", ] @@ -7363,9 +7273,7 @@ dependencies = [ name = "sdk-test-procedure-concurrency-module" version = "0.1.0" dependencies = [ - "anyhow", "log", - "paste", "spacetimedb", ] @@ -7373,9 +7281,6 @@ dependencies = [ name = "sdk-test-procedure-module" version = "0.1.0" dependencies = [ - "anyhow", - "log", - "paste", "spacetimedb", ] @@ -7383,9 +7288,6 @@ dependencies = [ name = "sdk-test-view" version = "0.1.0" dependencies = [ - "anyhow", - "log", - "paste", "spacetimedb", ] @@ -7865,7 +7767,6 @@ dependencies = [ name = "spacetime-module" version = "0.1.0" dependencies = [ - "log", "spacetimedb", ] @@ -7876,13 +7777,11 @@ dependencies = [ "anyhow", "bytemuck", "bytes", - "derive_more 0.99.20", "getrandom 0.2.16", "http 1.3.1", "insta", "log", "rand 0.8.5", - "scoped-tls", "serde_json", "spacetimedb-bindings-macro", "spacetimedb-bindings-sys", @@ -7911,13 +7810,10 @@ version = "2.8.0" dependencies = [ "ahash 0.8.12", "anyhow", - "anymap", - "byte-unit", "clap 4.5.50", "convert_case 0.6.0", "criterion", "foldhash 0.2.0", - "futures", "hashbrown 0.16.1", "iai-callgrind", "iai-callgrind-macros", @@ -7933,7 +7829,6 @@ dependencies = [ "serde_json", "serial_test", "smallvec", - "spacetimedb-client-api", "spacetimedb-client-api-messages", "spacetimedb-core", "spacetimedb-data-structures", @@ -7945,7 +7840,6 @@ dependencies = [ "spacetimedb-query", "spacetimedb-sats", "spacetimedb-schema", - "spacetimedb-standalone", "spacetimedb-table", "spacetimedb-testing", "tempdir", @@ -7964,7 +7858,6 @@ dependencies = [ "humantime", "proc-macro2", "quote", - "spacetimedb-primitives", "syn 2.0.107", ] @@ -7991,8 +7884,6 @@ dependencies = [ "dialoguer", "dirs", "duct", - "email_address", - "flate2", "fs-err", "fs_extra", "futures", @@ -8010,7 +7901,6 @@ dependencies = [ "path-clean", "percent-encoding", "pretty_assertions", - "quick-xml 0.31.0", "regex", "reqwest 0.12.24", "rolldown", @@ -8021,7 +7911,6 @@ dependencies = [ "serde", "serde_json", "serde_with", - "slab", "spacetimedb-auth", "spacetimedb-client-api-messages", "spacetimedb-codegen", @@ -8030,11 +7919,9 @@ dependencies = [ "spacetimedb-jsonwebtoken", "spacetimedb-lib", "spacetimedb-paths", - "spacetimedb-primitives", "spacetimedb-schema", "syntect", "tabled", - "tar", "tempfile", "termcolor", "termtree", @@ -8045,7 +7932,6 @@ dependencies = [ "tokio-tungstenite 0.27.0", "toml 0.8.23", "toml_edit 0.22.27", - "tracing", "walkdir", "wasmbin", "webbrowser", @@ -8065,10 +7951,8 @@ dependencies = [ "base64 0.21.7", "bytes", "bytestring", - "chrono", "crossbeam-queue", "derive_more 0.99.20", - "email_address", "futures", "headers", "http 1.3.1", @@ -8076,16 +7960,13 @@ dependencies = [ "humantime", "hyper 1.7.0", "hyper-util", - "itoa", "jemalloc_pprof", - "lazy_static", "log", "mime", "pretty_assertions", "prometheus", "proptest", "rand 0.9.2", - "regex", "scopeguard", "serde", "serde_json", @@ -8093,21 +7974,16 @@ dependencies = [ "spacetimedb-client-api-messages", "spacetimedb-core", "spacetimedb-data-structures", - "spacetimedb-datastore", "spacetimedb-jsonwebtoken", "spacetimedb-lib", "spacetimedb-paths", "spacetimedb-schema", - "tempfile", "thiserror 1.0.69", "tokio", - "tokio-stream", "tokio-tungstenite 0.27.0", "toml 0.8.23", "tower", "tower-http 0.5.2", - "tower-layer", - "tower-service", "tracing", "uuid", ] @@ -8118,7 +7994,6 @@ version = "2.8.0" dependencies = [ "bytes", "bytestring", - "chrono", "derive_more 0.99.20", "enum-as-inner", "hex", @@ -8131,7 +8006,6 @@ dependencies = [ "spacetimedb-lib", "spacetimedb-primitives", "spacetimedb-sats", - "strum", "thiserror 1.0.69", ] @@ -8185,7 +8059,6 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "zstd-framed", ] [[package]] @@ -8193,11 +8066,9 @@ name = "spacetimedb-core" version = "2.8.0" dependencies = [ "anyhow", - "arrayvec", "async-trait", "async_cache", "axum", - "backtrace", "base64 0.21.7", "blake3", "brotli", @@ -8207,27 +8078,19 @@ dependencies = [ "chrono", "core_affinity", "criterion", - "crossbeam-channel", "crossbeam-queue", "deno_core_icudata", "derive_more 0.99.20", - "dirs", - "enum-as-inner", "enum-map", "env_logger 0.10.2", "faststr", "flate2", - "fs2", "fs_extra", "futures", "futures-util", - "hex", - "hostname", "http 1.3.1", "http-body-util", "humantime", - "hyper 1.7.0", - "imara-diff", "indexmap 2.12.0", "itertools 0.12.1", "lazy_static", @@ -8238,7 +8101,6 @@ dependencies = [ "once_cell", "openssl", "parking_lot 0.12.5", - "paste", "pin-project-lite", "pretty_assertions", "prometheus", @@ -8257,10 +8119,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_with", - "sha1", - "similar", "slab", - "sled", "smallvec", "sourcemap", "spacetimedb-auth", @@ -8272,7 +8131,6 @@ dependencies = [ "spacetimedb-engine", "spacetimedb-execution", "spacetimedb-expr", - "spacetimedb-fs-utils", "spacetimedb-jsonwebtoken", "spacetimedb-jwks", "spacetimedb-lib", @@ -8285,14 +8143,10 @@ dependencies = [ "spacetimedb-runtime", "spacetimedb-sats", "spacetimedb-schema", - "spacetimedb-snapshot", "spacetimedb-subscription", "spacetimedb-table", - "sqlparser", "strum", - "tabled", "tempfile", - "thin-vec", "thiserror 1.0.69", "tikv-jemalloc-ctl", "tikv-jemallocator", @@ -8305,12 +8159,9 @@ dependencies = [ "tracing-appender", "tracing-core", "tracing-flame", - "tracing-log 0.1.4", "tracing-subscriber", "tracing-tracy", "url", - "urlencoding", - "uuid", "v8", "wasmtime", "wasmtime-internal-fiber", @@ -8339,7 +8190,6 @@ dependencies = [ "anyhow", "bytes", "derive_more 0.99.20", - "enum-as-inner", "enum-map", "itertools 0.12.1", "lazy_static", @@ -8356,7 +8206,6 @@ dependencies = [ "spacetimedb-execution", "spacetimedb-lib", "spacetimedb-metrics", - "spacetimedb-paths", "spacetimedb-primitives", "spacetimedb-sats", "spacetimedb-schema", @@ -8372,7 +8221,6 @@ name = "spacetimedb-dst-lib" version = "2.8.0" dependencies = [ "anyhow", - "clap 4.5.50", "spacetimedb-commitlog", "spacetimedb-datastore", "spacetimedb-durability", @@ -8384,7 +8232,6 @@ dependencies = [ "spacetimedb-schema", "spacetimedb-table", "tracing", - "tracing-subscriber", ] [[package]] @@ -8401,7 +8248,6 @@ dependencies = [ "spacetimedb-fs-utils", "spacetimedb-paths", "spacetimedb-runtime", - "spacetimedb-sats", "tempfile", "thiserror 1.0.69", "tokio", @@ -8461,7 +8307,6 @@ dependencies = [ "spacetimedb-physical-plan", "spacetimedb-primitives", "spacetimedb-sats", - "spacetimedb-sql-parser", "spacetimedb-table", ] @@ -8471,8 +8316,6 @@ version = "2.8.0" dependencies = [ "anyhow", "bigdecimal", - "bytes", - "derive_more 0.99.20", "ethnum", "pretty_assertions", "spacetimedb", @@ -8505,7 +8348,6 @@ dependencies = [ name = "spacetimedb-guard" version = "2.8.0" dependencies = [ - "portpicker", "reqwest 0.12.24", "tempfile", ] @@ -8554,17 +8396,11 @@ name = "spacetimedb-lib" version = "2.8.0" dependencies = [ "anyhow", - "bitflags 2.10.0", "blake3", - "bytes", - "chrono", "derive_more 0.99.20", - "enum-as-inner", "enum-map", "hex", "insta", - "itertools 0.12.1", - "log", "proptest", "proptest-derive", "ron", @@ -8575,7 +8411,6 @@ dependencies = [ "spacetimedb-metrics", "spacetimedb-primitives", "spacetimedb-sats", - "thiserror 1.0.69", ] [[package]] @@ -8668,17 +8503,12 @@ name = "spacetimedb-query" version = "2.8.0" dependencies = [ "anyhow", - "itertools 0.12.1", - "rayon", - "spacetimedb-client-api-messages", "spacetimedb-execution", "spacetimedb-expr", "spacetimedb-lib", "spacetimedb-physical-plan", "spacetimedb-primitives", "spacetimedb-schema", - "spacetimedb-sql-parser", - "spacetimedb-table", ] [[package]] @@ -8725,7 +8555,6 @@ dependencies = [ "ahash 0.8.12", "anyhow", "arrayvec", - "bitflags 2.10.0", "blake3", "bytemuck", "bytes", @@ -8770,7 +8599,6 @@ dependencies = [ "lean_string", "petgraph 0.6.5", "proptest", - "serde_json", "serial_test", "smallvec", "spacetimedb-data-structures", @@ -8778,7 +8606,6 @@ dependencies = [ "spacetimedb-memory-usage", "spacetimedb-primitives", "spacetimedb-sats", - "spacetimedb-sql-parser", "spacetimedb-testing", "termcolor", "thiserror 1.0.69", @@ -8791,7 +8618,6 @@ name = "spacetimedb-sdk" version = "2.8.0" dependencies = [ "anymap3", - "base64 0.21.7", "brotli", "bytes", "cursive", @@ -8819,7 +8645,6 @@ dependencies = [ "spacetimedb-metrics", "spacetimedb-query-builder", "spacetimedb-sats", - "spacetimedb-schema", "spacetimedb-testing", "thiserror 1.0.69", "tokio", @@ -8863,7 +8688,6 @@ dependencies = [ "crossbeam-queue", "env_logger 0.10.2", "futures", - "hex", "log", "pretty_assertions", "rand 0.9.2", @@ -8892,7 +8716,6 @@ dependencies = [ name = "spacetimedb-sql-parser" version = "2.8.0" dependencies = [ - "derive_more 0.99.20", "spacetimedb-lib", "sqlparser", "thiserror 1.0.69", @@ -8906,17 +8729,12 @@ dependencies = [ "async-trait", "axum", "clap 4.5.50", - "dirs", - "futures", - "hostname", "http 1.3.1", "log", "netstat2", "once_cell", - "openssl", "parse-size", "prometheus", - "scopeguard", "serde", "serde_json", "sled", @@ -8936,8 +8754,6 @@ dependencies = [ "tikv-jemallocator", "tokio", "toml 0.8.23", - "tower-http 0.5.2", - "tracing", ] [[package]] @@ -8963,7 +8779,6 @@ dependencies = [ "blake3", "bytemuck", "criterion", - "crossbeam-queue", "decorum", "derive_more 0.99.20", "enum-as-inner", @@ -9011,7 +8826,6 @@ dependencies = [ "spacetimedb-standalone", "tempfile", "tokio", - "wasmbin", ] [[package]] @@ -9036,7 +8850,6 @@ dependencies = [ "tar", "tempfile", "tokio", - "toml 0.8.23", "tracing", "windows-sys 0.59.0", "zip", @@ -9072,29 +8885,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "sqllogictest-engines" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8b8bc26d3d9cf2e273bef46f4e9a90a451cf12f5a6d593b2e4fb86b44eb145" -dependencies = [ - "async-trait", - "bytes", - "chrono", - "futures", - "log", - "pg_interval", - "postgres-types", - "rust_decimal", - "serde", - "serde_json", - "sqllogictest", - "thiserror 1.0.69", - "tokio", - "tokio-postgres", - "tokio-util", -] - [[package]] name = "sqlparser" version = "0.38.0" @@ -9115,10 +8905,8 @@ dependencies = [ "console", "derive_more 0.99.20", "fs-err", - "futures", "glob", "itertools 0.12.1", - "parking_lot 0.12.5", "postgres-types", "quick-junit", "rusqlite", @@ -9127,7 +8915,6 @@ dependencies = [ "spacetimedb-lib", "spacetimedb-sats", "sqllogictest", - "sqllogictest-engines", "tempfile", "tokio", "tokio-postgres", @@ -9543,7 +9330,6 @@ name = "test-counter" version = "2.8.0" dependencies = [ "anyhow", - "futures", "gloo-timers", "spacetimedb-data-structures", ] @@ -10122,17 +9908,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -10171,7 +9946,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", "tracing-serde", ] @@ -10304,8 +10079,6 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" name = "typescript-test-app" version = "0.1.0" dependencies = [ - "anyhow", - "log", "spacetimedb", ] @@ -10313,8 +10086,6 @@ dependencies = [ name = "typescript-test-react-router-app" version = "0.1.0" dependencies = [ - "anyhow", - "log", "spacetimedb", ] @@ -10322,8 +10093,6 @@ dependencies = [ name = "typescript-test-solid-router-app" version = "0.1.0" dependencies = [ - "anyhow", - "log", "spacetimedb", ] @@ -10432,12 +10201,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8-width" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" - [[package]] name = "utf8_iter" version = "1.0.4" diff --git a/crates/bench/Cargo.toml b/crates/bench/Cargo.toml index 115eb115022..7777edd22d3 100644 --- a/crates/bench/Cargo.toml +++ b/crates/bench/Cargo.toml @@ -37,7 +37,6 @@ name = "summarize" bench = false [dependencies] -spacetimedb-client-api = { path = "../client-api" } spacetimedb-client-api-messages = { path = "../client-api-messages" } spacetimedb-core = { path = "../core", features = ["test"] } spacetimedb-data-structures.workspace = true @@ -49,18 +48,14 @@ spacetimedb-primitives = { path = "../primitives" } spacetimedb-query = { path = "../query" } spacetimedb-sats = { path = "../sats" } spacetimedb-schema = { workspace = true, features = ["test"] } -spacetimedb-standalone = { path = "../standalone" } spacetimedb-table = { path = "../table" } spacetimedb-testing = { path = "../testing" } ahash.workspace = true anyhow.workspace = true convert_case.workspace = true -anymap.workspace = true -byte-unit.workspace = true clap.workspace = true criterion.workspace = true -futures.workspace = true foldhash.workspace = true hashbrown.workspace = true lazy_static.workspace = true @@ -95,5 +90,8 @@ iai-callgrind = { git = "https://github.com/clockworklabs/iai-callgrind.git", br iai-callgrind-runner = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } iai-callgrind-macros = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } +[package.metadata.cargo-machete] +ignored = ["spacetimedb-core"] + [lints] workspace = true diff --git a/crates/bindings-macro/Cargo.toml b/crates/bindings-macro/Cargo.toml index 9e0094df23a..0a54464f8a6 100644 --- a/crates/bindings-macro/Cargo.toml +++ b/crates/bindings-macro/Cargo.toml @@ -12,7 +12,6 @@ proc-macro = true bench = false [dependencies] -spacetimedb-primitives.workspace = true humantime.workspace = true proc-macro2.workspace = true diff --git a/crates/bindings-typescript/test-app/server/Cargo.toml b/crates/bindings-typescript/test-app/server/Cargo.toml index 7bc77ce9454..84695c84618 100644 --- a/crates/bindings-typescript/test-app/server/Cargo.toml +++ b/crates/bindings-typescript/test-app/server/Cargo.toml @@ -10,5 +10,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb = { workspace = true } -log = "0.4" -anyhow = "1.0" diff --git a/crates/bindings-typescript/test-react-router-app/server/Cargo.toml b/crates/bindings-typescript/test-react-router-app/server/Cargo.toml index 99797536040..e89454b96f8 100644 --- a/crates/bindings-typescript/test-react-router-app/server/Cargo.toml +++ b/crates/bindings-typescript/test-react-router-app/server/Cargo.toml @@ -10,5 +10,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log = "0.4" -anyhow = "1.0" diff --git a/crates/bindings-typescript/test-solid-router/server/Cargo.toml b/crates/bindings-typescript/test-solid-router/server/Cargo.toml index a7ad32478e1..fa12084c62e 100644 --- a/crates/bindings-typescript/test-solid-router/server/Cargo.toml +++ b/crates/bindings-typescript/test-solid-router/server/Cargo.toml @@ -10,5 +10,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log = "0.4" -anyhow = "1.0" diff --git a/crates/bindings/Cargo.toml b/crates/bindings/Cargo.toml index 54c3bd3c74e..d32109b96ac 100644 --- a/crates/bindings/Cargo.toml +++ b/crates/bindings/Cargo.toml @@ -29,10 +29,8 @@ spacetimedb-query-builder.workspace = true anyhow.workspace = true bytemuck.workspace = true bytes.workspace = true -derive_more.workspace = true http.workspace = true log.workspace = true -scoped-tls.workspace = true rand08 = { workspace = true, optional = true } # we depend on getrandom and enable the `custom` feature, so that @@ -45,5 +43,8 @@ serde_json.workspace = true insta.workspace = true trybuild.workspace = true +[package.metadata.cargo-machete] +ignored = ["getrandom02"] + [lints] workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index bb71fb8b6e5..81f406ebbec 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -28,7 +28,6 @@ spacetimedb-data-structures.workspace = true spacetimedb-fs-utils.workspace = true spacetimedb-lib.workspace = true spacetimedb-paths.workspace = true -spacetimedb-primitives.workspace = true spacetimedb-schema.workspace = true anyhow.workspace = true @@ -41,9 +40,7 @@ colored.workspace = true convert_case.workspace = true dirs.workspace = true duct.workspace = true -email_address.workspace = true futures.workspace = true -flate2.workspace = true fs-err.workspace = true http.workspace = true is-terminal.workspace = true @@ -60,19 +57,15 @@ rustyline.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["raw_value", "preserve_order", "arbitrary_precision"] } serde_with = { workspace = true, features = ["chrono_0_4"] } -slab.workspace = true syntect.workspace = true tabled.workspace = true -tar.workspace = true tempfile.workspace = true termcolor.workspace = true termtree.workspace = true thiserror.workspace = true tokio.workspace = true tokio-tungstenite.workspace = true -toml.workspace = true toml_edit.workspace = true -tracing = { workspace = true, features = ["release_max_level_off"] } walkdir.workspace = true wasmbin.workspace = true webbrowser.workspace = true @@ -85,7 +78,6 @@ rolldown_common.workspace = true rolldown_error.workspace = true rolldown_utils.workspace = true xmltree.workspace = true -quick-xml.workspace = true names.workspace = true notify.workspace = true path-clean = "1.0.1" diff --git a/crates/client-api-messages/Cargo.toml b/crates/client-api-messages/Cargo.toml index a5af8be01f0..f278ac59e02 100644 --- a/crates/client-api-messages/Cargo.toml +++ b/crates/client-api-messages/Cargo.toml @@ -13,13 +13,11 @@ spacetimedb-sats = { workspace = true, features = ["bytestring"] } bytes.workspace = true bytestring.workspace = true -chrono = { workspace = true, features = ["serde"] } enum-as-inner.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true serde_with.workspace = true smallvec.workspace = true -strum.workspace = true thiserror.workspace = true derive_more.workspace = true diff --git a/crates/client-api/Cargo.toml b/crates/client-api/Cargo.toml index 09fcb563300..da9b34609ae 100644 --- a/crates/client-api/Cargo.toml +++ b/crates/client-api/Cargo.toml @@ -10,7 +10,6 @@ rust-version.workspace = true spacetimedb-client-api-messages.workspace = true spacetimedb-core.workspace = true spacetimedb-data-structures.workspace = true -spacetimedb-datastore.workspace = true spacetimedb-lib = { workspace = true, features = ["serde"] } spacetimedb-paths.workspace = true spacetimedb-schema.workspace = true @@ -18,17 +17,12 @@ spacetimedb-schema.workspace = true base64.workspace = true http-body-util.workspace = true tokio = { workspace = true, features = ["full"] } -lazy_static = "1.4.0" log = "0.4.4" serde = "1.0.136" serde_json = { version = "1.0", features = ["raw_value"] } anyhow = { version = "1.0.57", features = ["backtrace"] } -regex = "1" prometheus.workspace = true -email_address = "0.2.3" -tempfile.workspace = true async-trait = "0.1.60" -chrono = { workspace = true, features = ["serde"] } rand.workspace = true axum.workspace = true axum-extra.workspace = true @@ -37,16 +31,12 @@ hyper-util.workspace = true http.workspace = true headers.workspace = true mime = "0.3.17" -tokio-stream = { version = "0.1.12", features = ["sync"] } -tower-layer.workspace = true -tower-service.workspace = true tower-http.workspace = true futures = "0.3" bytes = "1" tracing.workspace = true bytestring = "1" tokio-tungstenite.workspace = true -itoa.workspace = true derive_more = "0.99.17" uuid.workspace = true jsonwebtoken.workspace = true @@ -71,6 +61,9 @@ proptest.workspace = true tokio = { workspace = true, features = ["full", "test-util"] } toml.workspace = true +[package.metadata.cargo-machete] +ignored = ["spacetimedb-core"] + [lints] workspace = true diff --git a/crates/commitlog/Cargo.toml b/crates/commitlog/Cargo.toml index 0c30960248f..411a1398c48 100644 --- a/crates/commitlog/Cargo.toml +++ b/crates/commitlog/Cargo.toml @@ -36,7 +36,6 @@ tempfile.workspace = true thiserror.workspace = true tokio = { workspace = true, optional = true } tokio-util = { workspace = true, optional = true, features = ["io-util"] } -zstd-framed.workspace = true # For the 'test' feature env_logger = { workspace = true, optional = true } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b9c21843e59..ddc5a712b0e 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -33,16 +33,12 @@ spacetimedb-runtime.workspace = true spacetimedb-sats = { workspace = true, features = ["serde"] } spacetimedb-schema.workspace = true spacetimedb-table.workspace = true -spacetimedb-snapshot.workspace = true spacetimedb-subscription.workspace = true spacetimedb-expr.workspace = true spacetimedb-execution.workspace = true -spacetimedb-fs-utils.workspace = true anyhow = { workspace = true, features = ["backtrace"] } -arrayvec.workspace = true async-trait.workspace = true -backtrace.workspace = true base64.workspace = true blake3.workspace = true brotli.workspace = true @@ -50,24 +46,16 @@ bytemuck.workspace = true bytes.workspace = true bytestring.workspace = true chrono.workspace = true -crossbeam-channel.workspace = true crossbeam-queue.workspace = true deno_core_icudata.workspace = true derive_more.workspace = true -dirs.workspace = true -enum-as-inner.workspace = true enum-map.workspace = true flate2.workspace = true -fs2.workspace = true futures.workspace = true futures-util.workspace = true -hex.workspace = true -hostname.workspace = true http.workspace = true http-body-util.workspace = true humantime.workspace = true -hyper.workspace = true -imara-diff.workspace = true indexmap.workspace = true itertools.workspace = true jsonwebtoken.workspace = true @@ -78,7 +66,6 @@ nohash-hasher.workspace = true once_cell.workspace = true openssl.workspace = true parking_lot.workspace = true -paste.workspace = true pin-project-lite.workspace = true prometheus.workspace = true rayon.workspace = true @@ -93,18 +80,12 @@ serde.workspace = true serde_json.workspace = true serde_path_to_error.workspace = true serde_with = { workspace = true, features = ["chrono_0_4"] } -sha1.workspace = true -similar.workspace = true slab.workspace = true -sled.workspace = true smallvec.workspace = true sourcemap.workspace = true -sqlparser.workspace = true strum.workspace = true -tabled.workspace = true tempfile.workspace = true thiserror.workspace = true -thin-vec.workspace = true tokio-util.workspace = true tokio.workspace = true tokio-stream = { workspace = true, features = ["sync"] } @@ -113,13 +94,10 @@ toml.workspace = true tracing-appender.workspace = true tracing-core.workspace = true tracing-flame.workspace = true -tracing-log.workspace = true tracing-subscriber.workspace = true tracing-tracy.workspace = true tracing.workspace = true url.workspace = true -urlencoding.workspace = true -uuid.workspace = true v8.workspace = true wasmtime.workspace = true wasmtime-internal-fiber.workspace = true diff --git a/crates/datastore/Cargo.toml b/crates/datastore/Cargo.toml index 86cb2aca0b6..d42e633f4b3 100644 --- a/crates/datastore/Cargo.toml +++ b/crates/datastore/Cargo.toml @@ -13,7 +13,6 @@ spacetimedb-commitlog.workspace = true spacetimedb-durability.workspace = true spacetimedb-metrics.workspace = true spacetimedb-primitives.workspace = true -spacetimedb-paths.workspace = true spacetimedb-sats = { workspace = true, features = ["serde"] } spacetimedb-schema.workspace = true spacetimedb-table.workspace = true @@ -23,7 +22,6 @@ spacetimedb-execution.workspace = true anyhow = { workspace = true, features = ["backtrace"] } bytes.workspace = true derive_more.workspace = true -enum-as-inner.workspace = true enum-map.workspace = true itertools.workspace = true lazy_static.workspace = true diff --git a/crates/dst/Cargo.toml b/crates/dst/Cargo.toml index 7d47c9a1b47..7c7bd071f4e 100644 --- a/crates/dst/Cargo.toml +++ b/crates/dst/Cargo.toml @@ -10,7 +10,6 @@ fallocate = ["spacetimedb-commitlog/fallocate"] [dependencies] anyhow.workspace = true -clap.workspace = true spacetimedb-datastore.workspace = true spacetimedb-commitlog.workspace = true spacetimedb-durability.workspace = true @@ -22,7 +21,6 @@ spacetimedb-sats.workspace = true spacetimedb-schema.workspace = true spacetimedb-table.workspace = true tracing.workspace = true -tracing-subscriber.workspace = true [lints] workspace = true diff --git a/crates/durability/Cargo.toml b/crates/durability/Cargo.toml index 49a5c9565fb..c1285a752fe 100644 --- a/crates/durability/Cargo.toml +++ b/crates/durability/Cargo.toml @@ -22,7 +22,6 @@ spacetimedb-commitlog.workspace = true spacetimedb-fs-utils.workspace = true spacetimedb-paths.workspace = true spacetimedb-runtime.workspace = true -spacetimedb-sats.workspace = true thiserror.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/crates/execution/Cargo.toml b/crates/execution/Cargo.toml index 9eff876d333..d40600dd183 100644 --- a/crates/execution/Cargo.toml +++ b/crates/execution/Cargo.toml @@ -14,7 +14,6 @@ spacetimedb-lib.workspace = true spacetimedb-sats.workspace = true spacetimedb-physical-plan.workspace = true spacetimedb-primitives.workspace = true -spacetimedb-sql-parser.workspace = true spacetimedb-table.workspace = true [lints] diff --git a/crates/expr/Cargo.toml b/crates/expr/Cargo.toml index 5138fb1d669..6f2f5e50301 100644 --- a/crates/expr/Cargo.toml +++ b/crates/expr/Cargo.toml @@ -9,7 +9,6 @@ description = "The logical expression representation for the SpacetimeDB query e [dependencies] anyhow.workspace = true bigdecimal.workspace = true -derive_more.workspace = true ethnum.workspace = true thiserror.workspace = true spacetimedb-data-structures.workspace = true @@ -18,7 +17,6 @@ spacetimedb-primitives.workspace = true spacetimedb-sats.workspace = true spacetimedb-schema.workspace = true spacetimedb-sql-parser.workspace = true -bytes.workspace = true [dev-dependencies] pretty_assertions.workspace = true diff --git a/crates/guard/Cargo.toml b/crates/guard/Cargo.toml index f675b6dba3c..02d31efc11f 100644 --- a/crates/guard/Cargo.toml +++ b/crates/guard/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true rust-version.workspace = true [dependencies] -portpicker = "0.1" reqwest = { workspace = true, features = ["blocking", "json"] } tempfile.workspace = true diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index 67f10d22485..ff1c4ee9656 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -38,16 +38,9 @@ spacetimedb-memory-usage = { workspace = true, optional = true } spacetimedb-metrics = { workspace = true, optional = true } anyhow.workspace = true -bitflags.workspace = true -bytes.workspace = true -chrono.workspace = true derive_more.workspace = true -enum-as-inner.workspace = true hex.workspace = true -itertools.workspace = true -log.workspace = true serde = { workspace = true, optional = true } -thiserror.workspace = true blake3.workspace = true enum-map = { workspace = true, optional = true } @@ -57,7 +50,6 @@ proptest-derive = { workspace = true, optional = true } [dev-dependencies] spacetimedb-sats = { path = "../sats", features = ["test"] } -bytes.workspace = true serde_json.workspace = true insta.workspace = true ron.workspace = true @@ -66,5 +58,8 @@ ron.workspace = true proptest.workspace = true proptest-derive.workspace = true +[package.metadata.cargo-machete] +ignored = ["proptest-derive"] + [lints] workspace = true diff --git a/crates/query/Cargo.toml b/crates/query/Cargo.toml index 7de32844fd5..26e0e08a04b 100644 --- a/crates/query/Cargo.toml +++ b/crates/query/Cargo.toml @@ -8,17 +8,12 @@ description = "Top level crate for invoking the query engine and optimizer" [dependencies] anyhow.workspace = true -itertools.workspace = true -rayon.workspace = true -spacetimedb-client-api-messages.workspace = true spacetimedb-execution.workspace = true spacetimedb-expr.workspace = true spacetimedb-lib.workspace = true spacetimedb-primitives.workspace = true spacetimedb-physical-plan.workspace = true spacetimedb-schema.workspace = true -spacetimedb-sql-parser.workspace = true -spacetimedb-table.workspace = true [lints] workspace = true diff --git a/crates/sats/Cargo.toml b/crates/sats/Cargo.toml index 27696f34ac6..ff1ac21eb16 100644 --- a/crates/sats/Cargo.toml +++ b/crates/sats/Cargo.toml @@ -39,7 +39,6 @@ spacetimedb-metrics = { workspace = true, optional = true } anyhow.workspace = true arrayvec.workspace = true -bitflags.workspace = true bytes.workspace = true bytemuck.workspace = true bytestring = { workspace = true, optional = true } diff --git a/crates/schema/Cargo.toml b/crates/schema/Cargo.toml index 25ff3e3e9cc..b79cc0a0974 100644 --- a/crates/schema/Cargo.toml +++ b/crates/schema/Cargo.toml @@ -15,7 +15,6 @@ spacetimedb-primitives = { workspace = true, features = ["memory-usage"] } spacetimedb-sats = { workspace = true, features = ["memory-usage"] } spacetimedb-data-structures.workspace = true spacetimedb-memory-usage.workspace = true -spacetimedb-sql-parser.workspace = true anyhow.workspace = true derive_more.workspace = true indexmap.workspace = true @@ -26,7 +25,6 @@ thiserror.workspace = true unicode-ident.workspace = true unicode-normalization.workspace = true petgraph.workspace = true -serde_json.workspace = true smallvec.workspace = true enum-as-inner.workspace = true enum-map.workspace = true diff --git a/crates/smoketests/modules/add-remove-index-indexed/Cargo.toml b/crates/smoketests/modules/add-remove-index-indexed/Cargo.toml index 876ee4f4e14..a395dc656bf 100644 --- a/crates/smoketests/modules/add-remove-index-indexed/Cargo.toml +++ b/crates/smoketests/modules/add-remove-index-indexed/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/add-remove-index/Cargo.toml b/crates/smoketests/modules/add-remove-index/Cargo.toml index 0318839bb12..34a5f04f578 100644 --- a/crates/smoketests/modules/add-remove-index/Cargo.toml +++ b/crates/smoketests/modules/add-remove-index/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-drop-event-table-after/Cargo.toml b/crates/smoketests/modules/auto-migration-drop-event-table-after/Cargo.toml index 4a0d71553b6..fcf118a6a7f 100644 --- a/crates/smoketests/modules/auto-migration-drop-event-table-after/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-drop-event-table-after/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-drop-event-table-before/Cargo.toml b/crates/smoketests/modules/auto-migration-drop-event-table-before/Cargo.toml index 90e4382cf60..6d680f279d7 100644 --- a/crates/smoketests/modules/auto-migration-drop-event-table-before/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-drop-event-table-before/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-event-table-after/Cargo.toml b/crates/smoketests/modules/auto-migration-event-table-after/Cargo.toml index 83da04df530..484f8c25d8b 100644 --- a/crates/smoketests/modules/auto-migration-event-table-after/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-event-table-after/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-event-table-before/Cargo.toml b/crates/smoketests/modules/auto-migration-event-table-before/Cargo.toml index c823f521b83..be4f4e9f496 100644 --- a/crates/smoketests/modules/auto-migration-event-table-before/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-event-table-before/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-with-primary-key/Cargo.toml b/crates/smoketests/modules/auto-migration-with-primary-key/Cargo.toml index 4672d5aa2e2..94a919fba88 100644 --- a/crates/smoketests/modules/auto-migration-with-primary-key/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-with-primary-key/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-without-primary-key-v2/Cargo.toml b/crates/smoketests/modules/auto-migration-without-primary-key-v2/Cargo.toml index 05be2d05624..bcfab7fe8d0 100644 --- a/crates/smoketests/modules/auto-migration-without-primary-key-v2/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-without-primary-key-v2/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/auto-migration-without-primary-key/Cargo.toml b/crates/smoketests/modules/auto-migration-without-primary-key/Cargo.toml index 9c40d1eb012..622b64124f9 100644 --- a/crates/smoketests/modules/auto-migration-without-primary-key/Cargo.toml +++ b/crates/smoketests/modules/auto-migration-without-primary-key/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/call-empty/Cargo.toml b/crates/smoketests/modules/call-empty/Cargo.toml index 0449b80a7a5..3fb08c1101c 100644 --- a/crates/smoketests/modules/call-empty/Cargo.toml +++ b/crates/smoketests/modules/call-empty/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/client-connection-disconnect-panic/Cargo.toml b/crates/smoketests/modules/client-connection-disconnect-panic/Cargo.toml index b4ac3a61b2f..6672ca2eda8 100644 --- a/crates/smoketests/modules/client-connection-disconnect-panic/Cargo.toml +++ b/crates/smoketests/modules/client-connection-disconnect-panic/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/client-connection-reject/Cargo.toml b/crates/smoketests/modules/client-connection-reject/Cargo.toml index 3fdd30aacb2..aaa67b24107 100644 --- a/crates/smoketests/modules/client-connection-reject/Cargo.toml +++ b/crates/smoketests/modules/client-connection-reject/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/confirmed-reads/Cargo.toml b/crates/smoketests/modules/confirmed-reads/Cargo.toml index 5d952b0ecb8..4390f0f8a30 100644 --- a/crates/smoketests/modules/confirmed-reads/Cargo.toml +++ b/crates/smoketests/modules/confirmed-reads/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/delete-database/Cargo.toml b/crates/smoketests/modules/delete-database/Cargo.toml index 48a6d18dffe..32de2cfb529 100644 --- a/crates/smoketests/modules/delete-database/Cargo.toml +++ b/crates/smoketests/modules/delete-database/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/dml/Cargo.toml b/crates/smoketests/modules/dml/Cargo.toml index 525cbc25919..4424355c3c5 100644 --- a/crates/smoketests/modules/dml/Cargo.toml +++ b/crates/smoketests/modules/dml/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/fail-initial-publish-broken/Cargo.toml b/crates/smoketests/modules/fail-initial-publish-broken/Cargo.toml index 0aacc78c49b..e3a345a1d7c 100644 --- a/crates/smoketests/modules/fail-initial-publish-broken/Cargo.toml +++ b/crates/smoketests/modules/fail-initial-publish-broken/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/fail-initial-publish-fixed/Cargo.toml b/crates/smoketests/modules/fail-initial-publish-fixed/Cargo.toml index 5a832ceaf53..d3ec6edd4af 100644 --- a/crates/smoketests/modules/fail-initial-publish-fixed/Cargo.toml +++ b/crates/smoketests/modules/fail-initial-publish-fixed/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/hotswap-basic/Cargo.toml b/crates/smoketests/modules/hotswap-basic/Cargo.toml index e8d3e0d5fa4..062560e66f9 100644 --- a/crates/smoketests/modules/hotswap-basic/Cargo.toml +++ b/crates/smoketests/modules/hotswap-basic/Cargo.toml @@ -8,4 +8,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/hotswap-updated/Cargo.toml b/crates/smoketests/modules/hotswap-updated/Cargo.toml index 14d17c5f2c7..ced522ab3ed 100644 --- a/crates/smoketests/modules/hotswap-updated/Cargo.toml +++ b/crates/smoketests/modules/hotswap-updated/Cargo.toml @@ -8,4 +8,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-egress/Cargo.toml b/crates/smoketests/modules/http-egress/Cargo.toml index 28fcbdc46d5..888f3f61333 100644 --- a/crates/smoketests/modules/http-egress/Cargo.toml +++ b/crates/smoketests/modules/http-egress/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes-example/Cargo.toml b/crates/smoketests/modules/http-routes-example/Cargo.toml index bd13a911518..7387aacebb5 100644 --- a/crates/smoketests/modules/http-routes-example/Cargo.toml +++ b/crates/smoketests/modules/http-routes-example/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes-full-uri/Cargo.toml b/crates/smoketests/modules/http-routes-full-uri/Cargo.toml index 550d375d654..8c1e6628485 100644 --- a/crates/smoketests/modules/http-routes-full-uri/Cargo.toml +++ b/crates/smoketests/modules/http-routes-full-uri/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes-request-body/Cargo.toml b/crates/smoketests/modules/http-routes-request-body/Cargo.toml index ef85e54ce7c..e222522ed2e 100644 --- a/crates/smoketests/modules/http-routes-request-body/Cargo.toml +++ b/crates/smoketests/modules/http-routes-request-body/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes-strict-non-root/Cargo.toml b/crates/smoketests/modules/http-routes-strict-non-root/Cargo.toml index 97bace5306e..062ad53cebd 100644 --- a/crates/smoketests/modules/http-routes-strict-non-root/Cargo.toml +++ b/crates/smoketests/modules/http-routes-strict-non-root/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes-strict-root/Cargo.toml b/crates/smoketests/modules/http-routes-strict-root/Cargo.toml index 6a50ce3e286..5c6f7092dc8 100644 --- a/crates/smoketests/modules/http-routes-strict-root/Cargo.toml +++ b/crates/smoketests/modules/http-routes-strict-root/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/http-routes/Cargo.toml b/crates/smoketests/modules/http-routes/Cargo.toml index 1a8e87717a7..710394a757b 100644 --- a/crates/smoketests/modules/http-routes/Cargo.toml +++ b/crates/smoketests/modules/http-routes/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/modules-breaking/Cargo.toml b/crates/smoketests/modules/modules-breaking/Cargo.toml index 05b36a5e614..137d938afbe 100644 --- a/crates/smoketests/modules/modules-breaking/Cargo.toml +++ b/crates/smoketests/modules/modules-breaking/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/noop/Cargo.toml b/crates/smoketests/modules/noop/Cargo.toml index c0f24646d6c..4e677e0f0b7 100644 --- a/crates/smoketests/modules/noop/Cargo.toml +++ b/crates/smoketests/modules/noop/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/panic-error/Cargo.toml b/crates/smoketests/modules/panic-error/Cargo.toml index 2e577b299d5..3ebc4e1e553 100644 --- a/crates/smoketests/modules/panic-error/Cargo.toml +++ b/crates/smoketests/modules/panic-error/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/permissions-lifecycle/Cargo.toml b/crates/smoketests/modules/permissions-lifecycle/Cargo.toml index af648415cec..4a77d77efe8 100644 --- a/crates/smoketests/modules/permissions-lifecycle/Cargo.toml +++ b/crates/smoketests/modules/permissions-lifecycle/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/permissions-private/Cargo.toml b/crates/smoketests/modules/permissions-private/Cargo.toml index 96268618d7d..2976df0fec5 100644 --- a/crates/smoketests/modules/permissions-private/Cargo.toml +++ b/crates/smoketests/modules/permissions-private/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/pg-wire/Cargo.toml b/crates/smoketests/modules/pg-wire/Cargo.toml index 908f0b87689..d60cf3875d6 100644 --- a/crates/smoketests/modules/pg-wire/Cargo.toml +++ b/crates/smoketests/modules/pg-wire/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/rls-broken-private-table/Cargo.toml b/crates/smoketests/modules/rls-broken-private-table/Cargo.toml index f0cd150c7df..9fec43ced0a 100644 --- a/crates/smoketests/modules/rls-broken-private-table/Cargo.toml +++ b/crates/smoketests/modules/rls-broken-private-table/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/rls-no-filter/Cargo.toml b/crates/smoketests/modules/rls-no-filter/Cargo.toml index ebd9975f9f1..57e17cdf8a5 100644 --- a/crates/smoketests/modules/rls-no-filter/Cargo.toml +++ b/crates/smoketests/modules/rls-no-filter/Cargo.toml @@ -8,4 +8,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/rls-with-filter/Cargo.toml b/crates/smoketests/modules/rls-with-filter/Cargo.toml index 5f59d9c9650..775c4102d2b 100644 --- a/crates/smoketests/modules/rls-with-filter/Cargo.toml +++ b/crates/smoketests/modules/rls-with-filter/Cargo.toml @@ -8,4 +8,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/rls/Cargo.toml b/crates/smoketests/modules/rls/Cargo.toml index f17e75a86b9..5bf7f23159e 100644 --- a/crates/smoketests/modules/rls/Cargo.toml +++ b/crates/smoketests/modules/rls/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/schedule-volatile/Cargo.toml b/crates/smoketests/modules/schedule-volatile/Cargo.toml index 961b77e6a40..253045f7d83 100644 --- a/crates/smoketests/modules/schedule-volatile/Cargo.toml +++ b/crates/smoketests/modules/schedule-volatile/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/sql-format/Cargo.toml b/crates/smoketests/modules/sql-format/Cargo.toml index ff9f2a8837c..bb86a1e9fd4 100644 --- a/crates/smoketests/modules/sql-format/Cargo.toml +++ b/crates/smoketests/modules/sql-format/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/views-basic/Cargo.toml b/crates/smoketests/modules/views-basic/Cargo.toml index eff289cae91..1c704fe15da 100644 --- a/crates/smoketests/modules/views-basic/Cargo.toml +++ b/crates/smoketests/modules/views-basic/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/views-broken-namespace/Cargo.toml b/crates/smoketests/modules/views-broken-namespace/Cargo.toml index f38f99a8256..3bcb166d37d 100644 --- a/crates/smoketests/modules/views-broken-namespace/Cargo.toml +++ b/crates/smoketests/modules/views-broken-namespace/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/views-broken-return-type/Cargo.toml b/crates/smoketests/modules/views-broken-return-type/Cargo.toml index b3eabc7190a..3f14a27f01d 100644 --- a/crates/smoketests/modules/views-broken-return-type/Cargo.toml +++ b/crates/smoketests/modules/views-broken-return-type/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/smoketests/modules/views-callable/Cargo.toml b/crates/smoketests/modules/views-callable/Cargo.toml index 08fe1a81579..6b9d6b90f84 100644 --- a/crates/smoketests/modules/views-callable/Cargo.toml +++ b/crates/smoketests/modules/views-callable/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log.workspace = true diff --git a/crates/snapshot/Cargo.toml b/crates/snapshot/Cargo.toml index 6be91172e42..65424b69142 100644 --- a/crates/snapshot/Cargo.toml +++ b/crates/snapshot/Cargo.toml @@ -20,7 +20,6 @@ blake3.workspace = true bytes.workspace = true crossbeam-queue.workspace = true futures.workspace = true -hex.workspace = true log.workspace = true scopeguard.workspace = true tempfile.workspace = true diff --git a/crates/sql-parser/Cargo.toml b/crates/sql-parser/Cargo.toml index 5ed77a05bdc..2d71bf53b66 100644 --- a/crates/sql-parser/Cargo.toml +++ b/crates/sql-parser/Cargo.toml @@ -7,7 +7,6 @@ license-file = "LICENSE" description = "The SpacetimeDB SQL AST and Parser" [dependencies] -derive_more.workspace = true sqlparser.workspace = true thiserror.workspace = true spacetimedb-lib.workspace = true diff --git a/crates/sqltest/Cargo.toml b/crates/sqltest/Cargo.toml index d6b1e2c8a69..2cc68893075 100644 --- a/crates/sqltest/Cargo.toml +++ b/crates/sqltest/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] spacetimedb-lib.workspace = true -spacetimedb-core = { workspace = true, features = ["test"] } +spacetimedb-core.workspace = true spacetimedb-sats.workspace = true anyhow.workspace = true @@ -18,19 +18,19 @@ clap.workspace = true console.workspace = true derive_more.workspace = true fs-err.workspace = true -futures.workspace = true glob.workspace = true itertools.workspace = true -parking_lot.workspace = true postgres-types.workspace = true quick-junit.workspace = true rusqlite.workspace = true rust_decimal.workspace = true -sqllogictest-engines.workspace = true sqllogictest.workspace = true tempfile.workspace = true tokio.workspace = true tokio-postgres.workspace = true +[package.metadata.cargo-machete] +ignored = ["spacetimedb-core"] + [lints] workspace = true diff --git a/crates/standalone/Cargo.toml b/crates/standalone/Cargo.toml index 180b3a60b4c..e9186bb18c3 100644 --- a/crates/standalone/Cargo.toml +++ b/crates/standalone/Cargo.toml @@ -39,25 +39,18 @@ anyhow.workspace = true async-trait.workspace = true axum.workspace = true clap = { workspace = true, features = ["derive", "string"] } -dirs.workspace = true -futures.workspace = true -hostname.workspace = true http.workspace = true log.workspace = true netstat2.workspace = true -openssl.workspace = true parse-size.workspace = true prometheus.workspace = true -scopeguard.workspace = true serde.workspace = true serde_json.workspace = true sled.workspace = true socket2.workspace = true thiserror.workspace = true tokio.workspace = true -tower-http.workspace = true toml.workspace = true -tracing = { workspace = true, features = ["release_max_level_debug"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = {workspace = true} @@ -67,5 +60,8 @@ tikv-jemalloc-ctl = {workspace = true} once_cell.workspace = true tempfile.workspace = true +[package.metadata.cargo-machete] +ignored = ["spacetimedb-core"] + [lints] workspace = true diff --git a/crates/table/Cargo.toml b/crates/table/Cargo.toml index bde6ab5351f..1b5d9684480 100644 --- a/crates/table/Cargo.toml +++ b/crates/table/Cargo.toml @@ -48,7 +48,6 @@ hashbrown.workspace = true itertools.workspace = true smallvec.workspace = true thiserror.workspace = true -crossbeam-queue.workspace = true # For the 'proptest' feature. proptest = { workspace = true, optional = true } diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index 84295a77f46..1e82be444b0 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -27,7 +27,6 @@ log.workspace = true clap.workspace = true serde_json.workspace = true tokio.workspace = true -wasmbin.workspace = true duct.workspace = true lazy_static.workspace = true rand.workspace = true @@ -38,5 +37,8 @@ futures.workspace = true [dev-dependencies] serial_test.workspace = true +[package.metadata.cargo-machete] +ignored = ["spacetimedb-core"] + [lints] workspace = true diff --git a/crates/update/Cargo.toml b/crates/update/Cargo.toml index db8fac5ec37..886da812371 100644 --- a/crates/update/Cargo.toml +++ b/crates/update/Cargo.toml @@ -31,7 +31,6 @@ serde_json.workspace = true tar.workspace = true tempfile.workspace = true tokio.workspace = true -toml.workspace = true tracing = { workspace = true, features = ["release_max_level_off"] } zip = "2.3" diff --git a/modules/benchmarks/Cargo.toml b/modules/benchmarks/Cargo.toml index 34b11db149a..762f3022bee 100644 --- a/modules/benchmarks/Cargo.toml +++ b/modules/benchmarks/Cargo.toml @@ -11,5 +11,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb = { path = "../../crates/bindings" } - -anyhow.workspace = true diff --git a/modules/keynote-benchmarks/Cargo.toml b/modules/keynote-benchmarks/Cargo.toml index 00ac4554c94..a0405689401 100644 --- a/modules/keynote-benchmarks/Cargo.toml +++ b/modules/keynote-benchmarks/Cargo.toml @@ -9,4 +9,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb = { path = "../../crates/bindings" } -log = "0.4" diff --git a/modules/perf-test/Cargo.toml b/modules/perf-test/Cargo.toml index 406c3bfd826..3e127468a20 100644 --- a/modules/perf-test/Cargo.toml +++ b/modules/perf-test/Cargo.toml @@ -11,4 +11,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb = { path = "../../crates/bindings" } -log.workspace = true diff --git a/modules/sdk-test-case-conversion/Cargo.toml b/modules/sdk-test-case-conversion/Cargo.toml index c65a6d832d6..966d827c1bc 100644 --- a/modules/sdk-test-case-conversion/Cargo.toml +++ b/modules/sdk-test-case-conversion/Cargo.toml @@ -8,7 +8,6 @@ license-file = "LICENSE" crate-type = ["cdylib"] [dependencies] -log.workspace = true [dependencies.spacetimedb] workspace = true diff --git a/modules/sdk-test-connect-disconnect/Cargo.toml b/modules/sdk-test-connect-disconnect/Cargo.toml index 8a5b32f5e46..124a805142b 100644 --- a/modules/sdk-test-connect-disconnect/Cargo.toml +++ b/modules/sdk-test-connect-disconnect/Cargo.toml @@ -11,4 +11,3 @@ crate-type = ["cdylib"] [dependencies] spacetimedb.workspace = true -log = "0.4" diff --git a/modules/sdk-test-procedure-concurrency/Cargo.toml b/modules/sdk-test-procedure-concurrency/Cargo.toml index 3f82f68dd69..b885df0822f 100644 --- a/modules/sdk-test-procedure-concurrency/Cargo.toml +++ b/modules/sdk-test-procedure-concurrency/Cargo.toml @@ -9,8 +9,6 @@ crate-type = ["cdylib"] [dependencies] log.workspace = true -anyhow.workspace = true -paste.workspace = true [dependencies.spacetimedb] workspace = true diff --git a/modules/sdk-test-procedure/Cargo.toml b/modules/sdk-test-procedure/Cargo.toml index b17e8f9b3c1..c9eee87587e 100644 --- a/modules/sdk-test-procedure/Cargo.toml +++ b/modules/sdk-test-procedure/Cargo.toml @@ -10,9 +10,6 @@ license-file = "LICENSE" crate-type = ["cdylib"] [dependencies] -log.workspace = true -anyhow.workspace = true -paste.workspace = true [dependencies.spacetimedb] workspace = true diff --git a/modules/sdk-test-view/Cargo.toml b/modules/sdk-test-view/Cargo.toml index 3647b98505d..8209ce83071 100644 --- a/modules/sdk-test-view/Cargo.toml +++ b/modules/sdk-test-view/Cargo.toml @@ -10,9 +10,6 @@ license-file = "LICENSE" crate-type = ["cdylib"] [dependencies] -log.workspace = true -anyhow.workspace = true -paste.workspace = true [dependencies.spacetimedb] workspace = true diff --git a/modules/sdk-test/Cargo.toml b/modules/sdk-test/Cargo.toml index 7249944a065..806e336afb0 100644 --- a/modules/sdk-test/Cargo.toml +++ b/modules/sdk-test/Cargo.toml @@ -10,7 +10,6 @@ license-file = "LICENSE" crate-type = ["cdylib"] [dependencies] -log.workspace = true anyhow.workspace = true paste.workspace = true diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 1f3ee9903d2..396b15cbea8 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -31,11 +31,9 @@ spacetimedb-lib = { workspace = true, features = ["serde", "metrics_impls"]} spacetimedb-client-api-messages.workspace = true spacetimedb-metrics.workspace = true spacetimedb-query-builder.workspace = true -spacetimedb-schema.workspace = true thiserror.workspace = true anymap3 = "1.0.1" -base64.workspace = true brotli.workspace = true bytes.workspace = true flate2.workspace = true @@ -46,7 +44,6 @@ log.workspace = true once_cell.workspace = true prometheus.workspace = true rand.workspace = true - getrandom = { version = "0.3.4", features = ["wasm_js"], optional = true } gloo-console = { version = "0.3.0", optional = true } gloo-net = { version = "0.6.0", optional = true } @@ -77,5 +74,8 @@ shlex = "1.3.0" # for tests spacetimedb-testing = { path = "../../crates/testing" } +[package.metadata.cargo-machete] +ignored = ["getrandom"] + [lints] workspace = true diff --git a/sdks/rust/tests/connect_disconnect_client/Cargo.toml b/sdks/rust/tests/connect_disconnect_client/Cargo.toml index 61ff6af9852..5f7e7eda720 100644 --- a/sdks/rust/tests/connect_disconnect_client/Cargo.toml +++ b/sdks/rust/tests/connect_disconnect_client/Cargo.toml @@ -34,13 +34,16 @@ required-features = ["native"] spacetimedb-sdk = { path = "../.." } test-counter = { path = "../test-counter" } anyhow.workspace = true -futures = { workspace = true, optional = true } tokio = { workspace = true, optional = true } +futures = { workspace = true, optional = true } gloo-timers = { version = "0.3.0", features = ["futures"], optional = true } wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/event-table-client/Cargo.toml b/sdks/rust/tests/event-table-client/Cargo.toml index 2fdbb9f04a0..86d85c0abb9 100644 --- a/sdks/rust/tests/event-table-client/Cargo.toml +++ b/sdks/rust/tests/event-table-client/Cargo.toml @@ -41,5 +41,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml index c3f668c260f..1b9d2507c39 100644 --- a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml +++ b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml @@ -38,5 +38,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/procedure-client/Cargo.toml b/sdks/rust/tests/procedure-client/Cargo.toml index c11bb90d108..e88384e9884 100644 --- a/sdks/rust/tests/procedure-client/Cargo.toml +++ b/sdks/rust/tests/procedure-client/Cargo.toml @@ -46,5 +46,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml index eb747915fba..8dadef8aa01 100644 --- a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml +++ b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml @@ -40,5 +40,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/test-client/Cargo.toml b/sdks/rust/tests/test-client/Cargo.toml index 06a92e1f5fb..25ecb5e7615 100644 --- a/sdks/rust/tests/test-client/Cargo.toml +++ b/sdks/rust/tests/test-client/Cargo.toml @@ -50,5 +50,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "gloo-timers", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/test-counter/Cargo.toml b/sdks/rust/tests/test-counter/Cargo.toml index fa0c9cebb33..e246865f13d 100644 --- a/sdks/rust/tests/test-counter/Cargo.toml +++ b/sdks/rust/tests/test-counter/Cargo.toml @@ -9,7 +9,6 @@ license-file = "LICENSE" [dependencies] spacetimedb-data-structures.workspace = true anyhow.workspace = true -futures.workspace = true gloo-timers = { version = "0.3.0", features = ["futures"] } [lints] diff --git a/sdks/rust/tests/view-client/Cargo.toml b/sdks/rust/tests/view-client/Cargo.toml index 2ca445f54a3..b3c7b182a66 100644 --- a/sdks/rust/tests/view-client/Cargo.toml +++ b/sdks/rust/tests/view-client/Cargo.toml @@ -45,5 +45,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/sdks/rust/tests/view-pk-client/Cargo.toml b/sdks/rust/tests/view-pk-client/Cargo.toml index 8e96bea8ec2..5b9b9e2cce6 100644 --- a/sdks/rust/tests/view-pk-client/Cargo.toml +++ b/sdks/rust/tests/view-pk-client/Cargo.toml @@ -42,5 +42,8 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } +[package.metadata.cargo-machete] +ignored = ["futures", "wasm-bindgen-futures"] + [lints] workspace = true diff --git a/templates/chat-console-rs/Cargo.toml b/templates/chat-console-rs/Cargo.toml index af92400907a..7988f26960a 100644 --- a/templates/chat-console-rs/Cargo.toml +++ b/templates/chat-console-rs/Cargo.toml @@ -6,5 +6,8 @@ edition = "2024" [workspace] exclude = ["spacetimedb"] +[workspace.dependencies] +log = "0.4" + [dependencies] spacetimedb-sdk = { path = "../../sdks/rust" } diff --git a/tools/replace-spacetimedb/Cargo.toml b/tools/replace-spacetimedb/Cargo.toml index ef4fc11e572..7c3070b1be1 100644 --- a/tools/replace-spacetimedb/Cargo.toml +++ b/tools/replace-spacetimedb/Cargo.toml @@ -15,4 +15,3 @@ path = "src/main.rs" clap.workspace = true regex.workspace = true ignore = "0.4" -memchr = "2" \ No newline at end of file diff --git a/tools/xtask-llm-benchmark/src/templates/rust/server/Cargo.toml b/tools/xtask-llm-benchmark/src/templates/rust/server/Cargo.toml index d925f98168b..aa94efb4670 100644 --- a/tools/xtask-llm-benchmark/src/templates/rust/server/Cargo.toml +++ b/tools/xtask-llm-benchmark/src/templates/rust/server/Cargo.toml @@ -9,5 +9,3 @@ edition = "2021" crate-type = ["cdylib"] [dependencies] -spacetimedb = { path = "../../../../../../sdks/rust/" } -log = "0.4" From 2b0e8612f29a220992e3bc2e102c021fef71e255 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 14:52:55 -0400 Subject: [PATCH 28/37] Address dep-check review --- .github/workflows/ci.yml | 37 ++++++++++++++++--- Cargo.lock | 4 -- crates/bench/Cargo.toml | 4 -- crates/bindings/Cargo.toml | 1 + crates/client-api/Cargo.toml | 4 -- crates/lib/Cargo.toml | 1 + crates/sqltest/Cargo.toml | 4 -- crates/standalone/Cargo.toml | 1 + crates/testing/Cargo.toml | 4 -- sdks/rust/Cargo.toml | 1 + .../connect_disconnect_client/Cargo.toml | 3 +- sdks/rust/tests/event-table-client/Cargo.toml | 1 + .../procedural-view-pk-client/Cargo.toml | 1 + sdks/rust/tests/procedure-client/Cargo.toml | 1 + .../procedure-concurrency-client/Cargo.toml | 1 + sdks/rust/tests/test-client/Cargo.toml | 1 + sdks/rust/tests/view-client/Cargo.toml | 1 + sdks/rust/tests/view-pk-client/Cargo.toml | 1 + 18 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57aacf41188..7a49190d77f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -445,12 +445,6 @@ jobs: - name: Run ci lint run: cargo ci lint - - name: Install cargo-machete - run: cargo install cargo-machete --locked - - - name: Run dependency check - run: cargo ci dep-check - - name: Upload public lint Cargo timing reports if: always() uses: actions/upload-artifact@v4 @@ -459,6 +453,37 @@ jobs: path: ${{ github.workspace }}/target/cargo-timings/ retention-days: 30 + dep_check: + needs: [merge_queue_noop] + if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} + name: Dependency Check + runs-on: spacetimedb-new-runner-2 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + steps: + - name: Checkout sources + uses: actions/checkout@v3 + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + - 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 + save-if: false + prefix-key: v1 + + - name: Install cargo-machete + run: cargo install cargo-machete --locked + + - name: Run dependency check + run: cargo ci dep-check + codeowners_check: if: ${{ github.event_name == 'pull_request' }} name: CODEOWNERS check diff --git a/Cargo.lock b/Cargo.lock index 6a080cdff18..64ae2828a25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7830,7 +7830,6 @@ dependencies = [ "serial_test", "smallvec", "spacetimedb-client-api-messages", - "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-execution", @@ -7972,7 +7971,6 @@ dependencies = [ "serde_json", "serde_with", "spacetimedb-client-api-messages", - "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-jsonwebtoken", "spacetimedb-lib", @@ -8817,7 +8815,6 @@ dependencies = [ "spacetimedb-cli", "spacetimedb-client-api", "spacetimedb-client-api-messages", - "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-guard", "spacetimedb-lib", @@ -8911,7 +8908,6 @@ dependencies = [ "quick-junit", "rusqlite", "rust_decimal", - "spacetimedb-core", "spacetimedb-lib", "spacetimedb-sats", "sqllogictest", diff --git a/crates/bench/Cargo.toml b/crates/bench/Cargo.toml index 7777edd22d3..3d0a3cc0797 100644 --- a/crates/bench/Cargo.toml +++ b/crates/bench/Cargo.toml @@ -38,7 +38,6 @@ bench = false [dependencies] spacetimedb-client-api-messages = { path = "../client-api-messages" } -spacetimedb-core = { path = "../core", features = ["test"] } spacetimedb-data-structures.workspace = true spacetimedb-datastore.workspace = true spacetimedb-execution = { path = "../execution" } @@ -90,8 +89,5 @@ iai-callgrind = { git = "https://github.com/clockworklabs/iai-callgrind.git", br iai-callgrind-runner = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } iai-callgrind-macros = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } -[package.metadata.cargo-machete] -ignored = ["spacetimedb-core"] - [lints] workspace = true diff --git a/crates/bindings/Cargo.toml b/crates/bindings/Cargo.toml index d32109b96ac..d241d8959be 100644 --- a/crates/bindings/Cargo.toml +++ b/crates/bindings/Cargo.toml @@ -44,6 +44,7 @@ insta.workspace = true trybuild.workspace = true [package.metadata.cargo-machete] +# See the `getrandom02` dependency comment above. ignored = ["getrandom02"] [lints] diff --git a/crates/client-api/Cargo.toml b/crates/client-api/Cargo.toml index da9b34609ae..923aa4ea87f 100644 --- a/crates/client-api/Cargo.toml +++ b/crates/client-api/Cargo.toml @@ -8,7 +8,6 @@ rust-version.workspace = true [dependencies] spacetimedb-client-api-messages.workspace = true -spacetimedb-core.workspace = true spacetimedb-data-structures.workspace = true spacetimedb-lib = { workspace = true, features = ["serde"] } spacetimedb-paths.workspace = true @@ -61,9 +60,6 @@ proptest.workspace = true tokio = { workspace = true, features = ["full", "test-util"] } toml.workspace = true -[package.metadata.cargo-machete] -ignored = ["spacetimedb-core"] - [lints] workspace = true diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index ff1c4ee9656..06dbd88c97c 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -59,6 +59,7 @@ proptest.workspace = true proptest-derive.workspace = true [package.metadata.cargo-machete] +# Used through the `proptest` feature's `dep:proptest-derive` entry. ignored = ["proptest-derive"] [lints] diff --git a/crates/sqltest/Cargo.toml b/crates/sqltest/Cargo.toml index 2cc68893075..8500b2f4062 100644 --- a/crates/sqltest/Cargo.toml +++ b/crates/sqltest/Cargo.toml @@ -8,7 +8,6 @@ publish = false [dependencies] spacetimedb-lib.workspace = true -spacetimedb-core.workspace = true spacetimedb-sats.workspace = true anyhow.workspace = true @@ -29,8 +28,5 @@ tempfile.workspace = true tokio.workspace = true tokio-postgres.workspace = true -[package.metadata.cargo-machete] -ignored = ["spacetimedb-core"] - [lints] workspace = true diff --git a/crates/standalone/Cargo.toml b/crates/standalone/Cargo.toml index e9186bb18c3..c3904619d91 100644 --- a/crates/standalone/Cargo.toml +++ b/crates/standalone/Cargo.toml @@ -61,6 +61,7 @@ once_cell.workspace = true tempfile.workspace = true [package.metadata.cargo-machete] +# Used through feature forwarding for standalone-only core features. ignored = ["spacetimedb-core"] [lints] diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index 1e82be444b0..c45b60a6b5c 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -12,7 +12,6 @@ allow_loopback_http_for_tests = ["spacetimedb-standalone/allow_loopback_http_for spacetimedb-cli.workspace = true spacetimedb-data-structures.workspace = true spacetimedb-lib.workspace = true -spacetimedb-core.workspace = true spacetimedb-standalone.workspace = true spacetimedb-client-api.workspace = true spacetimedb-client-api-messages.workspace = true @@ -37,8 +36,5 @@ futures.workspace = true [dev-dependencies] serial_test.workspace = true -[package.metadata.cargo-machete] -ignored = ["spacetimedb-core"] - [lints] workspace = true diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 396b15cbea8..7523aea66b9 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -75,6 +75,7 @@ shlex = "1.3.0" spacetimedb-testing = { path = "../../crates/testing" } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:getrandom` entry. ignored = ["getrandom"] [lints] diff --git a/sdks/rust/tests/connect_disconnect_client/Cargo.toml b/sdks/rust/tests/connect_disconnect_client/Cargo.toml index 5f7e7eda720..946a0b16dbf 100644 --- a/sdks/rust/tests/connect_disconnect_client/Cargo.toml +++ b/sdks/rust/tests/connect_disconnect_client/Cargo.toml @@ -34,8 +34,8 @@ required-features = ["native"] spacetimedb-sdk = { path = "../.." } test-counter = { path = "../test-counter" } anyhow.workspace = true -tokio = { workspace = true, optional = true } futures = { workspace = true, optional = true } +tokio = { workspace = true, optional = true } gloo-timers = { version = "0.3.0", features = ["futures"], optional = true } wasm-bindgen = { version = "0.2.100", optional = true } @@ -43,6 +43,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/event-table-client/Cargo.toml b/sdks/rust/tests/event-table-client/Cargo.toml index 86d85c0abb9..0f34cc1b79f 100644 --- a/sdks/rust/tests/event-table-client/Cargo.toml +++ b/sdks/rust/tests/event-table-client/Cargo.toml @@ -42,6 +42,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml index 1b9d2507c39..47bf0a1d896 100644 --- a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml +++ b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml @@ -39,6 +39,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entry. ignored = ["wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/procedure-client/Cargo.toml b/sdks/rust/tests/procedure-client/Cargo.toml index e88384e9884..6c32936d454 100644 --- a/sdks/rust/tests/procedure-client/Cargo.toml +++ b/sdks/rust/tests/procedure-client/Cargo.toml @@ -47,6 +47,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml index 8dadef8aa01..8f7f066a403 100644 --- a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml +++ b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml @@ -41,6 +41,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/test-client/Cargo.toml b/sdks/rust/tests/test-client/Cargo.toml index 25ecb5e7615..9f837e13884 100644 --- a/sdks/rust/tests/test-client/Cargo.toml +++ b/sdks/rust/tests/test-client/Cargo.toml @@ -51,6 +51,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "gloo-timers", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/view-client/Cargo.toml b/sdks/rust/tests/view-client/Cargo.toml index b3c7b182a66..f991019f791 100644 --- a/sdks/rust/tests/view-client/Cargo.toml +++ b/sdks/rust/tests/view-client/Cargo.toml @@ -46,6 +46,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/view-pk-client/Cargo.toml b/sdks/rust/tests/view-pk-client/Cargo.toml index 5b9b9e2cce6..dfdb83325ef 100644 --- a/sdks/rust/tests/view-pk-client/Cargo.toml +++ b/sdks/rust/tests/view-pk-client/Cargo.toml @@ -43,6 +43,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] +# Used through the `wasm` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] From 03cb678edff77a852929018041d731a05361d014 Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 15:07:53 -0400 Subject: [PATCH 29/37] Clarify dep-check ignore comments --- sdks/rust/tests/procedure-client/Cargo.toml | 2 +- templates/chat-console-rs/Cargo.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sdks/rust/tests/procedure-client/Cargo.toml b/sdks/rust/tests/procedure-client/Cargo.toml index 6c32936d454..f7d41cb4b0a 100644 --- a/sdks/rust/tests/procedure-client/Cargo.toml +++ b/sdks/rust/tests/procedure-client/Cargo.toml @@ -47,7 +47,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/templates/chat-console-rs/Cargo.toml b/templates/chat-console-rs/Cargo.toml index 7988f26960a..2f1479eda88 100644 --- a/templates/chat-console-rs/Cargo.toml +++ b/templates/chat-console-rs/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" exclude = ["spacetimedb"] [workspace.dependencies] +# Required by the nested `spacetimedb` package's `log.workspace = true`. log = "0.4" [dependencies] From c1b498cd8d44679d05780f158f27c6445533f58d Mon Sep 17 00:00:00 2001 From: clockwork-labs-bot Date: Sun, 9 Aug 2026 15:11:53 -0400 Subject: [PATCH 30/37] Fix browser feature dep-check comments --- sdks/rust/tests/connect_disconnect_client/Cargo.toml | 2 +- sdks/rust/tests/event-table-client/Cargo.toml | 2 +- sdks/rust/tests/procedural-view-pk-client/Cargo.toml | 2 +- sdks/rust/tests/procedure-concurrency-client/Cargo.toml | 2 +- sdks/rust/tests/test-client/Cargo.toml | 2 +- sdks/rust/tests/view-client/Cargo.toml | 2 +- sdks/rust/tests/view-pk-client/Cargo.toml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/sdks/rust/tests/connect_disconnect_client/Cargo.toml b/sdks/rust/tests/connect_disconnect_client/Cargo.toml index 946a0b16dbf..26a25e0d3a3 100644 --- a/sdks/rust/tests/connect_disconnect_client/Cargo.toml +++ b/sdks/rust/tests/connect_disconnect_client/Cargo.toml @@ -43,7 +43,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/event-table-client/Cargo.toml b/sdks/rust/tests/event-table-client/Cargo.toml index 0f34cc1b79f..86545ca44fd 100644 --- a/sdks/rust/tests/event-table-client/Cargo.toml +++ b/sdks/rust/tests/event-table-client/Cargo.toml @@ -42,7 +42,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml index 47bf0a1d896..fb86336c40f 100644 --- a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml +++ b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml @@ -39,7 +39,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entry. +# Used through the `browser` feature's `dep:` entry. ignored = ["wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml index 8f7f066a403..78a2fe9fe45 100644 --- a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml +++ b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml @@ -41,7 +41,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/test-client/Cargo.toml b/sdks/rust/tests/test-client/Cargo.toml index 9f837e13884..c40c83d3c86 100644 --- a/sdks/rust/tests/test-client/Cargo.toml +++ b/sdks/rust/tests/test-client/Cargo.toml @@ -51,7 +51,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "gloo-timers", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/view-client/Cargo.toml b/sdks/rust/tests/view-client/Cargo.toml index f991019f791..be29ed953f1 100644 --- a/sdks/rust/tests/view-client/Cargo.toml +++ b/sdks/rust/tests/view-client/Cargo.toml @@ -46,7 +46,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] diff --git a/sdks/rust/tests/view-pk-client/Cargo.toml b/sdks/rust/tests/view-pk-client/Cargo.toml index dfdb83325ef..30d0ff4b1b4 100644 --- a/sdks/rust/tests/view-pk-client/Cargo.toml +++ b/sdks/rust/tests/view-pk-client/Cargo.toml @@ -43,7 +43,7 @@ wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } [package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:` entries. +# Used through the `browser` feature's `dep:` entries. ignored = ["futures", "wasm-bindgen-futures"] [lints] From b91ddc9d00b2d926705facc9d6fa634ce7e85710 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Sun, 9 Aug 2026 12:31:48 -0700 Subject: [PATCH 31/37] [bot/cargo-ci-dep-check]: replace log dep --- templates/chat-console-rs/spacetimedb/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/chat-console-rs/spacetimedb/Cargo.toml b/templates/chat-console-rs/spacetimedb/Cargo.toml index 541a0cee506..3c1f893369a 100644 --- a/templates/chat-console-rs/spacetimedb/Cargo.toml +++ b/templates/chat-console-rs/spacetimedb/Cargo.toml @@ -11,4 +11,4 @@ crate-type = ["cdylib"] [dependencies] spacetimedb = { path = "../../../crates/bindings" } -log.workspace = true +log = "0.4" From bdde8d90f008e7d279606454396ca6295ee8866d Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Sun, 9 Aug 2026 12:35:12 -0700 Subject: [PATCH 32/37] [bot/cargo-ci-dep-check]: remove --- templates/chat-console-rs/Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/templates/chat-console-rs/Cargo.toml b/templates/chat-console-rs/Cargo.toml index 2f1479eda88..af92400907a 100644 --- a/templates/chat-console-rs/Cargo.toml +++ b/templates/chat-console-rs/Cargo.toml @@ -6,9 +6,5 @@ edition = "2024" [workspace] exclude = ["spacetimedb"] -[workspace.dependencies] -# Required by the nested `spacetimedb` package's `log.workspace = true`. -log = "0.4" - [dependencies] spacetimedb-sdk = { path = "../../sdks/rust" } From 3eae0f8e43383ff52cf174226ae52434ff9e03b6 Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Wed, 12 Aug 2026 08:16:45 -0700 Subject: [PATCH 33/37] Apply suggestion from @bfops Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com> --- sdks/rust/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 7523aea66b9..1dd1edf16cb 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -44,6 +44,7 @@ log.workspace = true once_cell.workspace = true prometheus.workspace = true rand.workspace = true + getrandom = { version = "0.3.4", features = ["wasm_js"], optional = true } gloo-console = { version = "0.3.0", optional = true } gloo-net = { version = "0.6.0", optional = true } From 08e17c31577065a5b733475a6323ee14a98c0abe Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:15:59 -0700 Subject: [PATCH 34/37] Apply suggestion from @bfops Signed-off-by: Zeke Foppa <196249+bfops@users.noreply.github.com> --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bce84f55ba..b0b0c6150af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -715,14 +715,6 @@ jobs: run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - 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 - save-if: false - prefix-key: v1 - - name: Install cargo-machete run: cargo install cargo-machete --locked From de64ed72643fee012ea87250caa4e812dfb0337b Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 14 Aug 2026 08:18:39 -0700 Subject: [PATCH 35/37] [bot/cargo-ci-dep-check]: move --- .github/workflows/ci.yml | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0b0c6150af..9ed0f3ba52a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -698,29 +698,6 @@ jobs: path: ${{ env.CARGO_TARGET_DIR }}/cargo-timings/ retention-days: 14 - dep_check: - needs: [merge_queue_noop] - if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} - name: Dependency Check - runs-on: spacetimedb-new-runner-2 - env: - CARGO_TARGET_DIR: ${{ github.workspace }}/target - RUST_BACKTRACE: full - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - uses: dsherret/rust-toolchain-file@v1 - - name: Set default rust toolchain - run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - - name: Install cargo-machete - run: cargo install cargo-machete --locked - - - name: Run dependency check - run: cargo ci dep-check - codeowners_check: if: ${{ github.event_name == 'pull_request' }} name: CODEOWNERS check @@ -1650,3 +1627,26 @@ jobs: # - name: Print rows in the user table # if: always() # run: spacetime sql quickstart-chat "SELECT * FROM user" + + dep_check: + needs: [merge_queue_noop] + if: ${{ needs.merge_queue_noop.outputs.skip != 'true' }} + name: Dependency Check + runs-on: spacetimedb-new-runner-2 + env: + CARGO_TARGET_DIR: ${{ github.workspace }}/target + RUST_BACKTRACE: full + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - uses: dsherret/rust-toolchain-file@v1 + - name: Set default rust toolchain + run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) + - run: echo ::add-matcher::.github/workflows/rust_matcher.json + + - name: Install cargo-machete + run: cargo install cargo-machete --locked + + - name: Run dependency check + run: cargo ci dep-check From 4d4216e708635ca7852a1eec369543d05465a0a4 Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 14 Aug 2026 11:16:01 -0700 Subject: [PATCH 36/37] [bot/cargo-ci-dep-check]: use shear --- .github/workflows/ci.yml | 7 +- Cargo.lock | 263 +----------------- Cargo.toml | 19 -- crates/bench/Cargo.toml | 31 ++- crates/bindings/Cargo.toml | 3 - crates/cli/Cargo.toml | 2 - crates/client-api/Cargo.toml | 2 + crates/core/Cargo.toml | 6 - crates/durability/Cargo.toml | 2 +- crates/lib/Cargo.toml | 3 - crates/smoketests/Cargo.toml | 1 - crates/sqltest/Cargo.toml | 1 + crates/standalone/Cargo.toml | 5 - crates/testing/Cargo.toml | 3 +- .../connect_disconnect_client/Cargo.toml | 3 - sdks/rust/tests/event-table-client/Cargo.toml | 3 - .../procedural-view-pk-client/Cargo.toml | 3 - sdks/rust/tests/procedure-client/Cargo.toml | 3 - .../procedure-concurrency-client/Cargo.toml | 3 - sdks/rust/tests/test-client/Cargo.toml | 3 - sdks/rust/tests/view-client/Cargo.toml | 3 - sdks/rust/tests/view-pk-client/Cargo.toml | 3 - tools/ci/commands/dep-check/src/main.rs | 5 +- 23 files changed, 40 insertions(+), 337 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ed0f3ba52a..9e9d5003fef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1645,8 +1645,11 @@ jobs: run: rustup default $(rustup show active-toolchain | cut -d' ' -f1) - run: echo ::add-matcher::.github/workflows/rust_matcher.json - - name: Install cargo-machete - run: cargo install cargo-machete --locked + - name: Install Rust 1.95 for cargo-shear + run: rustup toolchain install 1.95.0 --profile minimal + + - name: Install cargo-shear + run: cargo +1.95.0 install cargo-shear --locked - name: Run dependency check run: cargo ci dep-check diff --git a/Cargo.lock b/Cargo.lock index 4927afba276..2d2ea7a1c05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -210,21 +210,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "assert_cmd" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbb6924530aa9e0432442af08bbcafdad182db80d2e560da42a6d442535bf85" -dependencies = [ - "anstyle", - "bstr", - "libc", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - [[package]] name = "async-channel" version = "2.5.0" @@ -634,7 +619,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", - "regex-automata", "serde", ] @@ -1672,31 +1656,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.11", - "parking_lot 0.12.5", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crunchy" version = "0.2.4" @@ -1722,45 +1681,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "cursive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5438eb16bdd8af51b31e74764fef5d0a9260227a5ec82ba75c9d11ce46595839" -dependencies = [ - "ahash 0.8.12", - "cfg-if", - "crossbeam-channel", - "crossterm", - "cursive_core", - "lazy_static", - "libc", - "log", - "signal-hook", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "cursive_core" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4db3b58161228d0dcb45c7968c5e74c3f03ad39e8983e58ad7d57061aa2cd94d" -dependencies = [ - "ahash 0.8.12", - "crossbeam-channel", - "enum-map", - "enumset", - "lazy_static", - "log", - "num", - "owning_ref", - "time", - "unicode-segmentation", - "unicode-width 0.1.14", - "xi-unicode", -] - [[package]] name = "custom_debug" version = "0.5.1" @@ -2199,27 +2119,6 @@ dependencies = [ "syn 2.0.107", ] -[[package]] -name = "enumset" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b07a8dfbbbfc0064c0a6bdf9edcf966de6b1c33ce344bdeca3b41615452634" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43e744e4ea338060faee68ed933e46e722fb7f3617e722a5772d7e856d8b3ce" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.107", -] - [[package]] name = "env_filter" version = "0.1.4" @@ -3264,25 +3163,8 @@ name = "iai-callgrind-runner" version = "0.8.0" source = "git+https://github.com/clockworklabs/iai-callgrind.git?branch=main#3e78f68534bbe389e0fb00f729a6a7b877627a06" dependencies = [ - "anyhow", - "bincode", - "cargo_metadata", - "clap 3.2.23", - "colored", - "env_logger 0.10.2", - "glob", - "indexmap 1.9.3", - "inferno 0.11.21", "lazy_static", - "log", - "regex", - "sanitize-filename", "serde", - "serde_json", - "shlex", - "tempfile", - "version-compare", - "which 4.4.2", ] [[package]] @@ -3536,26 +3418,6 @@ dependencies = [ "cfb", ] -[[package]] -name = "inferno" -version = "0.11.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" -dependencies = [ - "ahash 0.8.12", - "crossbeam-channel", - "crossbeam-utils", - "dashmap 6.1.0", - "is-terminal", - "itoa", - "log", - "num-format", - "once_cell", - "quick-xml 0.26.0", - "rgb", - "str_stack", -] - [[package]] name = "inferno" version = "0.12.3" @@ -4219,18 +4081,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.1.0" @@ -4483,7 +4333,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio 1.1.0", + "mio", "notify-types", "walkdir", "windows-sys 0.52.0", @@ -4617,15 +4467,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "number_prefix" version = "0.4.0" @@ -4788,15 +4629,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" -[[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "owo-colors" version = "3.5.0" @@ -5752,7 +5584,7 @@ dependencies = [ "anyhow", "backtrace", "flate2", - "inferno 0.12.3", + "inferno", "num", "paste", "prost", @@ -5787,16 +5619,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" -[[package]] -name = "predicates-tree" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "pretty_assertions" version = "1.4.1" @@ -6077,15 +5899,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "quick-xml" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.31.0" @@ -6697,7 +6510,7 @@ dependencies = [ "inotify 0.11.0", "kqueue", "libc", - "mio 1.1.0", + "mio", "objc2-core-foundation", "objc2-core-services", "rolldown-notify-types", @@ -7203,16 +7016,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "sanitize-filename" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ed72fbaf78e6f2d41744923916966c4fbe3d7c74e3037a8ee482f1115572603" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "schannel" version = "0.1.28" @@ -7629,17 +7432,6 @@ dependencies = [ "signal-hook-registry", ] -[[package]] -name = "signal-hook-mio" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" -dependencies = [ - "libc", - "mio 0.8.11", - "signal-hook", -] - [[package]] name = "signal-hook-registry" version = "1.4.6" @@ -7835,8 +7627,6 @@ dependencies = [ "foldhash 0.2.0", "hashbrown 0.16.1", "iai-callgrind", - "iai-callgrind-macros", - "iai-callgrind-runner", "itertools 0.12.1", "lazy_static", "log", @@ -7849,6 +7639,7 @@ dependencies = [ "serial_test", "smallvec", "spacetimedb-client-api-messages", + "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-datastore", "spacetimedb-execution", @@ -7861,7 +7652,6 @@ dependencies = [ "spacetimedb-table", "spacetimedb-testing", "tempdir", - "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", "tracing-subscriber", @@ -7903,7 +7693,6 @@ dependencies = [ "dirs", "duct", "fs-err", - "fs_extra", "futures", "git2", "glob", @@ -7944,7 +7733,6 @@ dependencies = [ "termcolor", "termtree", "thiserror 1.0.69", - "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", "tokio-tungstenite 0.27.0", @@ -7990,7 +7778,9 @@ dependencies = [ "serde_json", "serde_with", "spacetimedb-client-api-messages", + "spacetimedb-core", "spacetimedb-data-structures", + "spacetimedb-datastore", "spacetimedb-jsonwebtoken", "spacetimedb-lib", "spacetimedb-paths", @@ -8094,15 +7884,12 @@ dependencies = [ "bytestring", "chrono", "core_affinity", - "criterion", "crossbeam-queue", "deno_core_icudata", "derive_more 0.99.20", "enum-map", - "env_logger 0.10.2", "faststr", "flate2", - "fs_extra", "futures", "futures-util", "http 1.3.1", @@ -8122,8 +7909,6 @@ dependencies = [ "pretty_assertions", "prometheus", "proptest", - "proptest-derive", - "rand 0.9.2", "rayon", "rayon-core", "regex", @@ -8166,7 +7951,6 @@ dependencies = [ "tempfile", "thiserror 1.0.69", "tikv-jemalloc-ctl", - "tikv-jemallocator", "tokio", "tokio-metrics", "tokio-stream", @@ -8637,7 +8421,6 @@ dependencies = [ "anymap3", "brotli", "bytes", - "cursive", "flate2", "futures", "futures-channel", @@ -8646,7 +8429,6 @@ dependencies = [ "gloo-net", "gloo-storage", "gloo-utils", - "hex", "home", "http 1.3.1", "js-sys", @@ -8654,7 +8436,6 @@ dependencies = [ "native-tls", "once_cell", "prometheus", - "rand 0.9.2", "shlex", "spacetimedb-client-api-messages", "spacetimedb-data-structures", @@ -8677,7 +8458,6 @@ name = "spacetimedb-smoketests" version = "2.8.1" dependencies = [ "anyhow", - "assert_cmd", "cargo_metadata", "fs_extra", "predicates", @@ -8766,7 +8546,6 @@ dependencies = [ "spacetimedb-table", "tempfile", "thiserror 1.0.69", - "tikv-jemalloc-ctl", "tikv-jemallocator", "tokio", "toml 0.8.23", @@ -8833,6 +8612,7 @@ dependencies = [ "spacetimedb-cli", "spacetimedb-client-api", "spacetimedb-client-api-messages", + "spacetimedb-core", "spacetimedb-data-structures", "spacetimedb-guard", "spacetimedb-lib", @@ -8926,6 +8706,7 @@ dependencies = [ "quick-junit", "rusqlite", "rust_decimal", + "spacetimedb-core", "spacetimedb-lib", "spacetimedb-sats", "sqllogictest", @@ -9462,9 +9243,7 @@ checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -9543,7 +9322,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.1.0", + "mio", "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", @@ -10279,12 +10058,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "version-compare" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" - [[package]] name = "version_check" version = "0.9.5" @@ -10804,18 +10577,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - [[package]] name = "which" version = "6.0.3" @@ -11468,12 +11229,6 @@ version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" -[[package]] -name = "xi-unicode" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" - [[package]] name = "xml-rs" version = "0.8.28" diff --git a/Cargo.toml b/Cargo.toml index 8c7c2d2af1a..446c12435bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,20 +185,17 @@ spacetimedb-table = { path = "crates/table", version = "=2.8.1" } # from appearing in module dependency graphs. ahash = { version = "0.8", default-features = false, features = ["std"] } anyhow = "1.0.68" -anymap = "0.12" arrayvec = "0.7.2" async-channel = "2.5" async-stream = "0.3.6" async-trait = "0.1.68" axum = { version = "0.7", features = ["tracing", "http2"] } axum-extra = { version = "0.9", features = ["typed-header"] } -backtrace = "0.3.66" base64 = "0.21.2" bigdecimal = "0.4.7" bitflags = "2.3.3" blake3 = "1.5.1" brotli = "3.5" -byte-unit = "4.0.18" bytemuck = { version = "1.16.2", features = ["must_cast"] } bytes = "1.10.1" bytestring = { version = "1.2.0", features = ["serde"] } @@ -211,16 +208,13 @@ 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"] } -crossbeam-channel = "0.5" crossbeam-queue = "0.3.12" -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 } dirs = "5.0.1" duct = "0.13.5" either = "1.9" -email_address = "0.2.4" enum-as-inner = "0.6" enum-map = "2.6.3" env_logger = "0.10" @@ -241,14 +235,12 @@ headers = "0.4" heck = "0.4" hex = "0.4.3" home = "0.5" -hostname = "^0.3" http = "1.0" http-body-util= "0.1.3" humantime = "2.3" hyper = "1.0" hyper-util = { version = "0.1", features = ["tokio"] } ignore = "0.4" -imara-diff = "0.1.3" indexmap = "2.0.0" indicatif = "0.17" insta = { version = "1.21.0", features = ["toml", "filters"] } @@ -284,11 +276,9 @@ prometheus = "0.14.0" proptest = "1.4" proptest-derive = "0.5" quick-junit = { version = "0.3.2" } -quick-xml = "0.31" quote = "1.0.8" rand08 = { package = "rand", version = "0.8" } rand = "0.9" -rand_distr = "0.5.1" rayon = "1.8" rayon-core = "1.11.0" regex = "1" @@ -303,7 +293,6 @@ rust_decimal = { version = "1.29.1", features = ["db-tokio-postgres"] } rustc-demangle = "0.1.21" rustc-hash = "2" rustyline = { version = "12.0.0", features = [] } -scoped-tls = "1.0.1" scopeguard = "1.1.0" second-stack = "0.3" self-replace = "1.5" @@ -313,16 +302,13 @@ serde_json = { version = "1.0.128", features = ["raw_value"] } serde_path_to_error = "0.1.9" serde_with = { version = "3.3.0", features = ["base64", "hex"] } serial_test = "2.0.0" -sha1 = "0.10.1" sha3 = "0.10.0" -similar = "2.3" slab = "0.4.7" sled = "0.34.7" smallvec = { version = "1.11", features = ["union", "const_generics"] } socket2 = "0.5" sourcemap = "9.3.1" sqllogictest = "0.17" -sqllogictest-engines = "0.17" sqlparser = "0.38.0" strum = { version = "0.25.0", features = ["derive"] } syn = { version = "2", features = ["full", "extra-traits"] } @@ -336,7 +322,6 @@ termtree = "0.5.1" thin-vec = "0.2.13" thiserror = "1.0.37" tokio = { version = "1.37", features = ["full"] } -tokio_metrics = { version = "0.4.0" } tokio-postgres = { version = "0.7.8", features = ["with-chrono-0_4"] } tokio-stream = "0.1.17" tokio-tungstenite = { version = "0.27.0", features = ["native-tls", "url"] } @@ -344,16 +329,12 @@ tokio-util = { version = "0.7.4", features = ["time"] } toml = "0.8" toml_edit = "0.22.22" tower-http = { version = "0.5", features = ["cors"] } -tower-layer = "0.3" -tower-service = "0.3" tracing = "0.1.37" tracing-appender = "0.2.2" tracing-core = "0.1.31" tracing-flame = "0.2.0" -tracing-log = "0.1.3" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } trybuild = "1" -typed-arena = "2.0" unicode-ident = "1.0.12" unicode-normalization = "0.1.23" url = "2.3.1" diff --git a/crates/bench/Cargo.toml b/crates/bench/Cargo.toml index 3d0a3cc0797..ccb3245fe84 100644 --- a/crates/bench/Cargo.toml +++ b/crates/bench/Cargo.toml @@ -37,47 +37,50 @@ name = "summarize" bench = false [dependencies] -spacetimedb-client-api-messages = { path = "../client-api-messages" } +spacetimedb-core = { path = "../core", features = ["test"] } spacetimedb-data-structures.workspace = true spacetimedb-datastore.workspace = true -spacetimedb-execution = { path = "../execution" } spacetimedb-lib = { path = "../lib" } spacetimedb-paths.workspace = true spacetimedb-primitives = { path = "../primitives" } -spacetimedb-query = { path = "../query" } spacetimedb-sats = { path = "../sats" } spacetimedb-schema = { workspace = true, features = ["test"] } -spacetimedb-table = { path = "../table" } spacetimedb-testing = { path = "../testing" } -ahash.workspace = true anyhow.workspace = true convert_case.workspace = true clap.workspace = true criterion.workspace = true -foldhash.workspace = true -hashbrown.workspace = true lazy_static.workspace = true log.workspace = true -mimalloc.workspace = true -rand.workspace = true regex.workspace = true rusqlite.workspace = true serde.workspace = true serde_json.workspace = true serial_test.workspace = true -smallvec.workspace = true tempdir.workspace = true tokio.workspace = true tracing-subscriber.workspace = true walkdir.workspace = true + +[dev-dependencies] +spacetimedb-client-api-messages = { path = "../client-api-messages" } +spacetimedb-execution = { path = "../execution" } +spacetimedb-query = { path = "../query" } +spacetimedb-table = { path = "../table" } + +ahash.workspace = true +foldhash.workspace = true +hashbrown.workspace = true itertools.workspace = true +mimalloc.workspace = true +rand.workspace = true +smallvec.workspace = true -[target.'cfg(not(target_env = "msvc"))'.dependencies] +[target.'cfg(not(target_env = "msvc"))'.dev-dependencies] tikv-jemallocator = { workspace = true } -tikv-jemalloc-ctl = { workspace = true } -[target.'cfg(target_os = "linux")'.dependencies] +[target.'cfg(target_os = "linux")'.dev-dependencies] # only try to build these on linux # also: @@ -86,8 +89,6 @@ tikv-jemalloc-ctl = { workspace = true } # iai-callgrind = "0.7.2" iai-callgrind = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } -iai-callgrind-runner = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } -iai-callgrind-macros = { git = "https://github.com/clockworklabs/iai-callgrind.git", branch = "main" } [lints] workspace = true diff --git a/crates/bindings/Cargo.toml b/crates/bindings/Cargo.toml index d241d8959be..64ecd2c2bfe 100644 --- a/crates/bindings/Cargo.toml +++ b/crates/bindings/Cargo.toml @@ -43,9 +43,6 @@ serde_json.workspace = true insta.workspace = true trybuild.workspace = true -[package.metadata.cargo-machete] -# See the `getrandom02` dependency comment above. -ignored = ["getrandom02"] [lints] workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 81f406ebbec..5ea9f718833 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -84,11 +84,9 @@ path-clean = "1.0.1" [dev-dependencies] pretty_assertions.workspace = true -fs_extra.workspace = true [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = { workspace = true } -tikv-jemalloc-ctl = { workspace = true } [target.'cfg(windows)'.dependencies] windows-sys = { workspace = true, features = ["Win32_System_Console"] } diff --git a/crates/client-api/Cargo.toml b/crates/client-api/Cargo.toml index 923aa4ea87f..1940230c119 100644 --- a/crates/client-api/Cargo.toml +++ b/crates/client-api/Cargo.toml @@ -8,7 +8,9 @@ rust-version.workspace = true [dependencies] spacetimedb-client-api-messages.workspace = true +spacetimedb-core.workspace = true spacetimedb-data-structures.workspace = true +spacetimedb-datastore.workspace = true spacetimedb-lib = { workspace = true, features = ["serde"] } spacetimedb-paths.workspace = true spacetimedb-schema.workspace = true diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index ddc5a712b0e..34f51b1b5a0 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -107,7 +107,6 @@ faststr = "0.2.23" core_affinity = "0.8" [target.'cfg(not(target_env = "msvc"))'.dependencies] -tikv-jemallocator = {workspace = true} tikv-jemalloc-ctl = {workspace = true} [target.'cfg(target_os = "linux")'.dependencies] @@ -136,16 +135,11 @@ spacetimedb-commitlog = { path = "../commitlog", features = ["test"] } spacetimedb-datastore = { path = "../datastore/", features = ["test"] } spacetimedb-engine = { workspace = true, features = ["test"] } -criterion.workspace = true # Also as dev-dependencies for use in _this_ crate's tests. proptest.workspace = true -proptest-derive.workspace = true -rand.workspace = true -env_logger.workspace = true pretty_assertions.workspace = true jsonwebtoken.workspace = true axum.workspace = true -fs_extra.workspace = true [lints] workspace = true diff --git a/crates/durability/Cargo.toml b/crates/durability/Cargo.toml index c1285a752fe..48784c3f44a 100644 --- a/crates/durability/Cargo.toml +++ b/crates/durability/Cargo.toml @@ -12,7 +12,6 @@ test = [] fallocate = ["spacetimedb-commitlog/fallocate"] [dependencies] -anyhow.workspace = true async-channel.workspace = true futures.workspace = true itertools.workspace = true @@ -27,6 +26,7 @@ tokio.workspace = true tracing.workspace = true [dev-dependencies] +anyhow.workspace = true spacetimedb-commitlog = { workspace = true, features = ["test"] } tempfile.workspace = true tokio.workspace = true diff --git a/crates/lib/Cargo.toml b/crates/lib/Cargo.toml index 06dbd88c97c..47dfcb5566b 100644 --- a/crates/lib/Cargo.toml +++ b/crates/lib/Cargo.toml @@ -58,9 +58,6 @@ ron.workspace = true proptest.workspace = true proptest-derive.workspace = true -[package.metadata.cargo-machete] -# Used through the `proptest` feature's `dep:proptest-derive` entry. -ignored = ["proptest-derive"] [lints] workspace = true diff --git a/crates/smoketests/Cargo.toml b/crates/smoketests/Cargo.toml index 1ae168c1480..90ad676634d 100644 --- a/crates/smoketests/Cargo.toml +++ b/crates/smoketests/Cargo.toml @@ -18,7 +18,6 @@ which = "8.0.0" [dev-dependencies] cargo_metadata.workspace = true -assert_cmd = "2" predicates = "3" socket2.workspace = true tokio.workspace = true diff --git a/crates/sqltest/Cargo.toml b/crates/sqltest/Cargo.toml index 8500b2f4062..a5aef2344da 100644 --- a/crates/sqltest/Cargo.toml +++ b/crates/sqltest/Cargo.toml @@ -7,6 +7,7 @@ license-file = "LICENSE" publish = false [dependencies] +spacetimedb-core = { workspace = true, features = ["test"] } spacetimedb-lib.workspace = true spacetimedb-sats.workspace = true diff --git a/crates/standalone/Cargo.toml b/crates/standalone/Cargo.toml index c3904619d91..624f98a96c9 100644 --- a/crates/standalone/Cargo.toml +++ b/crates/standalone/Cargo.toml @@ -54,15 +54,10 @@ toml.workspace = true [target.'cfg(not(target_env = "msvc"))'.dependencies] tikv-jemallocator = {workspace = true} -tikv-jemalloc-ctl = {workspace = true} [dev-dependencies] once_cell.workspace = true tempfile.workspace = true -[package.metadata.cargo-machete] -# Used through feature forwarding for standalone-only core features. -ignored = ["spacetimedb-core"] - [lints] workspace = true diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index c45b60a6b5c..fb453b76dce 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -15,13 +15,13 @@ spacetimedb-lib.workspace = true spacetimedb-standalone.workspace = true spacetimedb-client-api.workspace = true spacetimedb-client-api-messages.workspace = true +spacetimedb-core.workspace = true spacetimedb-guard.workspace = true spacetimedb-paths.workspace = true spacetimedb-schema.workspace = true anyhow.workspace = true bytes.workspace = true -env_logger.workspace = true log.workspace = true clap.workspace = true serde_json.workspace = true @@ -34,6 +34,7 @@ serde.workspace = true futures.workspace = true [dev-dependencies] +env_logger.workspace = true serial_test.workspace = true [lints] diff --git a/sdks/rust/tests/connect_disconnect_client/Cargo.toml b/sdks/rust/tests/connect_disconnect_client/Cargo.toml index 26a25e0d3a3..3783a7d91e1 100644 --- a/sdks/rust/tests/connect_disconnect_client/Cargo.toml +++ b/sdks/rust/tests/connect_disconnect_client/Cargo.toml @@ -42,9 +42,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/event-table-client/Cargo.toml b/sdks/rust/tests/event-table-client/Cargo.toml index 86545ca44fd..fb09cd8f340 100644 --- a/sdks/rust/tests/event-table-client/Cargo.toml +++ b/sdks/rust/tests/event-table-client/Cargo.toml @@ -41,9 +41,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml index fb86336c40f..15ed09983dc 100644 --- a/sdks/rust/tests/procedural-view-pk-client/Cargo.toml +++ b/sdks/rust/tests/procedural-view-pk-client/Cargo.toml @@ -38,9 +38,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entry. -ignored = ["wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/procedure-client/Cargo.toml b/sdks/rust/tests/procedure-client/Cargo.toml index f7d41cb4b0a..68324cb7fe0 100644 --- a/sdks/rust/tests/procedure-client/Cargo.toml +++ b/sdks/rust/tests/procedure-client/Cargo.toml @@ -46,9 +46,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml index 78a2fe9fe45..229aed8ab74 100644 --- a/sdks/rust/tests/procedure-concurrency-client/Cargo.toml +++ b/sdks/rust/tests/procedure-concurrency-client/Cargo.toml @@ -40,9 +40,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/test-client/Cargo.toml b/sdks/rust/tests/test-client/Cargo.toml index c40c83d3c86..f25152f5994 100644 --- a/sdks/rust/tests/test-client/Cargo.toml +++ b/sdks/rust/tests/test-client/Cargo.toml @@ -50,9 +50,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "gloo-timers", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/view-client/Cargo.toml b/sdks/rust/tests/view-client/Cargo.toml index be29ed953f1..8ca8e835720 100644 --- a/sdks/rust/tests/view-client/Cargo.toml +++ b/sdks/rust/tests/view-client/Cargo.toml @@ -45,9 +45,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/sdks/rust/tests/view-pk-client/Cargo.toml b/sdks/rust/tests/view-pk-client/Cargo.toml index 30d0ff4b1b4..553ef27738a 100644 --- a/sdks/rust/tests/view-pk-client/Cargo.toml +++ b/sdks/rust/tests/view-pk-client/Cargo.toml @@ -42,9 +42,6 @@ wasm-bindgen = { version = "0.2.100", optional = true } wasm-bindgen-futures = { version = "0.4.45", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true } -[package.metadata.cargo-machete] -# Used through the `browser` feature's `dep:` entries. -ignored = ["futures", "wasm-bindgen-futures"] [lints] workspace = true diff --git a/tools/ci/commands/dep-check/src/main.rs b/tools/ci/commands/dep-check/src/main.rs index f75ccc4c097..25f2c12197f 100644 --- a/tools/ci/commands/dep-check/src/main.rs +++ b/tools/ci/commands/dep-check/src/main.rs @@ -1,9 +1,12 @@ use anyhow::Result; use ci_common::ensure_repo_root; use duct::cmd; +use std::ffi::OsString; fn main() -> Result<()> { ensure_repo_root()?; - cmd("cargo-machete", std::env::args_os().skip(1)).run()?; + let mut args = vec![OsString::from("+1.95.0"), OsString::from("shear")]; + args.extend(std::env::args_os().skip(1)); + cmd("cargo", args).run()?; Ok(()) } From fca11e67d223a795bdd51a8ec636d4f46ce643ab Mon Sep 17 00:00:00 2001 From: Zeke Foppa Date: Fri, 14 Aug 2026 11:16:25 -0700 Subject: [PATCH 37/37] [bot/cargo-ci-dep-check]: use shear --- sdks/rust/Cargo.toml | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sdks/rust/Cargo.toml b/sdks/rust/Cargo.toml index 1dd1edf16cb..f8a2a1fe045 100644 --- a/sdks/rust/Cargo.toml +++ b/sdks/rust/Cargo.toml @@ -43,7 +43,6 @@ http.workspace = true log.workspace = true once_cell.workspace = true prometheus.workspace = true -rand.workspace = true getrandom = { version = "0.3.4", features = ["wasm_js"], optional = true } gloo-console = { version = "0.3.0", optional = true } @@ -65,19 +64,15 @@ tokio-tungstenite.workspace = true native-tls = "=0.2.14" [dev-dependencies] -# for quickstart-chat and cursive-chat examples -hex.workspace = true -# for cursive-chat example -cursive.workspace = true futures-channel.workspace = true shlex = "1.3.0" # for tests spacetimedb-testing = { path = "../../crates/testing" } -[package.metadata.cargo-machete] -# Used through the `wasm` feature's `dep:getrandom` entry. -ignored = ["getrandom"] - [lints] workspace = true + +[package.metadata.cargo-shear] +# This intentionally pins a transitive dependency to a Rust-compatible release. +ignored = ["native-tls"]