Allow passing number of thread for building and testing to CI (#19359)

# Objective

Fixes #16051
Closes #16145

## Solution

Allow passing `--build-jobs` and `--test-threads` to `ci`
i.e.
```
cargo run -p ci -- --build-jobs 4 --test-threads 4
```

## Testing

running ci locally

---------

Co-authored-by: Benjamin Brienen <Benjamin.Brienen@outlook.com>
This commit is contained in:
Lucas Franca 2025-06-16 18:19:47 -03:00 committed by GitHub
parent f7e112a3c9
commit c8cb7bdf57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 181 additions and 125 deletions

View File

@ -8,7 +8,6 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
argh = "0.1" argh = "0.1"
xshell = "0.2" xshell = "0.2"
bitflags = "2.3"
[lints] [lints]
workspace = true workspace = true

37
tools/ci/src/args.rs Normal file
View File

@ -0,0 +1,37 @@
use crate::CI;
/// Arguments that are available to CI commands.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Args {
keep_going: bool,
test_threads: Option<usize>,
build_jobs: Option<usize>,
}
impl Args {
#[inline(always)]
pub fn keep_going(&self) -> Option<&'static str> {
self.keep_going.then_some("--no-fail-fast")
}
#[inline(always)]
pub fn build_jobs(&self) -> Option<String> {
self.build_jobs.map(|jobs| format!("--jobs={jobs}"))
}
#[inline(always)]
pub fn test_threads(&self) -> Option<String> {
self.test_threads
.map(|threads| format!("--test-threads={threads}"))
}
}
impl From<&CI> for Args {
fn from(value: &CI) -> Self {
Args {
keep_going: value.keep_going,
test_threads: value.test_threads,
build_jobs: value.build_jobs,
}
}
}

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
args::Args,
commands, commands,
prepare::{Flag, Prepare, PreparedCommand}, prepare::{Prepare, PreparedCommand},
}; };
use argh::FromArgs; use argh::FromArgs;
@ -12,7 +13,15 @@ pub struct CI {
/// continue running commands even if one fails /// continue running commands even if one fails
#[argh(switch)] #[argh(switch)]
keep_going: bool, pub(crate) keep_going: bool,
/// parallelism of `cargo test`
#[argh(option)]
pub(crate) test_threads: Option<usize>,
/// number of build jobs
#[argh(option)]
pub(crate) build_jobs: Option<usize>,
} }
impl CI { impl CI {
@ -22,7 +31,6 @@ impl CI {
/// This is usually related to differing toolchains and configuration. /// This is usually related to differing toolchains and configuration.
pub fn run(self) { pub fn run(self) {
let sh = xshell::Shell::new().unwrap(); let sh = xshell::Shell::new().unwrap();
let prepared_commands = self.prepare(&sh); let prepared_commands = self.prepare(&sh);
let mut failures = vec![]; let mut failures = vec![];
@ -59,34 +67,30 @@ impl CI {
} }
fn prepare<'a>(&self, sh: &'a xshell::Shell) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell) -> Vec<PreparedCommand<'a>> {
let mut flags = Flag::empty(); let args = self.into();
if self.keep_going {
flags |= Flag::KEEP_GOING;
}
match &self.command { match &self.command {
Some(command) => command.prepare(sh, flags), Some(command) => command.prepare(sh, args),
None => { None => {
// Note that we are running the subcommands directly rather than using any aliases // Note that we are running the subcommands directly rather than using any aliases
let mut cmds = vec![]; let mut cmds = vec![];
cmds.append(&mut commands::FormatCommand::default().prepare(sh, flags)); cmds.append(&mut commands::FormatCommand::default().prepare(sh, args));
cmds.append(&mut commands::ClippyCommand::default().prepare(sh, flags)); cmds.append(&mut commands::ClippyCommand::default().prepare(sh, args));
cmds.append(&mut commands::TestCommand::default().prepare(sh, flags)); cmds.append(&mut commands::TestCommand::default().prepare(sh, args));
cmds.append(&mut commands::TestCheckCommand::default().prepare(sh, flags)); cmds.append(&mut commands::TestCheckCommand::default().prepare(sh, args));
cmds.append(&mut commands::IntegrationTestCommand::default().prepare(sh, flags)); cmds.append(&mut commands::IntegrationTestCommand::default().prepare(sh, args));
cmds.append( cmds.append(
&mut commands::IntegrationTestCheckCommand::default().prepare(sh, flags), &mut commands::IntegrationTestCheckCommand::default().prepare(sh, args),
); );
cmds.append( cmds.append(
&mut commands::IntegrationTestCleanCommand::default().prepare(sh, flags), &mut commands::IntegrationTestCleanCommand::default().prepare(sh, args),
); );
cmds.append(&mut commands::DocCheckCommand::default().prepare(sh, flags)); cmds.append(&mut commands::DocCheckCommand::default().prepare(sh, args));
cmds.append(&mut commands::DocTestCommand::default().prepare(sh, flags)); cmds.append(&mut commands::DocTestCommand::default().prepare(sh, args));
cmds.append(&mut commands::CompileCheckCommand::default().prepare(sh, flags)); cmds.append(&mut commands::CompileCheckCommand::default().prepare(sh, args));
cmds.append(&mut commands::CompileFailCommand::default().prepare(sh, flags)); cmds.append(&mut commands::CompileFailCommand::default().prepare(sh, args));
cmds.append(&mut commands::BenchCheckCommand::default().prepare(sh, flags)); cmds.append(&mut commands::BenchCheckCommand::default().prepare(sh, args));
cmds.append(&mut commands::ExampleCheckCommand::default().prepare(sh, flags)); cmds.append(&mut commands::ExampleCheckCommand::default().prepare(sh, args));
cmds cmds
} }
} }
@ -118,25 +122,25 @@ enum Commands {
} }
impl Prepare for Commands { impl Prepare for Commands {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
match self { match self {
Commands::Lints(subcommand) => subcommand.prepare(sh, flags), Commands::Lints(subcommand) => subcommand.prepare(sh, args),
Commands::Doc(subcommand) => subcommand.prepare(sh, flags), Commands::Doc(subcommand) => subcommand.prepare(sh, args),
Commands::Compile(subcommand) => subcommand.prepare(sh, flags), Commands::Compile(subcommand) => subcommand.prepare(sh, args),
Commands::Format(subcommand) => subcommand.prepare(sh, flags), Commands::Format(subcommand) => subcommand.prepare(sh, args),
Commands::Clippy(subcommand) => subcommand.prepare(sh, flags), Commands::Clippy(subcommand) => subcommand.prepare(sh, args),
Commands::Test(subcommand) => subcommand.prepare(sh, flags), Commands::Test(subcommand) => subcommand.prepare(sh, args),
Commands::TestCheck(subcommand) => subcommand.prepare(sh, flags), Commands::TestCheck(subcommand) => subcommand.prepare(sh, args),
Commands::IntegrationTest(subcommand) => subcommand.prepare(sh, flags), Commands::IntegrationTest(subcommand) => subcommand.prepare(sh, args),
Commands::IntegrationTestCheck(subcommand) => subcommand.prepare(sh, flags), Commands::IntegrationTestCheck(subcommand) => subcommand.prepare(sh, args),
Commands::IntegrationTestClean(subcommand) => subcommand.prepare(sh, flags), Commands::IntegrationTestClean(subcommand) => subcommand.prepare(sh, args),
Commands::DocCheck(subcommand) => subcommand.prepare(sh, flags), Commands::DocCheck(subcommand) => subcommand.prepare(sh, args),
Commands::DocTest(subcommand) => subcommand.prepare(sh, flags), Commands::DocTest(subcommand) => subcommand.prepare(sh, args),
Commands::CompileCheck(subcommand) => subcommand.prepare(sh, flags), Commands::CompileCheck(subcommand) => subcommand.prepare(sh, args),
Commands::CompileFail(subcommand) => subcommand.prepare(sh, flags), Commands::CompileFail(subcommand) => subcommand.prepare(sh, args),
Commands::BenchCheck(subcommand) => subcommand.prepare(sh, flags), Commands::BenchCheck(subcommand) => subcommand.prepare(sh, args),
Commands::ExampleCheck(subcommand) => subcommand.prepare(sh, flags), Commands::ExampleCheck(subcommand) => subcommand.prepare(sh, args),
} }
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,11 +8,13 @@ use xshell::cmd;
pub struct BenchCheckCommand {} pub struct BenchCheckCommand {}
impl Prepare for BenchCheckCommand { impl Prepare for BenchCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!( cmd!(
sh, sh,
"cargo check --benches --target-dir ../target --manifest-path ./benches/Cargo.toml" "cargo check --benches {jobs...} --target-dir ../target --manifest-path ./benches/Cargo.toml"
), ),
"Failed to check the benches.", "Failed to check the benches.",
)] )]

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,11 +8,13 @@ use xshell::cmd;
pub struct ClippyCommand {} pub struct ClippyCommand {}
impl Prepare for ClippyCommand { impl Prepare for ClippyCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!( cmd!(
sh, sh,
"cargo clippy --workspace --all-targets --all-features -- -Dwarnings" "cargo clippy --workspace --all-targets --all-features {jobs...} -- -Dwarnings"
), ),
"Please fix clippy errors in output above.", "Please fix clippy errors in output above.",
)] )]

