Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/tools/ci/src/ci.rs
6596 views
1
use crate::{
2
args::Args,
3
commands,
4
prepare::{Prepare, PreparedCommand},
5
};
6
use argh::FromArgs;
7
8
/// The CI command line tool for Bevy.
9
#[derive(FromArgs)]
10
pub struct CI {
11
#[argh(subcommand)]
12
command: Option<Commands>,
13
14
/// continue running commands even if one fails
15
#[argh(switch)]
16
pub(crate) keep_going: bool,
17
18
/// parallelism of `cargo test`
19
#[argh(option)]
20
pub(crate) test_threads: Option<usize>,
21
22
/// number of build jobs
23
#[argh(option)]
24
pub(crate) build_jobs: Option<usize>,
25
}
26
27
impl CI {
28
/// Runs the specified commands or all commands if none are specified.
29
///
30
/// When run locally, results may differ from actual CI runs triggered by `.github/workflows/ci.yml`.
31
/// This is usually related to differing toolchains and configuration.
32
pub fn run(self) {
33
let sh = xshell::Shell::new().unwrap();
34
let prepared_commands = self.prepare(&sh);
35
36
let mut failures = vec![];
37
38
for command in prepared_commands {
39
// If the CI test is to be executed in a subdirectory, we move there before running the command.
40
// This will automatically move back to the original directory once dropped.
41
let _subdir_hook = command.subdir.map(|path| sh.push_dir(path));
42
43
// Execute each command, checking if it returned an error.
44
if command.command.envs(command.env_vars).run().is_err() {
45
let name = command.name;
46
let message = command.failure_message;
47
48
if self.keep_going {
49
// We use bullet points here because there can be more than one error.
50
failures.push(format!("- {name}: {message}"));
51
} else {
52
failures.push(format!("{name}: {message}"));
53
break;
54
}
55
}
56
}
57
58
// Log errors at the very end.
59
if !failures.is_empty() {
60
let failures = failures.join("\n");
61
62
panic!(
63
"One or more CI commands failed:\n\
64
{failures}"
65
);
66
}
67
}
68
69
fn prepare<'a>(&self, sh: &'a xshell::Shell) -> Vec<PreparedCommand<'a>> {
70
let args = self.into();
71
match &self.command {
72
Some(command) => command.prepare(sh, args),
73
None => {
74
// Note that we are running the subcommands directly rather than using any aliases
75
let mut cmds = vec![];
76
cmds.append(&mut commands::FormatCommand::default().prepare(sh, args));
77
cmds.append(&mut commands::ClippyCommand::default().prepare(sh, args));
78
cmds.append(&mut commands::TestCommand::default().prepare(sh, args));
79
cmds.append(&mut commands::TestCheckCommand::default().prepare(sh, args));
80
cmds.append(&mut commands::IntegrationTestCommand::default().prepare(sh, args));
81
cmds.append(
82
&mut commands::IntegrationTestCheckCommand::default().prepare(sh, args),
83
);
84
cmds.append(
85
&mut commands::IntegrationTestCleanCommand::default().prepare(sh, args),
86
);
87
cmds.append(&mut commands::DocCheckCommand::default().prepare(sh, args));
88
cmds.append(&mut commands::DocTestCommand::default().prepare(sh, args));
89
cmds.append(&mut commands::CompileCheckCommand::default().prepare(sh, args));
90
cmds.append(&mut commands::CompileFailCommand::default().prepare(sh, args));
91
cmds.append(&mut commands::BenchCheckCommand::default().prepare(sh, args));
92
cmds.append(&mut commands::ExampleCheckCommand::default().prepare(sh, args));
93
94
cmds
95
}
96
}
97
}
98
}
99
100
/// The subcommands that can be run by the CI script.
101
#[derive(FromArgs)]
102
#[argh(subcommand)]
103
enum Commands {
104
// Aliases (subcommands that run other subcommands)
105
Lints(commands::LintsCommand),
106
Doc(commands::DocCommand),
107
Compile(commands::CompileCommand),
108
// Actual subcommands
109
Format(commands::FormatCommand),
110
Clippy(commands::ClippyCommand),
111
Test(commands::TestCommand),
112
TestCheck(commands::TestCheckCommand),
113
IntegrationTest(commands::IntegrationTestCommand),
114
IntegrationTestCheck(commands::IntegrationTestCheckCommand),
115
IntegrationTestClean(commands::IntegrationTestCleanCommand),
116
DocCheck(commands::DocCheckCommand),
117
DocTest(commands::DocTestCommand),
118
CompileCheck(commands::CompileCheckCommand),
119
CompileFail(commands::CompileFailCommand),
120
BenchCheck(commands::BenchCheckCommand),
121
ExampleCheck(commands::ExampleCheckCommand),
122
}
123
124
impl Prepare for Commands {
125
fn prepare<'a>(&self, sh: &'a xshell::Shell, args: Args) -> Vec<PreparedCommand<'a>> {
126
match self {
127
Commands::Lints(subcommand) => subcommand.prepare(sh, args),
128
Commands::Doc(subcommand) => subcommand.prepare(sh, args),
129
Commands::Compile(subcommand) => subcommand.prepare(sh, args),
130
131
Commands::Format(subcommand) => subcommand.prepare(sh, args),
132
Commands::Clippy(subcommand) => subcommand.prepare(sh, args),
133
Commands::Test(subcommand) => subcommand.prepare(sh, args),
134
Commands::TestCheck(subcommand) => subcommand.prepare(sh, args),
135
Commands::IntegrationTest(subcommand) => subcommand.prepare(sh, args),
136
Commands::IntegrationTestCheck(subcommand) => subcommand.prepare(sh, args),
137
Commands::IntegrationTestClean(subcommand) => subcommand.prepare(sh, args),
138
Commands::DocCheck(subcommand) => subcommand.prepare(sh, args),
139
Commands::DocTest(subcommand) => subcommand.prepare(sh, args),
140
Commands::CompileCheck(subcommand) => subcommand.prepare(sh, args),
141
Commands::CompileFail(subcommand) => subcommand.prepare(sh, args),
142
Commands::BenchCheck(subcommand) => subcommand.prepare(sh, args),
143
Commands::ExampleCheck(subcommand) => subcommand.prepare(sh, args),
144
}
145
}
146
}
147
148