rci/
lib.rs

1//! rci is wrapper for environment variables of some common continiuous integration services.
2//! At the moment [travis](https://travis-ci.org/) and [circle-ci](https://circleci.com/) is supported.
3//! A possible use case for this library is to check if your tests are running in an contniuous
4//! service.
5//! **Don't** use this to skip all of your tests and pretend everything works fine!
6//! If you are testing for example audio or graphics output
7//! that is not available in certain CI environments
8//! then you can use this library to skip those tests,
9//! like shown in the following example:
10//!
11//! ```rust
12//! use rci::*;
13//!
14//! let ci = Ci::new();
15//! if ci.is_none() {
16//!     return;
17//! } else {
18//!     println!("I'm running in: {}", ci.unwrap());
19//! }
20//! ```
21
22use std::env;
23use std::fmt;
24
25#[derive(PartialEq, Debug)]
26pub enum CiService {
27    Travis,
28    Circle,
29    Unknown,
30}
31
32macro_rules! err {
33    ($expr:expr) => {
34        match $expr {
35            Ok(val) => Some(val),
36            Err(e) => match e {
37                ::std::env::VarError::NotPresent => None,
38                _ => panic!(e),
39            }
40        }
41    }
42}
43
44macro_rules! lang_version {
45    ($lang:expr, $name:ident) => {
46        /// **Travis only**: Returns the version of the language that is used.
47        pub fn $name(&self) -> Option<String> {
48            match self.service {
49                CiService::Travis => err!(env::var(format!("TRAVIS_{}_VERSION", $lang))),
50                _ => None,
51            }
52        }
53    }
54}
55
56pub struct Ci {
57    service: CiService,
58}
59impl Ci {
60    pub fn new() -> Option<Self> {
61        let s = Ci::which_ci();
62        match s {
63            CiService::Travis | CiService::Circle => Some(Ci { service: s }),
64            _ => None,
65        }
66    }
67
68    pub fn which_ci() -> CiService {
69        match (err!(env::var("TRAVIS")), err!(env::var("CIRCLECI"))) {
70            (Some(_), None) => CiService::Travis,
71            (None, Some(_)) => CiService::Circle,
72            _ => CiService::Unknown,
73        }
74    }
75
76    /// Returns the locale setting, g.e. `en_US.UTF-8`.
77    pub fn lang() -> Option<String> {
78        err!(env::var("LANG"))
79    }
80
81    /// Returns the search path.
82    pub fn path() -> Option<String> {
83        err!(env::var("PATH"))
84    }
85
86    /// Returns the path to the users home directory.
87    pub fn home() -> Option<String> {
88        err!(env::var("HOME"))
89    }
90
91    pub fn is_travis(&self) -> bool {
92        match self.service {
93            CiService::Travis => true,
94            _ => false,
95        }
96    }
97
98    pub fn is_circle(&self) -> bool {
99        match self.service {
100            CiService::Circle => true,
101            _ => false,
102        }
103    }
104
105    pub fn branch(&self) -> Option<String> {
106        match self.service {
107            CiService::Circle => err!(env::var("CIRCLE_BRANCH")),
108            CiService::Travis => err!(env::var("TRAVIS_BRANCH")),
109            _ => None,
110        }
111    }
112
113    /// **Circle only**
114    /// A permanent link to the current build, for example,
115    /// https://circleci.com/gh/circleci/frontend/933
116    pub fn build_url(&self) -> Option<String> {
117        match self.service {
118            CiService::Circle => err!(env::var("CIRCLE_BUILD_URL")),
119            _ => None,
120        }
121    }
122
123    /// Returns the build number.
124    /// TODO: convert this to a number.
125    pub fn build_id(&self) -> Option<String> {
126        match self.service {
127            CiService::Circle => err!(env::var("CIRCLE_BUILD_NUM")),
128            CiService::Travis => err!(env::var("TRAVIS_BUILD_NUMBER")),
129            _ => None,
130        }
131    }
132
133    /// **Travis only**: The absolute path to the directory where the repository
134    /// being built has been copied on the worker.
135    /// TODO: Return a filesystem path instead?
136    pub fn build_dir(&self) -> Option<String> {
137        match self.service {
138            CiService::Travis => err!(env::var("TRAVIS_BUILD_DIR")),
139            _ => None,
140        }
141    }
142
143    /// The sha1 hash of the commit being tested.
144    pub fn commit(&self) -> Option<String> {
145        match self.service {
146            CiService::Circle => err!(env::var("CIRCLE_SHA1")),
147            CiService::Travis => err!(env::var("TRAVIS_COMMIT")),
148            _ => None,
149        }
150    }
151
152    /// The number of the pull request this build forms part of.
153    /// If this build is not part of a pull request, `None` is returned.
154    /// TODO: convert this to a number.
155    pub fn pull_request(&self) -> Option<String> {
156        match self.service {
157            CiService::Circle => err!(env::var("CIRCLE_PR_NUMBER")),
158            CiService::Travis => {
159                let pr = err!(env::var("TRAVIS_PULL_REQUEST")).unwrap_or("false".to_string());
160                if pr == "false" {
161                    None
162                } else {
163                    Some(pr)
164                }
165            }
166            _ => None,
167        }
168    }
169
170    lang_version!("DART", dart);
171    lang_version!("GO", go);
172    lang_version!("HAXE", haxe);
173    lang_version!("JDK", java);
174    lang_version!("JULIA", julia);
175    lang_version!("NODE", node);
176    lang_version!("OTP", otp);
177    lang_version!("PERL", perl);
178    lang_version!("PHP", php);
179    lang_version!("PYTHON", python);
180    lang_version!("R", r);
181    lang_version!("RUBY", ruby);
182    lang_version!("RUST", rust);
183    lang_version!("SCALA", scala);
184}
185impl fmt::Display for Ci {
186    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
187        write!(f, "Continuous Integration Service: {:?}", self.service)
188    }
189}