1#[macro_use]
9extern crate version;
10#[macro_use]
11pub mod macros;
12
13pub mod builder;
14pub mod error_formatter;
15pub mod git;
16pub mod init;
17pub mod logger;
18pub mod py_logger;
19pub mod task;
20pub mod utils;
21pub mod validation;
22
23use builder::build_app;
24use error_formatter::PythonErrorFormatter;
25use task::ANGREAL_TASKS;
26
27use pyo3::types::{IntoPyDict, PyDict};
28use std::ops::Not;
29use std::vec::Vec;
30
31use std::process::exit;
32
33use pyo3::{prelude::*, wrap_pymodule};
34
35use log::{debug, error, warn};
36
37#[pyfunction]
39fn main() -> PyResult<()> {
40 let handle = logger::init_logger();
41 if std::env::var("ANGREAL_DEBUG").unwrap_or_default() == "true" {
42 logger::update_verbosity(&handle, 2);
43 warn!("Angreal application starting with debug level logging from environment");
44 }
45 debug!("Angreal application starting...");
46
47 let mut argvs: Vec<String> = std::env::args().collect();
50 argvs = argvs.split_off(2);
51
52 debug!("Checking if binary is up to date...");
53 match utils::check_up_to_date() {
54 Ok(()) => (),
55 Err(e) => warn!(
56 "An error occurred while checking if our binary is up to date. {}",
57 e.to_string()
58 ),
59 };
60
61 let angreal_project_result = utils::is_angreal_project();
63 let in_angreal_project = angreal_project_result.is_ok();
64
65 if in_angreal_project {
66 debug!("Angreal project detected, loading found tasks.");
67 let angreal_path = angreal_project_result.expect("Expected angreal project path");
68 let angreal_tasks_to_load = utils::get_task_files(angreal_path);
70
71 let _angreal_tasks_to_load = match angreal_tasks_to_load {
73 Ok(tasks) => tasks,
74 Err(_) => {
75 error!("Exiting due to unrecoverable error.");
76 exit(1);
77 }
78 };
79
80 for task in _angreal_tasks_to_load.iter() {
82 if let Err(e) = utils::load_python(task.clone()) {
83 error!("Failed to load Python task: {}", e);
84 }
85 }
86 }
87
88 let app = build_app(in_angreal_project);
89 let mut app_copy = app.clone();
90 let sub_command = app.get_matches_from(&argvs);
91
92 let verbosity = sub_command.get_count("verbose");
94
95 if std::env::var("ANGREAL_DEBUG").is_err() {
97 logger::update_verbosity(&handle, verbosity);
98 debug!("Log verbosity set to level: {}", verbosity);
99 }
100
101 match sub_command.subcommand() {
102 Some(("init", _sub_matches)) => init::init(
103 _sub_matches.value_of("template").unwrap(),
104 _sub_matches.is_present("force"),
105 _sub_matches.is_present("defaults").not(),
106 if _sub_matches.is_present("values_file") {
107 Some(_sub_matches.value_of("values_file").unwrap())
108 } else {
109 None
110 },
111 ),
112 Some((task, sub_m)) => {
113 if !in_angreal_project {
114 error!("This doesn't appear to be an angreal project.");
115 exit(1)
116 }
117
118 let mut command_groups: Vec<String> = Vec::new();
119 command_groups.push(task.to_string());
120
121 let mut next = sub_m.subcommand();
124 let mut arg_matches = sub_m.clone();
125 while next.is_some() {
126 let cmd = next.unwrap();
127 command_groups.push(cmd.0.to_string());
128 next = cmd.1.subcommand();
129 arg_matches = cmd.1.clone();
130 }
131
132 let task = command_groups.pop().unwrap();
133
134 let some_command = ANGREAL_TASKS.lock().unwrap().clone();
135 let some_command = some_command.iter().find(|&x| {
136 x.name == task.as_str()
137 && x.group
138 .clone()
139 .unwrap()
140 .iter()
141 .map(|x| x.name.to_string())
142 .collect::<Vec<String>>()
143 == command_groups
144 });
145
146 debug!("Executing command: {}", task);
147 let command = match some_command {
148 None => {
149 error!("Command '{}' not found.", task);
150 app_copy.print_help().unwrap_or(());
151 exit(1)
152 }
153 Some(some_command) => some_command,
154 };
155
156 let args = builder::select_args(task.as_str());
157 Python::with_gil(|py| {
158 debug!("Starting Python execution for command: {}", task);
159 let mut kwargs: Vec<(&str, PyObject)> = Vec::new();
160
161 for arg in args.into_iter() {
162 let n = Box::leak(Box::new(arg.name));
163 if arg.is_flag.unwrap() {
167 let v = arg_matches.get_flag(&n.clone());
168 kwargs.push((n.as_str(), v.to_object(py)));
169 } else {
170 let v = arg_matches.value_of(n.clone());
171 match v {
172 None => {
173 kwargs.push((n.as_str(), v.to_object(py)));
176 }
177 Some(v) => match arg.python_type.unwrap().as_str() {
178 "str" => kwargs.push((n.as_str(), v.to_object(py))),
179 "int" => kwargs
180 .push((n.as_str(), v.parse::<i32>().unwrap().to_object(py))),
181 "float" => kwargs
182 .push((n.as_str(), v.parse::<f32>().unwrap().to_object(py))),
183 _ => kwargs.push((n.as_str(), v.to_object(py))),
184 },
185 }
186 }
187 }
188
189 let r_value = command.func.call(py, (), Some(kwargs.into_py_dict(py)));
190
191 match r_value {
192 Ok(_r_value) => debug!("Successfully executed Python command: {}", task),
193 Err(err) => {
194 error!("Failed to execute Python command: {}", task);
195 let formatter = PythonErrorFormatter::new(err);
196 println!("{}", formatter);
197 exit(1);
198 }
199 }
200 });
201 }
202 _ => {
203 println!("process for current context")
204 }
205 }
206
207 debug!("Angreal application completed successfully.");
208 Ok(())
209}
210
211#[pymodule]
212fn angreal(_py: Python, m: &PyModule) -> PyResult<()> {
213 m.add("__version__", env!("CARGO_PKG_VERSION"))?;
214
215 py_logger::register();
216 m.add_function(wrap_pyfunction!(main, m)?)?;
217 task::register(_py, m)?;
218 utils::register(_py, m)?;
219
220 m.add_wrapped(wrap_pymodule!(_integrations))?;
221
222 let sys = PyModule::import(_py, "sys")?;
223 let sys_modules: &PyDict = sys.getattr("modules")?.downcast()?;
224 sys_modules.set_item("angreal._integrations", m.getattr("_integrations")?)?;
225 sys_modules.set_item(
226 "angreal._integrations.docker",
227 m.getattr("_integrations")?.getattr("docker")?,
228 )?;
229
230 sys_modules.set_item(
231 "angreal._integrations.docker.image",
232 m.getattr("_integrations")?
233 .getattr("docker")?
234 .getattr("image")?,
235 )?;
236 sys_modules.set_item(
237 "angreal._integrations.docker.container",
238 m.getattr("_integrations")?
239 .getattr("docker")?
240 .getattr("container")?,
241 )?;
242 sys_modules.set_item(
243 "angreal._integrations.docker.network",
244 m.getattr("_integrations")?
245 .getattr("docker")?
246 .getattr("network")?,
247 )?;
248 sys_modules.set_item(
249 "angreal._integrations.docker.volume",
250 m.getattr("_integrations")?
251 .getattr("docker")?
252 .getattr("volume")?,
253 )?;
254 Ok(())
255}
256
257#[pymodule]
258fn _integrations(_py: Python, m: &PyModule) -> PyResult<()> {
259 m.add_wrapped(wrap_pymodule!(docker))?;
260 Ok(())
261}
262
263#[pymodule]
264fn docker(_py: Python, m: &PyModule) -> PyResult<()> {
265 m.add_class::<docker_pyo3::Pyo3Docker>()?;
266 m.add_wrapped(wrap_pymodule!(docker_pyo3::image::image))?;
267 m.add_wrapped(wrap_pymodule!(docker_pyo3::container::container))?;
268 m.add_wrapped(wrap_pymodule!(docker_pyo3::network::network))?;
269 m.add_wrapped(wrap_pymodule!(docker_pyo3::volume::volume))?;
270 Ok(())
271}