dotfiles_core/action.rs
1// Copyright (c) 2021-2026 Miguel Barreto and others
2//
3// Permission is hereby granted, free of charge, to any person obtaining
4// a copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to
8// permit persons to whom the Software is furnished to do so, subject to
9// the following conditions:
10//
11// The above copyright notice and this permission notice shall be
12// included in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22//! This module contains the base trait for all [Action]s.
23
24use crate::error::DotfilesError;
25
26/// Skip this whole action in CI environments.
27pub const SKIP_IN_CI_SETTING: &str = "skip_in_ci";
28/// An action to be run by the dotfiles runtime.
29pub trait Action {
30 /// Executes the action.
31 ///
32 /// Returns an error String describing the issue, this string can be used
33 /// to log or display to the user.
34 fn execute(&self) -> Result<(), DotfilesError>;
35
36 /// Whether to skip this action in Continuous Integration environments.
37 ///
38 /// See [is_running_in_ci()]
39 fn skip_in_ci(&self) -> bool;
40
41 /// Checks that the conditions allow for executing this action, and if so executes it according to
42 /// [execute(&self)].
43 ///
44 /// If conditions don't pass it simply skips and returns `Ok(())`
45 ///
46 /// At this moment the only condition that is supported is whether the action should be skipped in
47 /// CI, see [skip_in_ci(&self)].
48 fn check_conditions_and_execute(&self) -> Result<(), DotfilesError> {
49 if self.skip_in_ci() && is_running_in_ci() {
50 Ok(())
51 } else {
52 self.execute()
53 }
54 }
55}
56
57/// Whether the execution environment is presumed to be CI
58///
59/// The presence of any of the following Environment Variables is assumed to
60/// mean that this action is running in a CI Environment:
61///
62/// * `TF_BUILD`
63/// * `BUILDKITE`
64/// * `BUILD_ID`
65/// * `CI`
66/// * `CIRCLECI`
67/// * `CIRRUS_CI`
68/// * `CODEBUILD_BUILD_ID`
69/// * `GITLAB_CI`
70/// * `GITHUB_ACTIONS`
71/// * `HEROKU_TEST_RUN_ID`
72/// * `TEAMCITY_VERSION`
73/// * `TRAVIS`
74pub fn is_running_in_ci() -> bool {
75 if std::env::var("DOTFILES_TESTING_ENV_VAR").is_ok() {
76 return std::env::var("TESTING_ONLY_FAKE_CI").is_ok();
77 }
78 let env_vars = vec![
79 "TF_BUILD",
80 "BUILDKITE",
81 "BUILD_ID",
82 "CI",
83 "CIRCLECI",
84 "CIRRUS_CI",
85 "CODEBUILD_BUILD_ID",
86 "GITHUB_ACTIONS",
87 "GITLAB_CI",
88 "HEROKU_TEST_RUN_ID",
89 "TEAMCITY_VERSION",
90 "TRAVIS",
91 ];
92 for var in env_vars.iter() {
93 if std::env::var(var).is_ok() {
94 return true;
95 }
96 }
97 false
98}