fluentci_graphql/schema/objects/
pipeline.rs

1use std::{
2    fs::{self},
3    sync::{mpsc::Receiver, Arc, Mutex},
4};
5
6use async_graphql::{Context, Error, Object, ID};
7use fluentci_common::common;
8use fluentci_core::deps::{Graph, GraphCommand};
9use fluentci_ext::devbox::Devbox as DevboxExt;
10use fluentci_ext::devenv::Devenv as DevenvExt;
11use fluentci_ext::envhub::Envhub as EnvhubExt;
12use fluentci_ext::flox::Flox as FloxExt;
13use fluentci_ext::git::Git as GitExt;
14use fluentci_ext::hermit::Hermit as HermitExt;
15use fluentci_ext::http::Http as HttpExt;
16use fluentci_ext::mise::Mise as MiseExt;
17use fluentci_ext::nix::Nix as NixExt;
18use fluentci_ext::pixi::Pixi as PixiExt;
19use fluentci_ext::pkgx::Pkgx as PkgxExt;
20use fluentci_ext::proto::Proto as ProtoExt;
21use fluentci_ext::runner::Runner;
22use fluentci_types::{nix, pipeline as types};
23use uuid::Uuid;
24
25use crate::{
26    schema::objects::{file::File, git::Git, mise::Mise, nix::NixArgs},
27    util::{extract_git_repo, validate_git_url},
28};
29
30use super::{
31    devbox::Devbox, devenv::Devenv, envhub::Envhub, flox::Flox, hermit::Hermit, nix::Nix,
32    pixi::Pixi, pkgx::Pkgx, proto::Proto, service::Service,
33};
34
35#[derive(Debug, Clone, Default)]
36pub struct Pipeline {
37    pub id: ID,
38}
39
40#[Object]
41impl Pipeline {
42    async fn id(&self) -> &ID {
43        &self.id
44    }
45
46    async fn http(&self, ctx: &Context<'_>, url: String) -> Result<File, Error> {
47        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
48        let mut graph = graph.lock().unwrap();
49        graph.runner = Arc::new(Box::new(HttpExt::default()));
50        graph.runner.setup()?;
51        graph.work_dir = format!(
52            "{}/.fluentci/cache",
53            dirs::home_dir().unwrap().to_str().unwrap()
54        );
55        fs::create_dir_all(&graph.work_dir)?;
56
57        let id = Uuid::new_v4().to_string();
58
59        let dep_id = graph.vertices[graph.size() - 1].id.clone();
60        let deps = match graph.size() {
61            1 => vec![],
62            _ => vec![dep_id],
63        };
64
65        graph.execute(GraphCommand::AddVertex(
66            id.clone(),
67            "http".into(),
68            url.clone(),
69            deps,
70            Arc::new(Box::new(HttpExt::default())),
71        ))?;
72        graph.execute_vertex(&id)?;
73
74        if graph.size() > 2 {
75            let x = graph.size() - 2;
76            let y = graph.size() - 1;
77            graph.execute(GraphCommand::AddEdge(x, y))?;
78        }
79        let filename = sha256::digest(url).to_string();
80        let work_dir = graph.work_dir.clone();
81
82        let file = File {
83            id: ID(id.clone()),
84            path: format!("{}/{}", work_dir, filename),
85        };
86
87        graph.execute(GraphCommand::AddVolume(
88            id,
89            "file".into(),
90            file.path.clone(),
91        ))?;
92
93        Ok(file)
94    }
95
96    async fn git(&self, ctx: &Context<'_>, url: String) -> Result<Git, Error> {
97        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
98        let mut graph = graph.lock().unwrap();
99        graph.runner = Arc::new(Box::new(GitExt::default()));
100        graph.runner.setup()?;
101        graph.work_dir = format!(
102            "{}/.fluentci/cache",
103            dirs::home_dir().unwrap().to_str().unwrap()
104        );
105
106        if !validate_git_url(&url) {
107            return Err(Error::new("Invalid git url"));
108        }
109        let repo = extract_git_repo(&url);
110        graph.work_dir = format!("{}/{}", graph.work_dir, repo);
111
112        fs::create_dir_all(&graph.work_dir)?;
113
114        let id = Uuid::new_v4().to_string();
115
116        let dep_id = graph.vertices[graph.size() - 1].id.clone();
117        let deps = match graph.size() {
118            1 => vec![],
119            _ => vec![dep_id],
120        };
121        graph.execute(GraphCommand::AddVertex(
122            id.clone(),
123            "git".into(),
124            url.clone(),
125            deps,
126            Arc::new(Box::new(GitExt::default())),
127        ))?;
128        graph.execute_vertex(&id)?;
129
130        if graph.size() > 2 {
131            let x = graph.size() - 2;
132            let y = graph.size() - 1;
133            graph.execute(GraphCommand::AddEdge(x, y))?;
134        }
135
136        graph.work_dir = format!(
137            "{}/{}",
138            graph.work_dir,
139            url.split("/").last().unwrap().replace(".git", "")
140        );
141
142        let git = Git { id: ID(id) };
143        Ok(git)
144    }
145
146    async fn devbox(&self, ctx: &Context<'_>) -> Result<Devbox, Error> {
147        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
148        let mut graph = graph.lock().unwrap();
149        graph.runner = Arc::new(Box::new(DevboxExt::default()));
150        graph.runner.setup()?;
151
152        let id = Uuid::new_v4().to_string();
153
154        let dep_id = graph.vertices[graph.size() - 1].id.clone();
155        let deps = match graph.size() {
156            1 => vec![],
157            _ => vec![dep_id],
158        };
159        graph.execute(GraphCommand::AddVertex(
160            id.clone(),
161            "devbox".into(),
162            "".into(),
163            deps,
164            Arc::new(Box::new(DevboxExt::default())),
165        ))?;
166
167        if graph.size() > 2 {
168            let x = graph.size() - 2;
169            let y = graph.size() - 1;
170            graph.execute(GraphCommand::AddEdge(x, y))?;
171        }
172
173        let devbox = Devbox { id: ID(id) };
174        Ok(devbox)
175    }
176
177    async fn devenv(&self, ctx: &Context<'_>) -> Result<Devenv, Error> {
178        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
179        let mut graph = graph.lock().unwrap();
180        graph.runner = Arc::new(Box::new(DevenvExt::default()));
181        graph.runner.setup()?;
182
183        let id = Uuid::new_v4().to_string();
184
185        let dep_id = graph.vertices[graph.size() - 1].id.clone();
186        let deps = match graph.size() {
187            1 => vec![],
188            _ => vec![dep_id],
189        };
190        graph.execute(GraphCommand::AddVertex(
191            id.clone(),
192            "devenv".into(),
193            "".into(),
194            deps,
195            Arc::new(Box::new(DevenvExt::default())),
196        ))?;
197
198        if graph.size() > 2 {
199            let x = graph.size() - 2;
200            let y = graph.size() - 1;
201            graph.execute(GraphCommand::AddEdge(x, y))?;
202        }
203
204        let devenv = Devenv { id: ID(id) };
205        Ok(devenv)
206    }
207
208    async fn flox(&self, ctx: &Context<'_>) -> Result<Flox, Error> {
209        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
210        let mut graph = graph.lock().unwrap();
211        graph.runner = Arc::new(Box::new(FloxExt::default()));
212        graph.runner.setup()?;
213
214        let id = Uuid::new_v4().to_string();
215
216        let dep_id = graph.vertices[graph.size() - 1].id.clone();
217        let deps = match graph.size() {
218            1 => vec![],
219            _ => vec![dep_id],
220        };
221        graph.execute(GraphCommand::AddVertex(
222            id.clone(),
223            "flox".into(),
224            "".into(),
225            deps,
226            Arc::new(Box::new(FloxExt::default())),
227        ))?;
228
229        if graph.size() > 2 {
230            let x = graph.size() - 2;
231            let y = graph.size() - 1;
232            graph.execute(GraphCommand::AddEdge(x, y))?;
233        }
234
235        let flox = Flox { id: ID(id) };
236        Ok(flox)
237    }
238
239    async fn nix(&self, ctx: &Context<'_>, args: Option<NixArgs>) -> Result<Nix, Error> {
240        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
241        let mut graph = graph.lock().unwrap();
242        let args: nix::NixArgs = args.unwrap_or_default().into();
243        graph.runner = Arc::new(Box::new(NixExt::new(args.clone())));
244        graph.runner.setup()?;
245        graph.nix_args = args.clone();
246
247        let id = Uuid::new_v4().to_string();
248
249        let dep_id = graph.vertices[graph.size() - 1].id.clone();
250        let deps = match graph.size() {
251            1 => vec![],
252            _ => vec![dep_id],
253        };
254
255        graph.execute(GraphCommand::AddVertex(
256            id.clone(),
257            "nix".into(),
258            "".into(),
259            deps,
260            Arc::new(Box::new(NixExt::new(args))),
261        ))?;
262
263        if graph.size() > 2 {
264            let x = graph.size() - 2;
265            let y = graph.size() - 1;
266            graph.execute(GraphCommand::AddEdge(x, y))?;
267        }
268
269        let nix = Nix { id: ID(id) };
270        Ok(nix)
271    }
272
273    async fn pkgx(&self, ctx: &Context<'_>) -> Result<Pkgx, Error> {
274        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
275        let mut graph = graph.lock().unwrap();
276        graph.runner = Arc::new(Box::new(PkgxExt::default()));
277        graph.runner.setup()?;
278
279        let id = Uuid::new_v4().to_string();
280
281        let dep_id = graph.vertices[graph.size() - 1].id.clone();
282        let deps = match graph.size() {
283            1 => vec![],
284            _ => vec![dep_id],
285        };
286        graph.execute(GraphCommand::AddVertex(
287            id.clone(),
288            "pkgx".into(),
289            "".into(),
290            deps,
291            Arc::new(Box::new(PkgxExt::default())),
292        ))?;
293
294        if graph.size() > 2 {
295            let x = graph.size() - 2;
296            let y = graph.size() - 1;
297            graph.execute(GraphCommand::AddEdge(x, y))?;
298        }
299
300        let pkgx = Pkgx { id: ID(id) };
301        Ok(pkgx)
302    }
303
304    async fn pixi(&self, ctx: &Context<'_>) -> Result<Pixi, Error> {
305        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
306        let mut graph = graph.lock().unwrap();
307        graph.runner = Arc::new(Box::new(PixiExt::default()));
308        graph.runner.setup()?;
309
310        let id = Uuid::new_v4().to_string();
311
312        let dep_id = graph.vertices[graph.size() - 1].id.clone();
313        let deps = match graph.size() {
314            1 => vec![],
315            _ => vec![dep_id],
316        };
317        graph.execute(GraphCommand::AddVertex(
318            id.clone(),
319            "pixi".into(),
320            "".into(),
321            deps,
322            Arc::new(Box::new(PixiExt::default())),
323        ))?;
324
325        if graph.size() > 2 {
326            let x = graph.size() - 2;
327            let y = graph.size() - 1;
328            graph.execute(GraphCommand::AddEdge(x, y))?;
329        }
330
331        let pixi = Pixi { id: ID(id) };
332        Ok(pixi)
333    }
334
335    async fn proto(&self, ctx: &Context<'_>) -> Result<Proto, Error> {
336        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
337        let mut graph = graph.lock().unwrap();
338        graph.runner = Arc::new(Box::new(ProtoExt::default()));
339        graph.runner.setup()?;
340
341        let id = Uuid::new_v4().to_string();
342
343        let dep_id = graph.vertices[graph.size() - 1].id.clone();
344        let deps = match graph.size() {
345            1 => vec![],
346            _ => vec![dep_id],
347        };
348        graph.execute(GraphCommand::AddVertex(
349            id.clone(),
350            "proto".into(),
351            "".into(),
352            deps,
353            Arc::new(Box::new(ProtoExt::default())),
354        ))?;
355
356        if graph.size() > 2 {
357            let x = graph.size() - 2;
358            let y = graph.size() - 1;
359            graph.execute(GraphCommand::AddEdge(x, y))?;
360        }
361
362        let proto = Proto { id: ID(id) };
363        Ok(proto)
364    }
365
366    async fn hermit(&self, ctx: &Context<'_>) -> Result<Hermit, Error> {
367        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
368        let mut graph = graph.lock().unwrap();
369        graph.runner = Arc::new(Box::new(HermitExt::default()));
370        graph.runner.setup()?;
371
372        let id = Uuid::new_v4().to_string();
373
374        let dep_id = graph.vertices[graph.size() - 1].id.clone();
375        let deps = match graph.size() {
376            1 => vec![],
377            _ => vec![dep_id],
378        };
379        graph.execute(GraphCommand::AddVertex(
380            id.clone(),
381            "hermit".into(),
382            "".into(),
383            deps,
384            Arc::new(Box::new(HermitExt::default())),
385        ))?;
386
387        if graph.size() > 2 {
388            let x = graph.size() - 2;
389            let y = graph.size() - 1;
390            graph.execute(GraphCommand::AddEdge(x, y))?;
391        }
392
393        let hermit = Hermit { id: ID(id) };
394        Ok(hermit)
395    }
396
397    async fn mise(&self, ctx: &Context<'_>) -> Result<Mise, Error> {
398        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
399        let mut graph = graph.lock().unwrap();
400        graph.runner = Arc::new(Box::new(MiseExt::default()));
401        graph.runner.setup()?;
402
403        let id = Uuid::new_v4().to_string();
404
405        let dep_id = graph.vertices[graph.size() - 1].id.clone();
406        let deps = match graph.size() {
407            1 => vec![],
408            _ => vec![dep_id],
409        };
410        graph.execute(GraphCommand::AddVertex(
411            id.clone(),
412            "mise".into(),
413            "".into(),
414            deps,
415            Arc::new(Box::new(MiseExt::default())),
416        ))?;
417
418        if graph.size() > 2 {
419            let x = graph.size() - 2;
420            let y = graph.size() - 1;
421            graph.execute(GraphCommand::AddEdge(x, y))?;
422        }
423
424        let mise = Mise { id: ID(id) };
425        Ok(mise)
426    }
427
428    async fn envhub(&self, ctx: &Context<'_>) -> Result<Envhub, Error> {
429        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
430        let mut graph = graph.lock().unwrap();
431        graph.runner = Arc::new(Box::new(EnvhubExt::default()));
432        graph.runner.setup()?;
433
434        let id = Uuid::new_v4().to_string();
435
436        let dep_id = graph.vertices[graph.size() - 1].id.clone();
437        let deps = match graph.size() {
438            1 => vec![],
439            _ => vec![dep_id],
440        };
441        graph.execute(GraphCommand::AddVertex(
442            id.clone(),
443            "envhub".into(),
444            "".into(),
445            deps,
446            Arc::new(Box::new(EnvhubExt::default())),
447        ))?;
448
449        if graph.size() > 2 {
450            let x = graph.size() - 2;
451            let y = graph.size() - 1;
452            graph.execute(GraphCommand::AddEdge(x, y))?;
453        }
454
455        let envhub = Envhub { id: ID(id) };
456        Ok(envhub)
457    }
458
459    async fn with_exec(&self, ctx: &Context<'_>, args: Vec<String>) -> Result<&Pipeline, Error> {
460        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
461        common::with_exec(graph.clone(), args, Arc::new(Box::new(Runner::default())))?;
462        Ok(self)
463    }
464
465    async fn with_workdir(&self, ctx: &Context<'_>, path: String) -> Result<&Pipeline, Error> {
466        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
467        common::with_workdir(graph.clone(), path, Arc::new(Box::new(Runner::default())))?;
468        Ok(self)
469    }
470
471    async fn with_service(&self, ctx: &Context<'_>, service: ID) -> Result<&Pipeline, Error> {
472        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
473        common::with_service(graph.clone(), service.into())?;
474        Ok(self)
475    }
476
477    async fn with_cache(
478        &self,
479        ctx: &Context<'_>,
480        path: String,
481        cache: ID,
482    ) -> Result<&Pipeline, Error> {
483        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
484        common::with_cache(graph.clone(), cache.into(), path)?;
485        Ok(self)
486    }
487
488    async fn with_file(
489        &self,
490        ctx: &Context<'_>,
491        path: String,
492        file_id: ID,
493    ) -> Result<&Pipeline, Error> {
494        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
495        common::with_file(graph.clone(), file_id.into(), path)?;
496        Ok(self)
497    }
498
499    async fn stdout(&self, ctx: &Context<'_>) -> Result<String, Error> {
500        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
501        let rx = ctx.data::<Arc<Mutex<Receiver<(String, usize)>>>>().unwrap();
502        common::stdout(graph.clone(), rx.clone()).map_err(|e| Error::new(e.to_string()))
503    }
504
505    async fn stderr(&self, ctx: &Context<'_>) -> Result<String, Error> {
506        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
507        let rx = ctx.data::<Arc<Mutex<Receiver<(String, usize)>>>>().unwrap();
508        common::stderr(graph.clone(), rx.clone()).map_err(|e| Error::new(e.to_string()))
509    }
510
511    async fn as_service(&self, ctx: &Context<'_>, name: String) -> Result<Service, Error> {
512        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
513        let service = common::as_service(graph.clone(), name)?;
514        Ok(service.into())
515    }
516
517    async fn with_env_variable(
518        &self,
519        ctx: &Context<'_>,
520        name: String,
521        value: String,
522    ) -> Result<&Pipeline, Error> {
523        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
524        common::with_env_variable(graph.clone(), &name, &value)?;
525        Ok(self)
526    }
527
528    async fn wait_on(
529        &self,
530        ctx: &Context<'_>,
531        port: u32,
532        timeout: Option<u32>,
533    ) -> Result<&Pipeline, Error> {
534        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
535        common::wait_on(graph.clone(), port, timeout)?;
536        Ok(self)
537    }
538
539    async fn with_secret_variable(
540        &self,
541        ctx: &Context<'_>,
542        name: String,
543        secret: ID,
544    ) -> Result<&Pipeline, Error> {
545        let graph = ctx.data::<Arc<Mutex<Graph>>>().unwrap();
546        let g = graph.lock().unwrap();
547        let secret_name = g.secret_names.get(secret.as_str()).unwrap().clone();
548        drop(g);
549        common::with_secret_variable(graph.clone(), &name, secret.as_str(), &secret_name)?;
550        Ok(self)
551    }
552}
553
554impl From<types::Pipeline> for Pipeline {
555    fn from(pipeline: types::Pipeline) -> Self {
556        Self {
557            id: ID(pipeline.id),
558        }
559    }
560}