11use anyhow:: { bail, Result } ;
22use clap:: { CommandFactory , Parser , Subcommand } ;
33use duct:: cmd;
4- use std:: collections:: HashMap ;
54use std:: path:: Path ;
65use std:: { env, fs} ;
76
@@ -93,15 +92,6 @@ enum CiCmd {
9392 } ,
9493}
9594
96- macro_rules! bash {
97- ( $cmdline: expr) => {
98- run_bash( $cmdline, & Vec :: new( ) )
99- } ;
100- ( $cmdline: expr, $envs: expr) => {
101- run_bash( $cmdline, $envs)
102- } ;
103- }
104-
10595fn run_all_clap_subcommands ( skips : & [ String ] ) -> Result < ( ) > {
10696 let subcmds = Cli :: command ( )
10797 . get_subcommands ( )
@@ -114,98 +104,164 @@ fn run_all_clap_subcommands(skips: &[String]) -> Result<()> {
114104 continue ;
115105 }
116106 log:: info!( "executing cargo ci {subcmd}" ) ;
117- bash ! ( & format! ( "cargo ci { subcmd}" ) ) ?;
107+ cmd ! ( "cargo" , "ci" , & subcmd) . run ( ) ?;
118108 }
119-
120109 Ok ( ( ) )
121110}
122111
123- fn run_bash ( cmdline : & str , additional_env : & [ ( & str , & str ) ] ) -> Result < ( ) > {
124- let mut env = env:: vars ( ) . collect :: < HashMap < _ , _ > > ( ) ;
125- env. extend ( additional_env. iter ( ) . map ( |( k, v) | ( k. to_string ( ) , v. to_string ( ) ) ) ) ;
126- log:: debug!( "$ {cmdline}" ) ;
127- let status = cmd ! ( "bash" , "-lc" , cmdline) . full_env ( env) . run ( ) ?;
128- if !status. status . success ( ) {
129- let e = anyhow:: anyhow!( "command failed: {cmdline}" ) ;
130- log:: error!( "{e}" ) ;
131- return Err ( e) ;
112+ fn infer_python ( ) -> String {
113+ let py3_available = cmd ! ( "python3" , "--version" ) . run ( ) . is_ok ( ) ;
114+ if py3_available {
115+ "python3" . to_string ( )
116+ } else {
117+ "python" . to_string ( )
132118 }
133- Ok ( ( ) )
134119}
135120
136121fn main ( ) -> Result < ( ) > {
122+ env_logger:: init ( ) ;
123+
137124 let cli = Cli :: parse ( ) ;
138125
139126 match cli. cmd {
140127 Some ( CiCmd :: Test ) => {
141- bash ! ( "cargo test --all -- --skip unreal" ) ?;
128+ // TODO: This doesn't work on at least user Linux machines, because something here apparently uses `sudo`?
129+
130+ cmd ! ( "cargo" , "test" , "--all" , "--" , "--skip" , "unreal" ) . run ( ) ?;
131+ // TODO: This should check for a diff at the start. If there is one, we should alert the user
132+ // that we're disabling diff checks because they have a dirty git repo, and to re-run in a clean one
133+ // if they want those checks.
134+
142135 // The fallocate tests have been flakely when running in parallel
143- bash ! ( "cargo test -p spacetimedb-durability --features fallocate -- --test-threads=1" ) ?;
144- bash ! ( "bash tools/check-diff.sh" ) ?;
145- bash ! ( "cargo run -p spacetimedb-codegen --example regen-csharp-moduledef && bash tools/check-diff.sh crates/bindings-csharp" ) ?;
146- bash ! ( "(cd crates/bindings-csharp && dotnet test -warnaserror)" ) ?;
136+ cmd ! (
137+ "cargo" ,
138+ "test" ,
139+ "-p" ,
140+ "spacetimedb-durability" ,
141+ "--features" ,
142+ "fallocate" ,
143+ "--" ,
144+ "--test-threads=1" ,
145+ )
146+ . run ( ) ?;
147+ cmd ! ( "bash" , "tools/check-diff.sh" ) . run ( ) ?;
148+ cmd ! (
149+ "cargo" ,
150+ "run" ,
151+ "-p" ,
152+ "spacetimedb-codegen" ,
153+ "--example" ,
154+ "regen-csharp-moduledef" ,
155+ )
156+ . run ( ) ?;
157+ cmd ! ( "bash" , "tools/check-diff.sh" , "crates/bindings-csharp" ) . run ( ) ?;
158+ cmd ! ( "dotnet" , "test" , "-warnaserror" )
159+ . dir ( "crates/bindings-csharp" )
160+ . run ( ) ?;
147161 }
148162
149163 Some ( CiCmd :: Lint ) => {
150- bash ! ( "cargo fmt --all -- --check" ) ?;
151- bash ! ( "cargo clippy --all --tests --benches -- -D warnings" ) ?;
152- bash ! ( "(cd crates/bindings-csharp && dotnet tool restore && dotnet csharpier --check .)" ) ?;
164+ cmd ! ( "cargo" , "fmt" , "--all" , "--" , "--check" ) . run ( ) ?;
165+ cmd ! (
166+ "cargo" ,
167+ "clippy" ,
168+ "--all" ,
169+ "--tests" ,
170+ "--benches" ,
171+ "--" ,
172+ "-D" ,
173+ "warnings" ,
174+ )
175+ . run ( ) ?;
176+ cmd ! ( "dotnet" , "tool" , "restore" ) . dir ( "crates/bindings-csharp" ) . run ( ) ?;
177+ cmd ! ( "dotnet" , "csharpier" , "--check" , "." )
178+ . dir ( "crates/bindings-csharp" )
179+ . run ( ) ?;
153180 // `bindings` is the only crate we care strongly about documenting,
154181 // since we link to its docs.rs from our website.
155182 // We won't pass `--no-deps`, though,
156183 // since we want everything reachable through it to also work.
157184 // This includes `sats` and `lib`.
158- bash ! (
159- "cd crates/bindings && cargo doc" ,
185+ cmd ! ( "cargo" , "doc" )
186+ . dir ( " crates/bindings" )
160187 // Make `cargo doc` exit with error on warnings, most notably broken links
161- & [ ( "RUSTDOCFLAGS" , "--deny warnings" ) ]
162- ) ?;
188+ . env ( "RUSTDOCFLAGS" , "--deny warnings" )
189+ . run ( ) ?;
163190 }
164191
165192 Some ( CiCmd :: WasmBindings ) => {
166- bash ! ( "cargo test -p spacetimedb-codegen" ) ?;
193+ cmd ! ( "cargo" , " test" , "-p" , " spacetimedb-codegen") . run ( ) ?;
167194 // Make sure the `Cargo.lock` file reflects the latest available versions.
168195 // This is what users would end up with on a fresh module, so we want to
169196 // catch any compile errors arising from a different transitive closure
170197 // of dependencies than what is in the workspace lock file.
171198 //
172199 // For context see also: https://github.com/clockworklabs/SpacetimeDB/pull/2714
173- bash ! ( "cargo update" ) ?;
174- bash ! ( "cargo run -p spacetimedb-cli -- build --project-path modules/module-test" ) ?;
200+ cmd ! ( "cargo" , "update" ) . run ( ) ?;
201+ cmd ! (
202+ "cargo" ,
203+ "run" ,
204+ "-p" ,
205+ "spacetimedb-cli" ,
206+ "--" ,
207+ "build" ,
208+ "--project-path" ,
209+ "modules/module-test" ,
210+ )
211+ . run ( ) ?;
175212 }
176213
177- Some ( CiCmd :: Smoketests { args } ) => {
178- // On some systems, there is no `python`, but there is `python3`.
179- let py3_available = bash ! ( "command -v python3 >/dev/null 2>&1" ) . is_ok ( ) ;
180- let python = if py3_available { "python3" } else { "python" } ;
181- bash ! ( & format!( "{python} -m smoketests {}" , args. join( " " ) ) ) ?;
214+ Some ( CiCmd :: Smoketests { args : smoketest_args } ) => {
215+ let python = infer_python ( ) ;
216+ cmd (
217+ python,
218+ [ "-m" , "smoketests" ]
219+ . into_iter ( )
220+ . map ( |s| s. to_string ( ) )
221+ . chain ( smoketest_args) ,
222+ )
223+ . run ( ) ?;
182224 }
183225
184226 Some ( CiCmd :: UpdateFlow {
185227 target,
186228 github_token_auth,
187229 } ) => {
188- let target = target. map ( |t| format ! ( "--target {t}" ) ) . unwrap_or_default ( ) ;
189- let github_token_auth_flag = if github_token_auth {
190- "--features github-token-auth "
230+ let mut common_args = vec ! [ ] ;
231+ if let Some ( target) = target. as_ref ( ) {
232+ common_args. push ( "--target" ) ;
233+ common_args. push ( target) ;
234+ log:: info!( "checking update flow for target: {target}" ) ;
191235 } else {
192- ""
193- } ;
236+ log:: info!( "checking update flow" ) ;
237+ }
238+ if github_token_auth {
239+ common_args. push ( "--features" ) ;
240+ common_args. push ( "github-token-auth" ) ;
241+ }
194242
195- bash ! ( & format!( "echo 'checking update flow for target: {target}'" ) ) ?;
196- bash ! ( & format!(
197- "cargo build {github_token_auth_flag}{target} -p spacetimedb-update"
198- ) ) ?;
243+ cmd (
244+ "cargo" ,
245+ [ "build" , "-p" , "spacetimedb-update" ]
246+ . into_iter ( )
247+ . chain ( common_args. clone ( ) ) ,
248+ )
249+ . run ( ) ?;
199250 // NOTE(bfops): We need the `github-token-auth` feature because we otherwise tend to get ratelimited when we try to fetch `/releases/latest`.
200251 // 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
201252 // happens very frequently on the `macos-runner`, but we haven't seen it on any others).
202- bash ! ( & format!(
203- r#"
204- ROOT_DIR="$(mktemp -d)"
205- cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install --root-dir="${{ROOT_DIR}}" --yes
206- "${{ROOT_DIR}}"/spacetime --root-dir="${{ROOT_DIR}}" help
207- "#
208- ) ) ?;
253+ let root_dir = tempfile:: tempdir ( ) ?;
254+ let root_dir_string = root_dir. path ( ) . to_string_lossy ( ) . to_string ( ) ;
255+ let root_arg = format ! ( "--root-dir={}" , root_dir_string) ;
256+ cmd (
257+ "cargo" ,
258+ [ "run" , "-p" , "spacetimedb-update" ]
259+ . into_iter ( )
260+ . chain ( common_args. clone ( ) )
261+ . chain ( [ "--" , "self-install" , & root_arg, "--yes" ] . into_iter ( ) ) ,
262+ )
263+ . run ( ) ?;
264+ cmd ! ( format!( "{}/spacetime" , root_dir_string) , & root_arg, "help" , ) . run ( ) ?;
209265 }
210266
211267 Some ( CiCmd :: CliDocs { spacetime_path } ) => {
@@ -220,19 +276,14 @@ cargo run {github_token_auth_flag}{target} -p spacetimedb-update -- self-install
220276 ) ;
221277 }
222278
223- bash ! ( "cd docs && pnpm generate-cli-docs" ) ?;
224- bash ! (
225- r#"
226- if [ -z "$(git status --porcelain)" ]; then
227- echo "No docs changes detected"
228- else
229- echo "It looks like the CLI docs have changed:"
230- exit 1
231- fi
232- "#
233- ) ?;
234- bash ! ( "git status" ) ?;
235- bash ! ( "git diff" ) ?;
279+ cmd ! ( "pnpm" , "install" , "--recursive" ) . run ( ) ?;
280+ cmd ! ( "pnpm" , "generate-cli-docs" ) . dir ( "docs" ) . run ( ) ?;
281+ let out = cmd ! ( "git" , "status" , "--porcelain" , "--" , "docs" ) . read ( ) ?;
282+ if out. is_empty ( ) {
283+ log:: info!( "No docs changes detected" ) ;
284+ } else {
285+ anyhow:: bail!( "CLI docs are out of date:\n {out}" ) ;
286+ }
236287 }
237288
238289 Some ( CiCmd :: SelfDocs { check } ) => {
0 commit comments