View File

@ -1,9 +1,10 @@
use crate::{ use crate::{
args::Args,
commands::{ commands::{
BenchCheckCommand, CompileCheckCommand, CompileFailCommand, ExampleCheckCommand, BenchCheckCommand, CompileCheckCommand, CompileFailCommand, ExampleCheckCommand,
IntegrationTestCheckCommand, TestCheckCommand, IntegrationTestCheckCommand, TestCheckCommand,
}, },
Flag, Prepare, PreparedCommand, Prepare, PreparedCommand,
}; };
use argh::FromArgs; use argh::FromArgs;
@ -13,14 +14,14 @@ use argh::FromArgs;
pub struct CompileCommand {} pub struct CompileCommand {}
impl Prepare for CompileCommand { impl Prepare for CompileCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let mut commands = vec![]; let mut commands = vec![];
commands.append(&mut CompileFailCommand::default().prepare(sh, flags)); commands.append(&mut CompileFailCommand::default().prepare(sh, args));
commands.append(&mut BenchCheckCommand::default().prepare(sh, flags)); commands.append(&mut BenchCheckCommand::default().prepare(sh, args));
commands.append(&mut ExampleCheckCommand::default().prepare(sh, flags)); commands.append(&mut ExampleCheckCommand::default().prepare(sh, args));
commands.append(&mut CompileCheckCommand::default().prepare(sh, flags)); commands.append(&mut CompileCheckCommand::default().prepare(sh, args));
commands.append(&mut TestCheckCommand::default().prepare(sh, flags)); commands.append(&mut TestCheckCommand::default().prepare(sh, args));
commands.append(&mut IntegrationTestCheckCommand::default().prepare(sh, flags)); commands.append(&mut IntegrationTestCheckCommand::default().prepare(sh, args));
commands commands
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,9 +8,11 @@ use xshell::cmd;
pub struct CompileCheckCommand {} pub struct CompileCheckCommand {}
impl Prepare for CompileCheckCommand { impl Prepare for CompileCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!(sh, "cargo check --workspace"), cmd!(sh, "cargo check --workspace {jobs...}"),
"Please fix compiler errors in output above.", "Please fix compiler errors in output above.",
)] )]
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,11 +8,12 @@ use xshell::cmd;
pub struct CompileFailCommand {} pub struct CompileFailCommand {}
impl Prepare for CompileFailCommand { impl Prepare for CompileFailCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let no_fail_fast = flags let no_fail_fast = args.keep_going();
.contains(Flag::KEEP_GOING) let jobs = args.build_jobs();
.then_some("--no-fail-fast") let test_threads = args.test_threads();
.unwrap_or_default(); let jobs_ref = jobs.as_ref();
let test_threads_ref = test_threads.as_ref();
let mut commands = vec![]; let mut commands = vec![];
@ -21,7 +22,7 @@ impl Prepare for CompileFailCommand {
// - See crates/bevy_macros_compile_fail_tests/README.md // - See crates/bevy_macros_compile_fail_tests/README.md
commands.push( commands.push(
PreparedCommand::new::<Self>( PreparedCommand::new::<Self>(
cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast}"), cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast...} {jobs_ref...} -- {test_threads_ref...}"),
"Compiler errors of the macros compile fail tests seem to be different than expected! Check locally and compare rust versions.", "Compiler errors of the macros compile fail tests seem to be different than expected! Check locally and compare rust versions.",
) )
.with_subdir("crates/bevy_derive/compile_fail"), .with_subdir("crates/bevy_derive/compile_fail"),
@ -32,7 +33,7 @@ impl Prepare for CompileFailCommand {
// - See crates/bevy_ecs_compile_fail_tests/README.md // - See crates/bevy_ecs_compile_fail_tests/README.md
commands.push( commands.push(
PreparedCommand::new::<Self>( PreparedCommand::new::<Self>(
cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast}"), cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast...} {jobs_ref...} -- {test_threads_ref...}"),
"Compiler errors of the ECS compile fail tests seem to be different than expected! Check locally and compare rust versions.", "Compiler errors of the ECS compile fail tests seem to be different than expected! Check locally and compare rust versions.",
) )
.with_subdir("crates/bevy_ecs/compile_fail"), .with_subdir("crates/bevy_ecs/compile_fail"),
@ -43,7 +44,7 @@ impl Prepare for CompileFailCommand {
// - See crates/bevy_reflect_compile_fail_tests/README.md // - See crates/bevy_reflect_compile_fail_tests/README.md
commands.push( commands.push(
PreparedCommand::new::<Self>( PreparedCommand::new::<Self>(
cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast}"), cmd!(sh, "cargo test --target-dir ../../../target {no_fail_fast...} {jobs...} -- {test_threads...}"),
"Compiler errors of the Reflect compile fail tests seem to be different than expected! Check locally and compare rust versions.", "Compiler errors of the Reflect compile fail tests seem to be different than expected! Check locally and compare rust versions.",
) )
.with_subdir("crates/bevy_reflect/compile_fail"), .with_subdir("crates/bevy_reflect/compile_fail"),

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
args::Args,
commands::{DocCheckCommand, DocTestCommand}, commands::{DocCheckCommand, DocTestCommand},
Flag, Prepare, PreparedCommand, Prepare, PreparedCommand,
}; };
use argh::FromArgs; use argh::FromArgs;
@ -10,10 +11,10 @@ use argh::FromArgs;
pub struct DocCommand {} pub struct DocCommand {}
impl Prepare for DocCommand { impl Prepare for DocCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let mut commands = vec![]; let mut commands = vec![];
commands.append(&mut DocTestCommand::default().prepare(sh, flags)); commands.append(&mut DocTestCommand::default().prepare(sh, args));
commands.append(&mut DocCheckCommand::default().prepare(sh, flags)); commands.append(&mut DocCheckCommand::default().prepare(sh, args));
commands commands
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,11 +8,13 @@ use xshell::cmd;
pub struct DocCheckCommand {} pub struct DocCheckCommand {}
impl Prepare for DocCheckCommand { impl Prepare for DocCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!( cmd!(
sh, sh,
"cargo doc --workspace --all-features --no-deps --document-private-items --keep-going" "cargo doc --workspace --all-features --no-deps --document-private-items {jobs...} --keep-going"
), ),
"Please fix doc warnings in output above.", "Please fix doc warnings in output above.",
) )

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,14 +8,16 @@ use xshell::cmd;
pub struct DocTestCommand {} pub struct DocTestCommand {}
impl Prepare for DocTestCommand { impl Prepare for DocTestCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let no_fail_fast = flags let no_fail_fast = args.keep_going();
.contains(Flag::KEEP_GOING) let jobs = args.build_jobs();
.then_some("--no-fail-fast") let test_threads = args.test_threads();
.unwrap_or_default();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!(sh, "cargo test --workspace --doc {no_fail_fast}"), cmd!(
sh,
"cargo test --workspace --doc {no_fail_fast...} {jobs...} -- {test_threads...}"
),
"Please fix failing doc tests in output above.", "Please fix failing doc tests in output above.",
)] )]
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,9 +8,11 @@ use xshell::cmd;
pub struct ExampleCheckCommand {} pub struct ExampleCheckCommand {}
impl Prepare for ExampleCheckCommand { impl Prepare for ExampleCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!(sh, "cargo check --workspace --examples"), cmd!(sh, "cargo check --workspace --examples {jobs...}"),
"Please fix compiler errors for examples in output above.", "Please fix compiler errors for examples in output above.",
)] )]
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,7 +8,7 @@ use xshell::cmd;
pub struct FormatCommand {} pub struct FormatCommand {}
impl Prepare for FormatCommand { impl Prepare for FormatCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, _args: Args) -> Vec<PreparedCommand<'a>> {
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!(sh, "cargo fmt --all -- --check"), cmd!(sh, "cargo fmt --all -- --check"),
"Please run 'cargo fmt --all' to format your code.", "Please run 'cargo fmt --all' to format your code.",

