Skip to content

Commit adb41e9

Browse files
committed
include_tasks like ansible
1 parent 1627a61 commit adb41e9

8 files changed

+165
-76
lines changed

src/common.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use indexmap::IndexMap;
22
use serde::{Deserialize, Serialize};
3+
use serde_json::Value;
34

45
#[derive(Debug, Deserialize)]
56
pub struct Debug(pub IndexMap<String, String>);
@@ -10,3 +11,17 @@ pub struct Register {
1011
pub stderr: String,
1112
pub rc: i32,
1213
}
14+
15+
#[derive(Debug, Deserialize)]
16+
pub struct Task {
17+
pub name: String,
18+
pub shell: Option<String>,
19+
pub command: Option<String>,
20+
pub register: Option<String>,
21+
pub debug: Option<Debug>,
22+
pub vars: Option<IndexMap<String, String>>,
23+
pub chdir: Option<String>,
24+
pub when: Option<String>,
25+
pub r#loop: Option<Vec<Value>>,
26+
pub include_tasks: Option<String>,
27+
}

src/main.rs

+103-76
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use colored::Colorize;
77
use indexmap::IndexMap;
88
use serde::Deserialize;
99
use serde_json::Value;
10+
use ssh2::Session;
1011
use std::path::Path;
1112
use std::process::exit;
1213

@@ -29,21 +30,99 @@ struct Deployment {
2930
name: String,
3031
hosts: String,
3132
chdir: Option<String>,
32-
tasks: Vec<Task>,
33+
tasks: Vec<common::Task>,
3334
}
3435

35-
#[derive(Debug, Deserialize)]
36-
#[serde(rename_all = "kebab-case")]
37-
struct Task {
38-
name: String,
39-
shell: Option<String>,
40-
command: Option<String>,
41-
register: Option<String>,
42-
debug: Option<common::Debug>,
43-
vars: Option<IndexMap<String, String>>,
44-
chdir: Option<String>,
45-
when: Option<String>,
46-
r#loop: Option<Vec<Value>>,
36+
fn process_tasks(
37+
tasks: &[common::Task],
38+
is_localhost: bool,
39+
session: Option<&Session>,
40+
dep_chdir: Option<&str>,
41+
vars_map: &mut IndexMap<String, Value>,
42+
deploy_file_dir: &Path,
43+
) -> Result<(), Box<dyn std::error::Error>> {
44+
for task in tasks {
45+
if !modules::when::process(&task.when, vars_map) {
46+
println!("{}", format!("Skipping task: {}\n", task.name).yellow());
47+
continue;
48+
}
49+
50+
println!("{}", format!("Executing task: {}", task.name).cyan());
51+
52+
let task_chdir = task.chdir.as_deref().or(dep_chdir); // Use task-level chdir if present, otherwise use top-level chdir
53+
54+
if let Some(vars) = &task.vars {
55+
for (key, value) in vars {
56+
let evaluated_value = utils::replace_placeholders_vars(&value, vars_map);
57+
vars_map.insert(key.clone(), evaluated_value);
58+
}
59+
}
60+
61+
// Debug print to verify vars_map
62+
// println!("Vars map: {:?}", vars_map);
63+
64+
let loop_items = task.r#loop.clone().unwrap_or_else(|| vec![Value::Null]);
65+
66+
for item in loop_items {
67+
vars_map.shift_remove("item");
68+
69+
if !item.is_null() {
70+
vars_map.insert("item".to_string(), item.clone());
71+
}
72+
73+
if let Some(debug) = &task.debug {
74+
modules::debug::process(debug, vars_map);
75+
}
76+
77+
if let Some(shell_command) = &task.shell {
78+
let commands = utils::split_commands(shell_command);
79+
modules::command::process(
80+
commands,
81+
is_localhost,
82+
session,
83+
true,
84+
task_chdir,
85+
task.register.as_ref(),
86+
vars_map,
87+
)?;
88+
}
89+
90+
if let Some(command) = &task.command {
91+
let commands = utils::split_commands(command);
92+
modules::command::process(
93+
commands,
94+
is_localhost,
95+
session,
96+
false,
97+
task_chdir,
98+
task.register.as_ref(),
99+
vars_map,
100+
)?;
101+
}
102+
103+
if let Some(include_file) = &task.include_tasks {
104+
println!(
105+
"{}",
106+
format!("Including tasks from: {}\n", include_file).blue()
107+
);
108+
let include_file_path = deploy_file_dir.join(include_file);
109+
let included_tasks =
110+
modules::include_tasks::process(include_file_path.to_str().unwrap())?;
111+
process_tasks(
112+
&included_tasks,
113+
is_localhost,
114+
session,
115+
task_chdir,
116+
vars_map,
117+
deploy_file_dir,
118+
)?;
119+
}
120+
}
121+
122+
println!();
123+
}
124+
125+
Ok(())
47126
}
48127

49128
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -114,6 +193,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
114193
}
115194
}
116195

