1use std::collections::BTreeMap;
10
11use schemars::JsonSchema as DeriveJsonSchema;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
15pub struct Pipeline {
16 pub version: String,
20 #[serde(default)]
21 pub env: Option<BTreeMap<String, String>>,
22 #[serde(default)]
23 pub default_image: Option<String>,
24 pub steps: Vec<Step>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
28#[serde(tag = "type", rename_all = "snake_case")]
29pub enum Step {
30 Command(Box<CommandStep>),
31 Wait(WaitStep),
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
35pub struct CommandStep {
36 pub key: String,
37 #[serde(default)]
38 pub label: Option<String>,
39 pub cmd: String,
40 #[serde(default)]
41 pub builds_in: Option<String>,
42 #[serde(default)]
43 pub image: Option<String>,
44 #[serde(default)]
45 pub env: Option<BTreeMap<String, String>>,
46 #[serde(default)]
47 pub timeout_seconds: Option<u32>,
48 #[serde(default)]
49 pub cache: Option<Cache>,
50
51 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub runner: Option<String>,
56
57 #[serde(default, skip_serializing_if = "Option::is_none")]
60 pub runner_args: Option<serde_json::Value>,
61}
62
63#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
64pub struct WaitStep {
65 #[serde(default)]
66 pub continue_on_failure: bool,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, DeriveJsonSchema)]
70pub struct Cache {
71 pub policy: String,
72 #[serde(default)]
73 pub key: Option<String>,
74}
75
76#[cfg(test)]
77#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn parses_step_with_runner() {
83 let json = br#"{
84 "version": "0",
85 "steps": [
86 {"type": "command", "key": "a", "cmd": "echo a"},
87 {"type": "command", "key": "b", "cmd": "freestyle run",
88 "runner": "freestyle", "runner_args": {"region": "us"}}
89 ]
90 }"#;
91 let p: Pipeline = serde_json::from_slice(json).unwrap();
92 let Step::Command(b) = &p.steps[1] else {
93 panic!("expected command")
94 };
95 assert_eq!(b.runner.as_deref(), Some("freestyle"));
96 assert_eq!(b.runner_args.as_ref().unwrap()["region"], "us");
97 }
98
99 #[test]
100 fn parses_legacy_step_without_runner() {
101 let json = br#"{
102 "version": "0",
103 "steps": [{"type": "command", "key": "a", "cmd": "echo a"}]
104 }"#;
105 let p: Pipeline = serde_json::from_slice(json).unwrap();
106 let Step::Command(a) = &p.steps[0] else {
107 panic!("expected command")
108 };
109 assert!(a.runner.is_none());
110 assert!(a.runner_args.is_none());
111 }
112}