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}