gh_workflow/
job.rs

1//!
2//! Job-related structures and implementations for GitHub workflow jobs.
3
4use derive_setters::Setters;
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use crate::concurrency::Concurrency;
10use crate::step::{Step, StepType, StepValue};
11use crate::{
12    Artifacts, Container, Defaults, Env, Expression, Permissions, RetryStrategy, Secret, Strategy,
13};
14
15/// Represents the environment in which a job runs.
16#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
17#[serde(transparent)]
18pub struct RunsOn(Value);
19
20impl<T> From<T> for RunsOn
21where
22    T: Into<Value>,
23{
24    /// Converts a value into a `RunsOn` instance.
25    fn from(value: T) -> Self {
26        RunsOn(value.into())
27    }
28}
29
30/// Represents a job in the workflow.
31#[derive(Debug, Setters, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
32#[serde(rename_all = "kebab-case")]
33#[setters(strip_option, into)]
34pub struct Job {
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub needs: Option<Vec<String>>,
37    #[serde(skip_serializing_if = "Option::is_none", rename = "if")]
38    pub cond: Option<Expression>,
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub name: Option<String>,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub runs_on: Option<RunsOn>,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub permissions: Option<Permissions>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub env: Option<Env>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub strategy: Option<Strategy>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub steps: Option<Vec<StepValue>>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub uses: Option<String>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub container: Option<Container>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub outputs: Option<IndexMap<String, String>>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub concurrency: Option<Concurrency>,
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub timeout_minutes: Option<u32>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub services: Option<IndexMap<String, Container>>,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub secrets: Option<IndexMap<String, Secret>>,
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub defaults: Option<Defaults>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub continue_on_error: Option<bool>,
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub retry: Option<RetryStrategy>,
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub artifacts: Option<Artifacts>,
73}
74
75impl Job {
76    /// Creates a new `Job` with the specified name and default settings.
77    pub fn new<T: ToString>(name: T) -> Self {
78        Self {
79            name: Some(name.to_string()),
80            runs_on: Some(RunsOn(Value::from("ubuntu-latest"))),
81            ..Default::default()
82        }
83    }
84
85    /// Adds a step to the job.
86    pub fn add_step<S: Into<Step<T>>, T: StepType>(mut self, step: S) -> Self {
87        let mut steps = self.steps.take().unwrap_or_default();
88        let step: Step<T> = step.into();
89        let step: StepValue = T::to_value(step);
90        steps.push(step);
91        self.steps = Some(steps);
92        self
93    }
94
95    /// Adds an environment variable to the job.
96    pub fn add_env<T: Into<Env>>(mut self, new_env: T) -> Self {
97        let mut env = self.env.take().unwrap_or_default();
98
99        env.0.extend(new_env.into().0);
100        self.env = Some(env);
101        self
102    }
103
104    pub fn add_needs<J: ToString>(mut self, job_id: J) -> Self {
105        if let Some(needs) = self.needs.as_mut() {
106            needs.push(job_id.to_string());
107        } else {
108            self.needs = Some(vec![job_id.to_string()]);
109        }
110        self
111    }
112}