1pub mod parser;
2use std::{collections::HashMap, fs::OpenOptions};
3
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6
7use crate::{core::watcher::WatchContext, exec::OutpuStrategy};
8
9#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
10pub struct Cmd {
11 pub cmd: String,
12 #[serde(default)]
13 pub blocking: bool,
14 #[serde(default)]
15 pub container: Option<String>,
16}
17
18#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
19pub struct Job {
20 #[serde(default)]
21 pub needs: Vec<String>,
22 #[serde(default)]
23 pub pipe: String,
24 #[serde(default)]
25 pub env: Option<HashMap<String, String>>,
26 pub steps: Vec<Cmd>,
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
30pub struct Pipeline {
31 pub notifications: Option<Notification>,
32 pub jobs: HashMap<String, Job>,
33}
34
35#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
36pub struct ProjectConfig {
37 pub pipeline: Pipeline,
38
39 pub branches: Vec<String>,
40
41 #[serde(default)]
42 pub timeout: Option<u64>,
43}
44
45#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
46pub struct ConfChannel {
47 pub service: String,
48 pub url: String,
49}
50
51#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
52pub struct Notification {
53 pub on: Vec<String>,
54 pub channels: Vec<ConfChannel>,
55 #[serde(default)]
56 pub thumbnail: Option<String>,
57}
58
59fn find_pipe_dependance(ctx: &WatchContext, job_name: &str) -> Option<Cmd> {
60 for (n, j) in &ctx.config.pipeline.jobs {
61 if !j.pipe.is_empty() && n == job_name {
62 let target = j.steps.last().cloned();
63 return target;
64 }
65 }
66 None
67}
68
69impl ProjectConfig {
70 pub fn drop_strategy(&self, job_name: &str, ctx: &WatchContext) -> Result<OutpuStrategy> {
71 let log_path = ctx.log_path();
72 let stdout_file = OpenOptions::new()
73 .create(true)
74 .append(true)
75 .open(&log_path)?;
76 let stderr_file = OpenOptions::new()
77 .create(true)
78 .append(true)
79 .open(&log_path)?;
80
81 for j in self.pipeline.jobs.values() {
82 if !j.pipe.is_empty() && j.pipe == job_name {
83 let cmd = ctx
84 .config
85 .pipeline
86 .jobs
87 .get(job_name)
88 .unwrap()
89 .steps
90 .last()
91 .unwrap();
92 let target = String::from(&j.steps.last().unwrap().cmd);
93 println!("[1]'{target}' has design as target");
94 return Ok(OutpuStrategy::ToPipeOut {
95 cmd: cmd.cmd.clone(),
96 stdout: stdout_file,
97 stderr: stderr_file,
98 target,
99 });
100 }
101 }
102 if let Some(depend) = find_pipe_dependance(ctx, job_name) {
103 return Ok(OutpuStrategy::ToPipeIn {
105 stdout: stdout_file,
106 stderr: stderr_file,
107 target: depend.cmd,
108 });
109 }
110 Ok(OutpuStrategy::ToFiles {
111 stdout: stdout_file,
112 stderr: stderr_file,
113 })
114 }
115}
116
117pub fn stdin_is_tty() -> bool {
118 #[cfg(feature = "no-tty")]
119 {
120 false
121 }
122 #[cfg(not(feature = "no-tty"))]
123 {
124 atty::is(atty::Stream::Stdin)
125 }
126}