build_helper/
ci.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum CiEnv {
    /// Not a CI environment.
    None,
    /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
    GitHubActions,
}

impl CiEnv {
    /// Obtains the current CI environment.
    pub fn current() -> CiEnv {
        if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
            CiEnv::GitHubActions
        } else {
            CiEnv::None
        }
    }

    pub fn is_ci() -> bool {
        Self::current() != CiEnv::None
    }

    /// Checks if running in rust-lang/rust managed CI job.
    pub fn is_rust_lang_managed_ci_job() -> bool {
        Self::is_ci()
            // If both are present, we can assume it's an upstream CI job
            // as they are always set unconditionally.
            && std::env::var_os("CI_JOB_NAME").is_some()
            && std::env::var_os("TOOLSTATE_REPO").is_some()
    }
}

pub mod gha {
    use std::sync::Mutex;

    static ACTIVE_GROUPS: Mutex<Vec<String>> = Mutex::new(Vec::new());

    /// All github actions log messages from this call to the Drop of the return value
    /// will be grouped and hidden by default in logs. Note that since github actions doesn't
    /// support group nesting, any active group will be first finished when a subgroup is started,
    /// and then re-started when the subgroup finishes.
    #[track_caller]
    pub fn group(name: impl std::fmt::Display) -> Group {
        let mut groups = ACTIVE_GROUPS.lock().unwrap();

        // A group is currently active. End it first to avoid nesting.
        if !groups.is_empty() {
            end_group();
        }

        let name = name.to_string();
        start_group(&name);
        groups.push(name);
        Group(())
    }

    /// A guard that closes the current github actions log group on drop.
    #[must_use]
    pub struct Group(());

    impl Drop for Group {
        fn drop(&mut self) {
            end_group();

            let mut groups = ACTIVE_GROUPS.lock().unwrap();
            // Remove the current group
            groups.pop();

            // If there was some previous group, restart it
            if is_in_gha() {
                if let Some(name) = groups.last() {
                    start_group(format!("{name} (continued)"));
                }
            }
        }
    }

    fn start_group(name: impl std::fmt::Display) {
        if is_in_gha() {
            println!("::group::{name}");
        } else {
            println!("{name}")
        }
    }

    fn end_group() {
        if is_in_gha() {
            println!("::endgroup::");
        }
    }

    fn is_in_gha() -> bool {
        std::env::var_os("GITHUB_ACTIONS").is_some()
    }
}