1#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2pub enum CiEnv {
3 None,
5 GitHubActions,
7}
8
9impl CiEnv {
10 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 pub fn is_rust_lang_managed_ci_job() -> bool {
25 Self::is_ci()
26 && 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 #[track_caller]
43 pub fn group(name: impl std::fmt::Display) -> Group {
44 let mut groups = ACTIVE_GROUPS.lock().unwrap();
45
46 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 #[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 groups.pop();
68
69 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}