1pub fn is_ci() -> bool {
7 is_ci_with(|name| std::env::var(name).ok())
8}
9
10pub fn is_ci_with(variable: impl Fn(&str) -> Option<String>) -> bool {
12 let ci = variable("CI");
13 if ci.as_deref() == Some("false") {
14 return false;
15 }
16 let set = |name: &str| variable(name).is_some_and(|value| !value.is_empty());
17 let is_true = |name: &str| variable(name).as_deref() == Some("true");
18 ci.is_some_and(|value| !value.is_empty())
19 || [
20 "TF_BUILD",
21 "GITHUB_ACTIONS",
22 "BUILDKITE",
23 "CIRCLECI",
24 "CIRRUS_CI",
25 "TRAVIS",
26 ]
27 .into_iter()
28 .any(is_true)
29 || [
30 "bamboo.buildKey",
31 "bamboo_buildKey",
32 "CODEBUILD_BUILD_ID",
33 "GITLAB_CI",
34 "HEROKU_TEST_RUN_ID",
35 "BUILD_ID",
36 "BUILD_NUMBER",
37 "BUILD_BUILDID",
38 "TEAMCITY_VERSION",
39 "JENKINS_URL",
40 "HUDSON_URL",
41 ]
42 .into_iter()
43 .any(set)
44}
45
46pub fn force_colour() -> Option<bool> {
50 let value = std::env::var_os("FORCE_COLOR")?;
51 Some(value != "0" && value != "false")
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 fn ci(variables: &[(&str, &str)]) -> bool {
59 is_ci_with(|name| {
60 variables
61 .iter()
62 .find(|(key, _)| *key == name)
63 .map(|(_, value)| (*value).to_owned())
64 })
65 }
66
67 #[test]
68 fn reads_ci_as_nx_does() {
69 assert!(ci(&[("CI", "0")]));
70 assert!(ci(&[("CI", "1")]));
71 assert!(!ci(&[("CI", "false")]));
72 assert!(!ci(&[("CI", "false"), ("GITHUB_ACTIONS", "true")]));
73 assert!(!ci(&[]));
74 assert!(!ci(&[("CI", "")]));
75 assert!(ci(&[("GITHUB_ACTIONS", "true")]));
76 assert!(!ci(&[("GITHUB_ACTIONS", "1")]));
77 assert!(ci(&[("GITLAB_CI", "1")]));
78 assert!(ci(&[("JENKINS_URL", "http://ci")]));
79 }
80}