diff --git a/src/compile/ir/tasks.rs b/src/compile/ir/tasks.rs index d966b302..59c9c0e6 100644 --- a/src/compile/ir/tasks.rs +++ b/src/compile/ir/tasks.rs @@ -11,6 +11,37 @@ use super::step::TaskStep; +/// Returns a [`TaskStep`] for `CopyFiles@2`. +/// +/// Copies files matching `contents` into `target_folder`. The optional +/// `source_folder` narrows the root from which the glob is evaluated; +/// when omitted ADO defaults to `$(Build.SourcesDirectory)`. +/// +/// Required inputs are positional parameters. Optional inputs (applied +/// via `.with_input(…)` on the returned value): +/// +/// | Input key | Type | Default | Description | +/// |---|---|---|---| +/// | `SourceFolder` | string | `$(Build.SourcesDirectory)` | Root for glob evaluation. | +/// | `CleanTargetFolder` | bool string | `"false"` | Delete target folder contents before copy. | +/// | `OverWrite` | bool string | `"false"` | Overwrite files in target folder. | +/// | `flattenFolders` | bool string | `"false"` | Flatten directory structure in target. | +/// | `preserveTimestamp` | bool string | `"false"` | Preserve source timestamps. | +/// | `retryCount` | string | `"0"` | Number of retry attempts on failure. | +/// | `delayBetweenRetries` | string | `"1000"` | Milliseconds between retries. | +/// | `ignoreMakeDirErrors` | bool string | `"false"` | Ignore errors when creating target folder. | +/// +/// ADO task reference: +/// +pub fn copy_files_step( + contents: impl Into, + target_folder: impl Into, +) -> TaskStep { + TaskStep::new("CopyFiles@2", "Copy Files") + .with_input("Contents", contents) + .with_input("TargetFolder", target_folder) +} + /// Returns a [`TaskStep`] for `DockerInstaller@0`. /// /// Installs a specific version of Docker Engine on the agent. @@ -68,6 +99,54 @@ pub fn publish_test_results_step( mod tests { use super::*; + // ── CopyFiles@2 ────────────────────────────────────────────────────── + + #[test] + fn copy_files_step_sets_task_and_required_inputs() { + let t = copy_files_step("**/*.rs", "$(Build.ArtifactStagingDirectory)"); + assert_eq!(t.task, "CopyFiles@2"); + assert_eq!(t.display_name, "Copy Files"); + assert_eq!(t.inputs.get("Contents").map(|s| s.as_str()), Some("**/*.rs")); + assert_eq!( + t.inputs.get("TargetFolder").map(|s| s.as_str()), + Some("$(Build.ArtifactStagingDirectory)") + ); + // no optional inputs by default + assert_eq!(t.inputs.len(), 2); + } + + #[test] + fn copy_files_step_accepts_source_folder_via_with_input() { + let t = copy_files_step("**", "$(Build.ArtifactStagingDirectory)") + .with_input("SourceFolder", "$(Build.SourcesDirectory)/src"); + assert_eq!(t.task, "CopyFiles@2"); + assert_eq!( + t.inputs.get("SourceFolder").map(|s| s.as_str()), + Some("$(Build.SourcesDirectory)/src") + ); + assert_eq!(t.inputs.len(), 3); + } + + #[test] + fn copy_files_step_accepts_optional_flags() { + let t = copy_files_step("**", "$(Build.ArtifactStagingDirectory)") + .with_input("CleanTargetFolder", "true") + .with_input("OverWrite", "true") + .with_input("flattenFolders", "true"); + assert_eq!( + t.inputs.get("CleanTargetFolder").map(|s| s.as_str()), + Some("true") + ); + assert_eq!(t.inputs.get("OverWrite").map(|s| s.as_str()), Some("true")); + assert_eq!( + t.inputs.get("flattenFolders").map(|s| s.as_str()), + Some("true") + ); + assert_eq!(t.inputs.len(), 5); + } + + // ── PublishTestResults@2 ───────────────────────────────────────────── + #[test] fn publish_test_results_step_sets_task_and_required_inputs() { let t = publish_test_results_step("JUnit", "**/TEST-*.xml");