build_helper/ci.rs
1#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2pub enum CiEnv {
3 /// Not a CI environment.
4 None,
5 /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
6 GitHubActions,
7}
8
9impl CiEnv {
10 /// Obtains the current CI environment.
11 pub fn current() -> CiEnv {
12 if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
13 CiEnv::GitHubActions
14 } else {
15 CiEnv::None
16 }
17 }
18
19 pub fn is_ci() -> bool {
20 Self::current().is_running_in_ci()
21 }
22
23 pub fn is_running_in_ci(self) -> bool {
24 self != CiEnv::None
25 }
26
27 /// Checks if running in rust-lang/rust managed CI job.
28 pub fn is_rust_lang_managed_ci_job() -> bool {
29 Self::is_ci()
30 // If both are present, we can assume it's an upstream CI job
31 // as they are always set unconditionally.
32 && std::env::var_os("CI_JOB_NAME").is_some()
33 && std::env::var_os("TOOLSTATE_REPO").is_some()
34 }
35}
36
37pub mod gha {
38 use std::sync::Mutex;
39
40 static ACTIVE_GROUPS: Mutex<Vec<String>> = Mutex::new(Vec::new());
41
42 /// All github actions log messages from this call to the Drop of the return value
43 /// will be grouped and hidden by default in logs. Note that since github actions doesn't
44 /// support group nesting, any active group will be first finished when a subgroup is started,
45 /// and then re-started when the subgroup finishes.
46 #[track_caller]
47 pub fn group(name: impl std::fmt::Display) -> Group {
48 let mut groups = ACTIVE_GROUPS.lock().unwrap();
49
50 // A group is currently active. End it first to avoid nesting.
51 if !groups.is_empty() {
52 end_group();
53 }
54
55 let name = name.to_string();
56 start_group(&name);
57 groups.push(name);
58 Group(())
59 }
60
61 /// A guard that closes the current github actions log group on drop.
62 #[must_use]
63 pub struct Group(());
64
65 impl Drop for Group {
66 fn drop(&mut self) {
67 end_group();
68
69 let mut groups = ACTIVE_GROUPS.lock().unwrap();
70 // Remove the current group
71 groups.pop();
72
73 // If there was some previous group, restart it
74 if is_in_gha() {
75 if let Some(name) = groups.last() {
76 start_group(format!("{name} (continued)"));
77 }
78 }
79 }
80 }
81
82 fn start_group(name: impl std::fmt::Display) {
83 if is_in_gha() {
84 println!("::group::{name}");
85 } else {
86 println!("{name}")
87 }
88 }
89
90 fn end_group() {
91 if is_in_gha() {
92 println!("::endgroup::");
93 }
94 }
95
96 fn is_in_gha() -> bool {
97 std::env::var_os("GITHUB_ACTIONS").is_some()
98 }
99}