feat(ir): validate front-matter task steps against typed builders#1096
feat(ir): validate front-matter task steps against typed builders#1096jamesadevine wants to merge 2 commits into
Conversation
Replace the free-function ADO task helpers in src/compile/ir/tasks.rs
(positional required args + untyped .with_input("camelKey","str") for
optionals) with a tasks/ directory module of typed builder structs, one
file per task. Each builder exposes new(<required>) + typed chained
setters + into_step() -> TaskStep; only set fields emit inputs, with
enums for constrained values and Option<bool> for bool-string inputs.
Command/mode-dispatch tasks (Docker@2, DotNetCoreCLI@2, NuGetCommand@2,
PowerShell@2) use a command enum with per-variant data so invalid
input/command combinations are unrepresentable; tasks/docker.rs is the
canonical template. Migrate the docker_installer call sites, update the
ado-task-ir-contributor workflow + docs to the new convention.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a parse direction to the typed task builders: parse_task_step() deserializes an authored ADO task-step mapping and validates it by reusing the builder structs as the schema. Derive Deserialize (keyed on ADO input names, deny_unknown_fields) on CopyFiles and the Docker@2 command variants, plus a flexible bool deserializer accepting both rue and "true". Partial coverage is safe and additive: parse_task_step returns Ok(Some(TaskStep)) for a recognized+valid step, Ok(None) for anything not modeled (unmapped task or non-task step) so the caller keeps the original YAML as today's opaque passthrough, and Err only for a recognized task with invalid inputs (missing required, unknown key, bad value, or an input for the wrong command). Wired up for CopyFiles@2 and Docker@2 as a proof of concept. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Rust PR ReviewSummary: Good design and clean implementation overall, but there's one real bug with integer-typed inputs and a silent data-loss risk in the public API surface that should be addressed before callers wire this up. Findings🐛 Bugs / Logic Issues
✅ What Looks Good
|
Summary
Adds the parse / validate direction to the typed task builders introduced in #1082 — making them usable as a schema for front-matter task steps, not just one-way constructors.
parse_task_step(&serde_yaml::Value) -> Result<Option<TaskStep>>(insrc/compile/ir/tasks/parse.rs) is the inverse ofinto_step(): it deserializes an authored ADO task-step mapping and validates it by reusing the builder structs.Deserialize(keyed on real ADO input names,deny_unknown_fields) onCopyFilesand the fiveDocker@2command-variant structs.common.rsso bothCleanTargetFolder: trueandCleanTargetFolder: "true"validate (matching how ADO inputs are authored).Docker@2dispatches on itscommandinput into the right variant, so each command only accepts its own inputs.Partial coverage is safe and additive
This is designed to sit in front of the existing front-matter
steps:passthrough, so it has three outcomes:Ok(Some(step))TaskStepOk(None)bash:/script:) → caller keeps the original YAML unchanged (today:Step::RawYaml)Err(e)"Not mapped" is deliberately not an error, so mapping a new task only ever adds validation and never rejects a workflow that compiled before. Strict mode (reject any
task:not in the IR) would be a separate opt-in policy on top.Wired up for
CopyFiles@2andDocker@2as a proof of concept; extending coverage = deriveDeserializeon the remaining builders + one match arm.Test plan
cargo build— cleancargo clippy --all-targets --all-features— cleancargo test— full suite green;tasks::parseadds 11 tests covering: valid CopyFiles/Docker roundtrip, missing required input, unknown input key, bad bool value, input supplied for the wrong Docker command, missing/unknown command, unmapped-task passthrough, and non-task-step passthrough