moon_config/project/task_options_config.rs
1use crate::portable_path::FilePath;
2use crate::shapes::{InputPath, OneOrMany};
3use crate::{config_enum, config_struct, config_unit_enum, generate_switch};
4use schematic::schema::{StringType, UnionType};
5use schematic::{Config, ConfigEnum, Schema, SchemaBuilder, Schematic, ValidateError};
6use std::env::consts;
7use std::str::FromStr;
8
9fn validate_interactive<C>(
10 enabled: &bool,
11 options: &PartialTaskOptionsConfig,
12 _ctx: &C,
13 _finalize: bool,
14) -> Result<(), ValidateError> {
15 if *enabled && options.persistent.is_some_and(|v| v) {
16 return Err(ValidateError::new(
17 "an interactive task cannot be persistent",
18 ));
19 }
20
21 Ok(())
22}
23
24config_enum!(
25 /// The pattern in which affected files will be passed to the affected task.
26 #[serde(expecting = "expected `args`, `env`, or a boolean")]
27 pub enum TaskOptionAffectedFiles {
28 /// Passed as command line arguments.
29 Args,
30 /// Passed as environment variables.
31 Env,
32 /// Passed as command line arguments and environment variables.
33 #[serde(untagged)]
34 Enabled(bool),
35 }
36);
37
38generate_switch!(TaskOptionAffectedFiles, ["args", "env"]);
39
40config_enum!(
41 /// The pattern in which a task is dependent on a `.env` file.
42 #[serde(
43 untagged,
44 expecting = "expected a boolean, a file path, or a list of file paths"
45 )]
46 pub enum TaskOptionEnvFile {
47 /// Uses an `.env` file in the project root.
48 Enabled(bool),
49 /// Explicit path to an `.env` file.
50 File(FilePath),
51 /// List of explicit `.env` file paths.
52 Files(Vec<FilePath>),
53 }
54);
55
56impl TaskOptionEnvFile {
57 pub fn to_input_paths(&self) -> Option<Vec<InputPath>> {
58 match self {
59 TaskOptionEnvFile::Enabled(true) => Some(vec![InputPath::ProjectFile(".env".into())]),
60 TaskOptionEnvFile::Enabled(false) => None,
61 TaskOptionEnvFile::File(path) => {
62 InputPath::from_str(path.as_str()).ok().map(|p| vec![p])
63 }
64 TaskOptionEnvFile::Files(paths) => Some(
65 paths
66 .iter()
67 .flat_map(|p| InputPath::from_str(p.as_str()).ok())
68 .collect(),
69 ),
70 }
71 }
72}
73
74impl Schematic for TaskOptionEnvFile {
75 fn schema_name() -> Option<String> {
76 Some("TaskOptionEnvFile".into())
77 }
78
79 fn build_schema(mut schema: SchemaBuilder) -> Schema {
80 schema.union(UnionType::new_any([
81 schema.infer::<bool>(),
82 schema.infer::<String>(),
83 schema.infer::<Vec<String>>(),
84 ]))
85 }
86}
87
88config_enum!(
89 /// The pattern in which to run the task automatically in CI.
90 #[serde(expecting = "expected `always`, `affected`, or a boolean")]
91 pub enum TaskOptionRunInCI {
92 /// Always run, regardless of affected.
93 Always,
94 /// Only run if affected by touched files.
95 Affected,
96 /// Either affected, or don't run at all.
97 #[serde(untagged)]
98 Enabled(bool),
99 }
100);
101
102generate_switch!(TaskOptionRunInCI, ["always", "affected"]);
103
104config_unit_enum!(
105 /// The strategy in which to merge a specific task option.
106 #[derive(ConfigEnum)]
107 pub enum TaskMergeStrategy {
108 #[default]
109 Append,
110 Prepend,
111 Preserve,
112 Replace,
113 }
114);
115
116config_unit_enum!(
117 /// The style in which task output will be printed to the console.
118 #[derive(ConfigEnum)]
119 pub enum TaskOutputStyle {
120 #[default]
121 Buffer,
122 BufferOnlyFailure,
123 Hash,
124 None,
125 Stream,
126 }
127);
128
129config_enum!(
130 /// The operating system in which to only run this task on.
131 #[derive(ConfigEnum, Copy)]
132 pub enum TaskOperatingSystem {
133 Linux,
134 #[serde(alias = "mac")]
135 Macos,
136 #[serde(alias = "win")]
137 Windows,
138 }
139);
140
141impl TaskOperatingSystem {
142 pub fn is_current_system(&self) -> bool {
143 let os = consts::OS;
144
145 match self {
146 Self::Linux => os == "linux" || os.ends_with("bsd"),
147 Self::Macos => os == "macos",
148 Self::Windows => os == "windows",
149 }
150 }
151}
152
153config_unit_enum!(
154 /// The priority levels a task can be bucketed into.
155 #[derive(ConfigEnum)]
156 pub enum TaskPriority {
157 Critical = 0,
158 High = 1,
159 #[default]
160 Normal = 2,
161 Low = 3,
162 }
163);
164
165impl TaskPriority {
166 pub fn get_level(&self) -> u8 {
167 *self as u8
168 }
169}
170
171config_unit_enum!(
172 /// A list of available shells on Unix.
173 #[derive(ConfigEnum)]
174 pub enum TaskUnixShell {
175 #[default]
176 Bash,
177 Elvish,
178 Fish,
179 Ion,
180 Murex,
181 #[serde(alias = "nushell")]
182 Nu,
183 #[serde(alias = "powershell")]
184 Pwsh,
185 Xonsh,
186 Zsh,
187 }
188);
189
190config_unit_enum!(
191 /// A list of available shells on Windows.
192 #[derive(ConfigEnum)]
193 pub enum TaskWindowsShell {
194 Bash,
195 Elvish,
196 Fish,
197 Murex,
198 #[serde(alias = "nushell")]
199 Nu,
200 #[default]
201 #[serde(alias = "powershell")]
202 Pwsh,
203 Xonsh,
204 }
205);
206
207config_struct!(
208 /// Options to control task inheritance and execution.
209 #[derive(Config)]
210 pub struct TaskOptionsConfig {
211 /// The pattern in which affected files will be passed to the task.
212 pub affected_files: Option<TaskOptionAffectedFiles>,
213
214 /// When affected and no files are matching, pass the task inputs
215 /// as arguments to the command, instead of `.`.
216 pub affected_pass_inputs: Option<bool>,
217
218 /// Allows the task to fail without failing the entire pipeline.
219 pub allow_failure: Option<bool>,
220
221 /// Caches the `outputs` of the task. Defaults to `true` if outputs
222 /// are configured for the task.
223 pub cache: Option<bool>,
224
225 /// A custom key to include in the cache hashing process. Can be
226 /// used to invalidate local and remote caches.
227 pub cache_key: Option<String>,
228
229 /// Lifetime to cache the task itself, in the format of "1h", "30m", etc.
230 /// If not defined, caches live forever, or until inputs change.
231 pub cache_lifetime: Option<String>,
232
233 /// Loads and sets environment variables from the `.env` file when
234 /// running the task.
235 pub env_file: Option<TaskOptionEnvFile>,
236
237 /// Automatically infer inputs from file groups or environment variables
238 /// that were utilized within `command`, `script`, `args`, and `env`.
239 pub infer_inputs: Option<bool>,
240
241 /// Marks the task as interactive, so that it will run in isolation,
242 /// and have direct access to stdin.
243 #[setting(validate = validate_interactive)]
244 pub interactive: Option<bool>,
245
246 /// Marks the task as internal, which disables it from begin ran
247 /// from the command line, but can be depended on.
248 pub internal: Option<bool>,
249
250 /// The default strategy to use when merging `args`, `deps`, `env`,
251 /// `inputs`, or `outputs` with an inherited task. Can be overridden
252 /// with the other field-specific merge options.
253 pub merge: Option<TaskMergeStrategy>,
254
255 /// The strategy to use when merging `args` with an inherited task.
256 pub merge_args: Option<TaskMergeStrategy>,
257
258 /// The strategy to use when merging `deps` with an inherited task.
259 pub merge_deps: Option<TaskMergeStrategy>,
260
261 /// The strategy to use when merging `env` with an inherited task.
262 pub merge_env: Option<TaskMergeStrategy>,
263
264 /// The strategy to use when merging `inputs` with an inherited task.
265 pub merge_inputs: Option<TaskMergeStrategy>,
266
267 /// The strategy to use when merging `outputs` with an inherited task.
268 pub merge_outputs: Option<TaskMergeStrategy>,
269
270 /// Creates an exclusive lock on a virtual resource, preventing other
271 /// tasks using the same resource from running concurrently.
272 pub mutex: Option<String>,
273
274 /// The operating system in which to only run this task on.
275 pub os: Option<OneOrMany<TaskOperatingSystem>>,
276
277 /// The style in which task output will be printed to the console.
278 #[setting(env = "MOON_OUTPUT_STYLE")]
279 pub output_style: Option<TaskOutputStyle>,
280
281 /// Marks the task as persistent (continuously running). This is ideal
282 /// for watchers, servers, or never-ending processes.
283 pub persistent: Option<bool>,
284
285 /// Marks the task with a certain priority, which determines the order
286 /// in which it is ran within the pipeline.
287 pub priority: Option<TaskPriority>,
288
289 /// The number of times a failing task will be retried to succeed.
290 #[setting(env = "MOON_RETRY_COUNT")]
291 pub retry_count: Option<u8>,
292
293 /// Runs direct task dependencies (via `deps`) in sequential order.
294 /// This _does not_ apply to indirect or transient dependencies.
295 pub run_deps_in_parallel: Option<bool>,
296
297 /// Whether to run the task in CI or not, when executing `moon ci` or `moon run`.
298 #[serde(rename = "runInCI")]
299 pub run_in_ci: Option<TaskOptionRunInCI>,
300
301 /// Runs the task from the workspace root, instead of the project root.
302 pub run_from_workspace_root: Option<bool>,
303
304 /// Runs the task within a shell. When not defined, runs the task
305 /// directly while relying on `PATH` resolution.
306 pub shell: Option<bool>,
307
308 /// The maximum time in seconds that a task can run before being cancelled.
309 pub timeout: Option<u64>,
310
311 /// The shell to run the task in when on a Unix-based machine.
312 pub unix_shell: Option<TaskUnixShell>,
313
314 /// The shell to run the task in when on a Windows machine.
315 pub windows_shell: Option<TaskWindowsShell>,
316 }
317);