1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//!
//! The serde representation of Github Actions Workflow.
use derive_setters::Setters;
use indexmap::IndexMap;
use merge::Merge;
use serde::{Deserialize, Serialize};
use crate::concurrency::Concurrency;
use crate::defaults::Defaults;
// Import the moved types
use crate::env::Env;
use crate::error::Result;
use crate::generate::Generate;
use crate::job::Job;
use crate::permissions::Permissions;
use crate::secret::Secret;
use crate::Event;
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
#[serde(transparent)]
pub struct Jobs(pub(crate) IndexMap<String, Job>);
impl Jobs {
pub fn add(mut self, key: String, value: Job) -> Self {
self.0.insert(key, value);
self
}
/// Gets a reference to a job by its key.
///
/// # Arguments
///
/// * `key` - The key of the job to retrieve
///
/// # Returns
///
/// Returns `Some(&Job)` if the job exists, `None` otherwise.
pub fn get(&self, key: &str) -> Option<&Job> {
self.0.get(key)
}
}
/// Represents the configuration for a GitHub workflow.
///
/// A workflow is a configurable automated process made up of one or more jobs.
/// This struct defines the properties that can be set in a workflow YAML file
/// for GitHub Actions, including the name, environment variables, permissions,
/// jobs, concurrency settings, and more.
#[derive(Debug, Default, Setters, Serialize, Deserialize, Clone)]
#[serde(rename_all = "kebab-case")]
#[setters(strip_option, into)]
pub struct Workflow {
/// The name of the workflow. GitHub displays the names of your workflows
/// under your repository's "Actions" tab.
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
/// Environment variables that can be used in the workflow.
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<Env>,
/// The name for workflow runs generated from the workflow.
/// GitHub displays the workflow run name in the list of workflow runs.
#[serde(skip_serializing_if = "Option::is_none")]
pub run_name: Option<String>,
/// The event that triggers the workflow. This can include events like
/// `push`, `pull_request`, etc.
#[serde(skip_serializing_if = "Option::is_none")]
pub on: Option<Event>,
/// Permissions granted to the `GITHUB_TOKEN` for the workflow.
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<Permissions>,
/// The jobs that are defined in the workflow.
#[serde(skip_serializing_if = "Option::is_none")]
pub jobs: Option<Jobs>,
/// Concurrency settings for the workflow, allowing control over
/// how jobs are executed.
#[serde(skip_serializing_if = "Option::is_none")]
pub concurrency: Option<Concurrency>,
/// Default settings for jobs in the workflow.
#[serde(skip_serializing_if = "Option::is_none")]
pub defaults: Option<Defaults>,
/// Secrets that can be used in the workflow, such as tokens or passwords.
#[serde(skip_serializing_if = "Option::is_none")]
pub secrets: Option<IndexMap<String, Secret>>,
/// The maximum number of minutes a job can run before it is canceled.
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_minutes: Option<u32>,
}
/// Represents an action that can be triggered by an event in the workflow.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct EventAction {
/// A list of branches that trigger the action.
#[serde(skip_serializing_if = "Vec::is_empty")]
branches: Vec<String>,
/// A list of branches that are ignored for the action.
#[serde(skip_serializing_if = "Vec::is_empty")]
branches_ignore: Vec<String>,
}
impl Workflow {
/// Creates a new `Workflow` with the specified name.
pub fn new<T: ToString>(name: T) -> Self {
Self { name: Some(name.to_string()), ..Default::default() }
}
/// Converts the `Workflow` to a YAML string representation.
pub fn to_string(&self) -> Result<String> {
Ok(serde_yml::to_string(self)?)
}
/// Adds a job to the workflow with the specified ID and job configuration.
pub fn add_job<T: ToString, J: Into<Job>>(mut self, id: T, job: J) -> Self {
let key = id.to_string();
let jobs = self.jobs.take().unwrap_or_default().add(key, job.into());
self.jobs = Some(jobs);
self
}
/// Parses a YAML string into a `Workflow`.
pub fn parse(yml: &str) -> Result<Self> {
Ok(serde_yml::from_str(yml)?)
}
/// Generates the workflow using the `Generate` struct.
pub fn generate(self) -> Result<()> {
Generate::new(self).generate()
}
/// Adds an event to the workflow.
pub fn add_event<T: Into<Event>>(mut self, that: T) -> Self {
if let Some(mut this) = self.on.take() {
this.merge(that.into());
self.on = Some(this);
} else {
self.on = Some(that.into());
}
self
}
/// Adds an environment variable to the workflow.
pub fn add_env<T: Into<Env>>(mut self, new_env: T) -> Self {
let mut env = self.env.take().unwrap_or_default();
env.0.extend(new_env.into().0);
self.env = Some(env);
self
}
}