astro_runner/
astro_runner.rs1use crate::{
2 executors::{DockerExecutor, Executor, HostExecutor},
3 Plugin, PluginDriver, SharedPluginDriver,
4};
5use astro_run::{
6 stream, Context, Error, HookNoopResult, Result, RunResponse, RunResult, Runner, TriggerEvent,
7 WorkflowId,
8};
9use parking_lot::Mutex;
10use std::{collections::HashMap, env, fs, path::PathBuf, sync::Arc};
11
12struct RunnerState {
13 workflow_events: HashMap<WorkflowId, TriggerEvent>,
14}
15
16#[derive(Clone)]
17pub struct AstroRunner {
18 working_directory: PathBuf,
19 state: Arc<Mutex<RunnerState>>,
20 plugin_driver: SharedPluginDriver,
21}
22
23impl AstroRunner {
24 pub fn builder() -> AstroRunnerBuilder {
25 AstroRunnerBuilder::new()
26 }
27}
28
29#[astro_run::async_trait]
30impl Runner for AstroRunner {
31 async fn on_workflow_completed(&self, result: astro_run::WorkflowRunResult) -> HookNoopResult {
32 if let Err(err) = self.cleanup_workflow_working_directory(result) {
33 log::error!("AstroRunner: cleanup error: {}", err);
34 }
35
36 Ok(())
37 }
38
39 async fn run(&self, ctx: Context) -> RunResponse {
40 let (sender, receiver) = stream();
41
42 let ctx = self.plugin_driver.on_before_run(ctx).await;
43
44 let executor = self.create_executor(&ctx);
45
46 let event = ctx.event.clone();
47 if let Some(event) = &ctx.event {
48 self
49 .state
50 .lock()
51 .workflow_events
52 .insert(ctx.command.id.workflow_id(), event.clone());
53 }
54
55 let plugins = Arc::clone(&self.plugin_driver);
56
57 tokio::spawn(async move {
58 if let Err(err) = executor.execute(ctx.clone(), sender.clone(), event).await {
59 log::error!("AstroRunner: execute error: {}", err);
60 }
61
62 if !sender.is_ended() {
63 sender.end(RunResult::Failed { exit_code: 1 });
64 }
65
66 plugins.on_after_run(ctx).await;
67 });
68
69 Ok(receiver)
70 }
71}
72
73impl AstroRunner {
74 fn create_executor(&self, ctx: &Context) -> Box<dyn Executor> {
75 let os_name = std::env::consts::OS;
76 let architecture = std::env::consts::ARCH;
77 let container = ctx.command.container.clone();
78 if let Some(container) = container {
79 let host_name = format!("host/{}", os_name);
81 let host_name_with_arch = format!("host/{}-{}", os_name, architecture);
83
84 if container.name == host_name_with_arch || container.name == host_name {
85 let executor = HostExecutor {
86 working_directory: self.working_directory.clone(),
87 };
88
89 return Box::new(executor);
90 }
91 }
92
93 let executor = DockerExecutor {
94 working_directory: self.working_directory.clone(),
95 };
96
97 Box::new(executor)
98 }
99
100 fn cleanup_workflow_working_directory(&self, result: astro_run::WorkflowRunResult) -> Result<()> {
101 let event = self.state.lock().workflow_events.get(&result.id).cloned();
102
103 let mut directory = self.working_directory.clone();
104
105 if let Some(event) = event {
106 directory = directory.join(&event.repo_owner).join(&event.repo_name);
107 }
108
109 directory = directory.join(result.id.inner());
110
111 if directory.exists() {
112 fs::remove_dir_all(directory)?;
113 }
114
115 Ok(()) as Result<()>
116 }
117}
118
119#[derive(Default)]
120pub struct AstroRunnerBuilder {
121 working_directory: Option<PathBuf>,
122 plugins: Vec<Box<dyn Plugin>>,
123}
124
125impl AstroRunnerBuilder {
126 pub fn new() -> Self {
127 Self::default()
128 }
129
130 pub fn plugin<P: Plugin + 'static>(mut self, plugin: P) -> Self {
131 self.plugins.push(Box::new(plugin));
132
133 self
134 }
135
136 pub fn working_directory(mut self, working_directory: PathBuf) -> Self {
137 self.working_directory = Some(working_directory);
138 self
139 }
140
141 pub fn build(self) -> Result<AstroRunner> {
142 let working_directory = self.working_directory.map(Ok).unwrap_or_else(|| {
143 #[allow(deprecated)]
144 env::home_dir()
145 .map(|home| home.join("astro-run"))
146 .ok_or_else(|| Error::init_error("AstroRunnerBuilder: working_directory is required"))
147 })?;
148
149 let runner = AstroRunner {
150 working_directory,
151 state: Arc::new(Mutex::new(RunnerState {
152 workflow_events: HashMap::new(),
153 })),
154 plugin_driver: Arc::new(PluginDriver::new(self.plugins)),
155 };
156
157 Ok(runner)
158 }
159}