View File

@ -1,4 +1,4 @@
use crate::{commands::get_integration_tests, Flag, Prepare, PreparedCommand}; use crate::{args::Args, commands::get_integration_tests, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,11 +8,12 @@ use xshell::cmd;
pub struct IntegrationTestCommand {} pub struct IntegrationTestCommand {}
impl Prepare for IntegrationTestCommand { impl Prepare for IntegrationTestCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let no_fail_fast = flags let no_fail_fast = args.keep_going();
.contains(Flag::KEEP_GOING) let jobs = args.build_jobs();
.then_some("--no-fail-fast") let test_threads = args.test_threads();
.unwrap_or_default(); let jobs_ref = jobs.as_ref();
let test_threads_ref = test_threads.as_ref();
get_integration_tests(sh) get_integration_tests(sh)
.into_iter() .into_iter()
@ -20,7 +21,7 @@ impl Prepare for IntegrationTestCommand {
PreparedCommand::new::<Self>( PreparedCommand::new::<Self>(
cmd!( cmd!(
sh, sh,
"cargo test --manifest-path {path}/Cargo.toml --tests {no_fail_fast}" "cargo test --manifest-path {path}/Cargo.toml --tests {no_fail_fast...} {jobs_ref...} -- {test_threads_ref...}"
), ),
"Please fix failing integration tests in output above.", "Please fix failing integration tests in output above.",
) )

View File

@ -1,6 +1,6 @@
use std::path::Path; use std::path::Path;
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -21,12 +21,18 @@ pub fn get_integration_tests(sh: &xshell::Shell) -> Vec<String> {
pub struct IntegrationTestCheckCommand {} pub struct IntegrationTestCheckCommand {}
impl Prepare for IntegrationTestCheckCommand { impl Prepare for IntegrationTestCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
let jobs_ref = jobs.as_ref();
get_integration_tests(sh) get_integration_tests(sh)
.into_iter() .into_iter()
.map(|path| { .map(|path| {
PreparedCommand::new::<Self>( PreparedCommand::new::<Self>(
cmd!(sh, "cargo check --manifest-path {path}/Cargo.toml --tests"), cmd!(
sh,
"cargo check --manifest-path {path}/Cargo.toml --tests {jobs_ref...}"
),
"Please fix compiler errors for tests in output above.", "Please fix compiler errors for tests in output above.",
) )
}) })

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -10,7 +10,7 @@ use super::get_integration_tests;
pub struct IntegrationTestCleanCommand {} pub struct IntegrationTestCleanCommand {}
impl Prepare for IntegrationTestCleanCommand { impl Prepare for IntegrationTestCleanCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, _args: Args) -> Vec<PreparedCommand<'a>> {
get_integration_tests(sh) get_integration_tests(sh)
.into_iter() .into_iter()
.map(|path| { .map(|path| {

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
args::Args,
commands::{ClippyCommand, FormatCommand}, commands::{ClippyCommand, FormatCommand},
Flag, Prepare, PreparedCommand, Prepare, PreparedCommand,
}; };
use argh::FromArgs; use argh::FromArgs;
@ -10,10 +11,10 @@ use argh::FromArgs;
pub struct LintsCommand {} pub struct LintsCommand {}
impl Prepare for LintsCommand { impl Prepare for LintsCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let mut commands = vec![]; let mut commands = vec![];
commands.append(&mut FormatCommand::default().prepare(sh, flags)); commands.append(&mut FormatCommand::default().prepare(sh, args));
commands.append(&mut ClippyCommand::default().prepare(sh, flags)); commands.append(&mut ClippyCommand::default().prepare(sh, args));
commands commands
} }
} }

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,18 +8,17 @@ use xshell::cmd;
pub struct TestCommand {} pub struct TestCommand {}
impl Prepare for TestCommand { impl Prepare for TestCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let no_fail_fast = flags let no_fail_fast = args.keep_going();
.contains(Flag::KEEP_GOING) let jobs = args.build_jobs();
.then_some("--no-fail-fast") let test_threads = args.test_threads();
.unwrap_or_default();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!( cmd!(
sh, sh,
// `--benches` runs each benchmark once in order to verify that they behave // `--benches` runs each benchmark once in order to verify that they behave
// correctly and do not panic. // correctly and do not panic.
"cargo test --workspace --lib --bins --tests --benches {no_fail_fast}" "cargo test --workspace --lib --bins --tests --benches {no_fail_fast...} {jobs...} -- {test_threads...}"
), ),
"Please fix failing tests in output above.", "Please fix failing tests in output above.",
)] )]

View File

@ -1,4 +1,4 @@
use crate::{Flag, Prepare, PreparedCommand}; use crate::{args::Args, Prepare, PreparedCommand};
use argh::FromArgs; use argh::FromArgs;
use xshell::cmd; use xshell::cmd;
@ -8,9 +8,11 @@ use xshell::cmd;
pub struct TestCheckCommand {} pub struct TestCheckCommand {}
impl Prepare for TestCheckCommand { impl Prepare for TestCheckCommand {
fn prepare<'a>(&self, sh: &'a xshell::Shell, _flags: Flag) -> Vec<PreparedCommand<'a>> { fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
let jobs = args.build_jobs();
vec![PreparedCommand::new::<Self>( vec![PreparedCommand::new::<Self>(
cmd!(sh, "cargo check --workspace --tests"), cmd!(sh, "cargo check --workspace --tests {jobs...}"),
"Please fix compiler errors for tests in output above.", "Please fix compiler errors for tests in output above.",
)] )]
} }

