feat(wfe-yaml): add YAML workflow definitions with shell executor

Concourse-CI-inspired YAML format for defining workflows. Compiles
to standard WorkflowDefinition + step factories.

Features:
- Schema parsing with serde_yaml (YamlWorkflow, YamlStep, StepConfig)
- ((var.path)) interpolation from config maps at load time
- YAML anchors (&anchor/*alias) fully supported
- Validation at load time (no runtime surprises)
- Shell executor: runs commands via tokio::process, captures stdout,
  parses ##wfe[output name=value] annotations for structured outputs
- Compiler: sequential wiring, parallel blocks, on_failure/on_success/
  ensure hooks, error behavior mapping
- Public API: load_workflow(), load_workflow_from_str()
- 23 tests (schema, interpolation, compiler, e2e)
This commit is contained in:
2026-03-25 21:32:00 +00:00
parent 8d0f83da3c
commit b89563af63
14 changed files with 1377 additions and 1 deletions

72
wfe-yaml/src/schema.rs Normal file
View File

@@ -0,0 +1,72 @@
use std::collections::HashMap;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct YamlWorkflow {
pub workflow: WorkflowSpec,
}
#[derive(Debug, Deserialize)]
pub struct WorkflowSpec {
pub id: String,
pub version: u32,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub error_behavior: Option<YamlErrorBehavior>,
pub steps: Vec<YamlStep>,
/// Allow unknown top-level keys (e.g. `_templates`) for YAML anchors.
#[serde(flatten)]
pub _extra: HashMap<String, serde_yaml::Value>,
}
#[derive(Debug, Deserialize)]
pub struct YamlStep {
pub name: String,
#[serde(rename = "type")]
pub step_type: Option<String>,
#[serde(default)]
pub config: Option<StepConfig>,
#[serde(default)]
pub inputs: Vec<DataRef>,
#[serde(default)]
pub outputs: Vec<DataRef>,
#[serde(default)]
pub parallel: Option<Vec<YamlStep>>,
#[serde(default)]
pub error_behavior: Option<YamlErrorBehavior>,
#[serde(default)]
pub on_success: Option<Box<YamlStep>>,
#[serde(default)]
pub on_failure: Option<Box<YamlStep>>,
#[serde(default)]
pub ensure: Option<Box<YamlStep>>,
}
#[derive(Debug, Deserialize, Clone)]
pub struct StepConfig {
pub run: Option<String>,
pub file: Option<String>,
pub script: Option<String>,
pub shell: Option<String>,
#[serde(default)]
pub env: HashMap<String, String>,
pub timeout: Option<String>,
pub working_dir: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct DataRef {
pub name: String,
pub path: Option<String>,
pub json_path: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct YamlErrorBehavior {
#[serde(rename = "type")]
pub behavior_type: String,
pub interval: Option<String>,
pub max_retries: Option<u32>,
}