196+
let deploy_file_path = Path::new(deploy_file);
197+
let deploy_file_dir = deploy_file_path.parent().unwrap_or(Path::new("."));
198+
117199
for dep in deployments {
118200
println!("{}", format!("Starting deployment: {}\n", dep.name).green());
119201

@@ -148,69 +230,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
148230
None
149231
};
150232

151-
for task in &dep.tasks {
152-
if !modules::when::process(&task.when, &vars_map) {
153-
println!("{}", format!("Skipping task: {}\n", task.name).yellow());
154-
continue;
155-
}
156-
157-
println!("{}", format!("Executing task: {}", task.name).cyan());
158-
159-
let task_chdir = task.chdir.as_deref().or(dep.chdir.as_deref()); // Use task-level chdir if present, otherwise use top-level chdir
160-
161-
if let Some(vars) = &task.vars {
162-
for (key, value) in vars {
163-
let evaluated_value =
164-
utils::replace_placeholders_vars(&value, &vars_map);
165-
vars_map.insert(key.clone(), evaluated_value);
166-
}
167-
}
168-
169-
// Debug print to verify vars_map
170-
// println!("Vars map: {:?}", vars_map);
171-
172-
let loop_items = task.r#loop.clone().unwrap_or_else(|| vec![Value::Null]);
173-
174-
for item in loop_items {
175-
vars_map.shift_remove("item");
176-
177-
if !item.is_null() {
178-
vars_map.insert("item".to_string(), item.clone());
179-
}
180-
181-
if let Some(debug) = &task.debug {
182-
modules::debug::process(debug, &vars_map);
183-
}
184-
185-
if let Some(shell_command) = &task.shell {
186-
let commands = utils::split_commands(shell_command);
187-
modules::command::process(
188-
commands,
189-
is_localhost,
190-
session.as_ref(),
191-
true,
192-
task_chdir,
193-
task.register.as_ref(),
194-
&mut vars_map,
195-
)?;
196-
}
197-
198-
if let Some(command) = &task.command {
199-
let commands = utils::split_commands(command);
200-
modules::command::process(
201-
commands,
202-
is_localhost,
203-
session.as_ref(),
204-
false,
205-
task_chdir,
206-
task.register.as_ref(),
207-
&mut vars_map,
208-
)?;
209-
}
210-
}
211-
212-
println!();
213-
}
233+
process_tasks(
234+
&dep.tasks,
235+
is_localhost,
236+
session.as_ref(),
237+
dep.chdir.as_deref(),
238+
&mut vars_map,
239+
deploy_file_dir,
240+
)?;
214241
} else {
215242
eprintln!(
216243
"{}",

src/modules/include_tasks.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use crate::common::Task;
2+
use crate::utils;
3+
4+
pub fn process(include_file: &str) -> Result<Vec<Task>, Box<dyn std::error::Error>> {
5+
let included_tasks: Vec<Task> = utils::read_yaml(include_file)?;
6+
Ok(included_tasks)
7+
}

src/modules/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod command;
22
pub mod debug;
3+
pub mod include_tasks;
34
pub mod when;

test-ymls/include-tasks.include.yml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-
2+
name: Task 1
3+
command: echo "Include Task 1"
4+
5+
-
6+
name: Task 2
7+
command: echo "Include Task 2"

test-ymls/include-tasks.yml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
- name: Test
2+
hosts: test
3+
tasks:
4+
- name: Include tasks
5+
include_tasks: include-tasks.include.yml
6+
7+
- name: Task after include tasks
8+
command: echo "Task after include tasks"

test-ymls/include-tasks.yml.out

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Starting deployment: Test
2+
3+
Executing task: Include tasks
4+
Including tasks from: include-tasks.include.yml
5+
6+
Executing task: Task 1
7+
> echo "Include Task 1"
8+
Include Task 1
9+
10+
Executing task: Task 2
11+
> echo "Include Task 2"
12+
Include Task 2
13+
14+
15+
Executing task: Task after include tasks
16+
> echo "Task after include tasks"
17+
Task after include tasks
18+

tests/integration_test.rs

+6
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,9 @@ fn loop_item() {
216216
setup();
217217
run_tests_for_both_inventories("test-ymls/loop-item.yml", false, "");
218218
}
219+
220+
#[test]
221+
fn include_tasks() {
222+
setup();
223+
run_tests_for_both_inventories("test-ymls/include-tasks.yml", false, "");
224+
}

0 commit comments

Comments
 (0)