View File

@ -1,5 +1,6 @@
//! CI script used for Bevy. //! CI script used for Bevy.
mod args;
mod ci; mod ci;
mod commands; mod commands;
mod prepare; mod prepare;

View File

@ -1,4 +1,4 @@
use bitflags::bitflags; use crate::args::Args;
/// Trait for preparing a subcommand to be run. /// Trait for preparing a subcommand to be run.
pub trait Prepare { pub trait Prepare {
@ -7,7 +7,7 @@ pub trait Prepare {
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use crate::{Flag, Prepare, PreparedCommand}; /// # use crate::{args::Args, Prepare, PreparedCommand};
/// # use argh::FromArgs; /// # use argh::FromArgs;
/// # use xshell::Shell; /// # use xshell::Shell;
/// # /// #
@ -16,7 +16,7 @@ pub trait Prepare {
/// struct CheckCommand {} /// struct CheckCommand {}
/// ///
/// impl Prepare for CheckCommand { /// impl Prepare for CheckCommand {
/// fn prepare<'a>(&self, sh: &'a Shell, flags: Flag) -> Vec<PreparedCommand<'a>> { /// fn prepare<'a>(&self, sh: &'a Shell, args: Args) -> Vec<PreparedCommand<'a>> {
/// vec![PreparedCommand::new::<Self>( /// vec![PreparedCommand::new::<Self>(
/// cmd!(sh, "cargo check --workspace"), /// cmd!(sh, "cargo check --workspace"),
/// "Please fix linter errors", /// "Please fix linter errors",
@ -24,16 +24,7 @@ pub trait Prepare {
/// } /// }
/// } /// }
/// ``` /// ```
fn prepare<'a>(&self, sh: &'a xshell::Shell, flags: Flag) -> Vec<PreparedCommand<'a>>; fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>>;
}
bitflags! {
/// Flags that modify how commands are run.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Flag: u32 {
/// Forces certain checks to continue running even if they hit an error.
const KEEP_GOING = 1 << 0;
}
} }
/// A command with associated metadata, created from a command that implements [`Prepare`]. /// A command with associated metadata, created from a command that implements [`Prepare`].