Skip to content

Commit a8e511a

Browse files
committed
[bfops/parallel-smoketests]: updates
1 parent 0e08c64 commit a8e511a

File tree

3 files changed

+115
-15
lines changed

3 files changed

+115
-15
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ jobs:
8989

9090
- name: Build crates
9191
run: cargo build -p spacetimedb-cli -p spacetimedb-standalone -p spacetimedb-update
92+
# TODO: This should be helpful on any platform.. but on Windows, `cargo ci` still ends up doing
93+
# a bunch of rebuilding when it runs `cargo build` internally.
94+
if: runner.os == 'Linux'
9295
- name: Find spacetime binary
9396
if: runner.os == 'Linux'
9497
run: |

smoketests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ def publish_module(self, domain=None, *, clear=True, capture_stderr=True, num_re
258258
def reset_config(cls):
259259
if not STDB_CONFIG:
260260
raise Exception("config toml has not been initialized yet")
261+
print('Writing to %s:\n%s' % (cls.config_path, STDB_CONFIG))
261262
cls.config_path.write_text(STDB_CONFIG)
262263

263264
def fingerprint(self):

tools/ci/src/main.rs

Lines changed: 111 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use duct::cmd;
44
use log::warn;
55
use serde_json;
66
use std::collections::HashSet;
7+
use std::io::{BufRead, BufReader};
78
use std::net::TcpListener;
89
use std::path::{Path, PathBuf};
9-
use std::sync::Mutex;
10+
use std::process::{Command, Stdio};
11+
use std::sync::{Arc, Mutex};
1012
use std::thread;
1113
use std::time::{Duration, Instant};
1214
use std::{env, fs};
@@ -239,6 +241,7 @@ pub enum ServerState {
239241
handle: thread::JoinHandle<()>,
240242
compose_file: PathBuf,
241243
project: String,
244+
logs: Option<Arc<Mutex<String>>>,
242245
},
243246
}
244247

@@ -247,6 +250,16 @@ impl ServerState {
247250
Self::start_with_output(start_mode, args, None)
248251
}
249252

253+
fn drain_logs(&self) -> String {
254+
match self {
255+
ServerState::Docker { logs: Some(logs), .. } => {
256+
let mut guard = logs.lock().expect("logs lock poisoned");
257+
std::mem::take(&mut *guard)
258+
}
259+
_ => String::new(),
260+
}
261+
}
262+
250263
fn start_with_output(start_mode: StartServer, args: &mut Vec<String>, output: Option<&mut String>) -> Result<Self> {
251264
// TODO: Currently the server output leaks. We should be capturing it and only printing if the test fails.
252265

@@ -266,31 +279,110 @@ impl ServerState {
266279
let server_url = format!("http://localhost:{server_port}");
267280
args.push(server_url.clone());
268281
let compose_str = compose_file.to_string_lossy().to_string();
282+
let logs: Option<Arc<Mutex<String>>> = output.as_ref().map(|_| Arc::new(Mutex::new(String::new())));
269283

270284
let handle = thread::spawn({
271285
let project = project.clone();
286+
let logs = logs.clone();
272287
move || {
273-
let _ = cmd!(
274-
"docker",
275-
"compose",
276-
"-f",
277-
&compose_str,
278-
"--project-name",
279-
&project,
280-
"up",
281-
"--abort-on-container-exit",
282-
)
283-
.env("STDB_PORT", server_port.to_string())
284-
.env("STDB_PG_PORT", pg_port.to_string())
285-
.env("STDB_TRACY_PORT", tracy_port.to_string())
286-
.run();
288+
if let Some(logs) = logs {
289+
let mut child = match Command::new("docker")
290+
.args([
291+
"compose",
292+
"-f",
293+
&compose_str,
294+
"--project-name",
295+
&project,
296+
"up",
297+
"--abort-on-container-exit",
298+
])
299+
.env("STDB_PORT", server_port.to_string())
300+
.env("STDB_PG_PORT", pg_port.to_string())
301+
.env("STDB_TRACY_PORT", tracy_port.to_string())
302+
.stdout(Stdio::piped())
303+
.stderr(Stdio::piped())
304+
.spawn()
305+
{
306+
Ok(child) => child,
307+
Err(e) => {
308+
let mut guard = logs.lock().expect("logs lock poisoned");
309+
guard.push_str(&format!("failed to spawn docker compose: {e}\n"));
310+
return;
311+
}
312+
};
313+
314+
let stdout = child.stdout.take();
315+
let stderr = child.stderr.take();
316+
317+
let stdout_handle = stdout.map(|stdout| {
318+
let logs = logs.clone();
319+
thread::spawn(move || {
320+
let mut reader = BufReader::new(stdout);
321+
let mut line = String::new();
322+
loop {
323+
line.clear();
324+
match reader.read_line(&mut line) {
325+
Ok(0) => break,
326+
Ok(_) => {
327+
let mut guard = logs.lock().expect("logs lock poisoned");
328+
guard.push_str(&line);
329+
}
330+
Err(_) => break,
331+
}
332+
}
333+
})
334+
});
335+
336+
let stderr_handle = stderr.map(|stderr| {
337+
let logs = logs.clone();
338+
thread::spawn(move || {
339+
let mut reader = BufReader::new(stderr);
340+
let mut line = String::new();
341+
loop {
342+
line.clear();
343+
match reader.read_line(&mut line) {
344+
Ok(0) => break,
345+
Ok(_) => {
346+
let mut guard = logs.lock().expect("logs lock poisoned");
347+
guard.push_str(&line);
348+
}
349+
Err(_) => break,
350+
}
351+
}
352+
})
353+
});
354+
355+
let _ = child.wait();
356+
if let Some(h) = stdout_handle {
357+
let _ = h.join();
358+
}
359+
if let Some(h) = stderr_handle {
360+
let _ = h.join();
361+
}
362+
} else {
363+
let _ = cmd!(
364+
"docker",
365+
"compose",
366+
"-f",
367+
&compose_str,
368+
"--project-name",
369+
&project,
370+
"up",
371+
"--abort-on-container-exit",
372+
)
373+
.env("STDB_PORT", server_port.to_string())
374+
.env("STDB_PG_PORT", pg_port.to_string())
375+
.env("STDB_TRACY_PORT", tracy_port.to_string())
376+
.run();
377+
}
287378
}
288379
});
289380
wait_until_http_ready(Duration::from_secs(900), &server_url)?;
290381
Ok(ServerState::Docker {
291382
handle,
292383
compose_file,
293384
project,
385+
logs,
294386
})
295387
}
296388
StartServer::Yes => {
@@ -426,12 +518,15 @@ fn run_smoketests_batch_captured(server_mode: StartServer, args: &[String], pyth
426518
}
427519

428520
if !res.status.success() {
521+
output.push_str(&_server.drain_logs());
429522
return (
430523
output,
431524
Err(anyhow::anyhow!("smoketests exited with status: {}", res.status)),
432525
);
433526
}
434527

528+
output.push_str(&_server.drain_logs());
529+
435530
(output, Ok(()))
436531
}
437532

@@ -806,6 +901,7 @@ fn main() -> Result<()> {
806901
}) => {
807902
let start_server = server_start_config(start_server, docker.clone());
808903
if matches!(start_server, StartServer::Yes { .. }) {
904+
// TODO: This seems to rebuild a ton, even if we recently ran cargo build. Figure out why..
809905
println!("Building SpacetimeDB..");
810906

811907
// Pre-build so that `cargo run -p spacetimedb-cli` will immediately start. Otherwise we risk timing out waiting for the server to come up.

0 commit comments

Comments
 (0)