Skip to main content

orchestrator_config/config/
pipeline.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Maximum byte length for a pipeline variable value to remain inline.
5/// Values exceeding this are spilled to a file and the inline value is truncated.
6/// 4 KB leaves headroom for bash escaping inflation (~1.5-2x) plus template
7/// boilerplate within the 16 KB runner safety limit.
8pub const PIPELINE_VAR_INLINE_LIMIT: usize = 4096;
9
10/// Pipeline variables passed between steps
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct PipelineVariables {
13    /// Key-value store of pipeline variables
14    #[serde(default)]
15    pub vars: HashMap<String, String>,
16    /// Build errors from the last build step
17    #[serde(default)]
18    pub build_errors: Vec<BuildError>,
19    /// Test failures from the last test step
20    #[serde(default)]
21    pub test_failures: Vec<TestFailure>,
22    /// Raw stdout from previous step
23    #[serde(default)]
24    pub prev_stdout: String,
25    /// Raw stderr from previous step
26    #[serde(default)]
27    pub prev_stderr: String,
28    /// Git diff of current cycle
29    #[serde(default)]
30    pub diff: String,
31}
32
33/// Build error with source location
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct BuildError {
36    /// Source file that emitted the error, when available.
37    pub file: Option<String>,
38    /// 1-based source line for the error, when available.
39    pub line: Option<u32>,
40    /// 1-based source column for the error, when available.
41    pub column: Option<u32>,
42    /// Human-readable compiler or build-system message.
43    pub message: String,
44    /// Severity assigned to the build diagnostic.
45    pub level: BuildErrorLevel,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
49#[serde(rename_all = "snake_case")]
50/// Severity levels recorded for build diagnostics.
51pub enum BuildErrorLevel {
52    /// A failing diagnostic that should block the pipeline.
53    Error,
54    /// A non-fatal diagnostic surfaced to the workflow.
55    Warning,
56}
57
58/// Test failure with source location
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct TestFailure {
61    /// Test case or suite name that failed.
62    pub test_name: String,
63    /// Source file associated with the failure, when available.
64    pub file: Option<String>,
65    /// 1-based source line associated with the failure, when available.
66    pub line: Option<u32>,
67    /// Human-readable failure message.
68    pub message: String,
69    /// Captured stdout emitted by the failing test, when available.
70    pub stdout: Option<String>,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_pipeline_variables_default() {
79        let pv = PipelineVariables::default();
80        assert!(pv.vars.is_empty());
81        assert!(pv.build_errors.is_empty());
82        assert!(pv.test_failures.is_empty());
83        assert_eq!(pv.prev_stdout, "");
84        assert_eq!(pv.prev_stderr, "");
85        assert_eq!(pv.diff, "");
86    }
87
88    #[test]
89    fn test_pipeline_variables_deserialize_minimal() {
90        let json = r#"{}"#;
91        let pv: PipelineVariables =
92            serde_json::from_str(json).expect("deserialize minimal pipeline variables");
93        assert!(pv.vars.is_empty());
94        assert!(pv.build_errors.is_empty());
95    }
96
97    #[test]
98    fn test_build_error_level_serde() {
99        let err: BuildErrorLevel =
100            serde_json::from_str("\"error\"").expect("deserialize error level");
101        assert_eq!(err, BuildErrorLevel::Error);
102        let warn: BuildErrorLevel =
103            serde_json::from_str("\"warning\"").expect("deserialize warning level");
104        assert_eq!(warn, BuildErrorLevel::Warning);
105    }
106
107    #[test]
108    fn test_pipeline_var_inline_limit() {
109        assert_eq!(PIPELINE_VAR_INLINE_LIMIT, 4096);
110    }
111}