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