From 8f3d9bb188b85b44034e80593f55f2c7c5278789 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 18:02:43 +0000 Subject: [PATCH] feat(ir): add typed helper for PublishPipelineArtifact@1 Adds `publish_pipeline_artifact_step()` to `src/compile/ir/tasks.rs`. The helper accepts the required `targetPath` as a positional parameter and exposes all optional inputs (artifact name, publishLocation, fileSharePath, parallel copy flags, custom properties) via the existing `.with_input()` builder on `TaskStep`. Four unit tests cover: - Default construction (task ID, display name, required input only) - Optional artifact name - Optional publishLocation - File share path variant Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/compile/ir/tasks.rs | 88 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/compile/ir/tasks.rs b/src/compile/ir/tasks.rs index bdc75f7c..bbfd9790 100644 --- a/src/compile/ir/tasks.rs +++ b/src/compile/ir/tasks.rs @@ -291,6 +291,37 @@ pub fn powershell_inline_step(script: impl Into) -> TaskStep { .with_input("script", script) } +/// Returns a [`TaskStep`] for `PublishPipelineArtifact@1`. +/// +/// Publishes (uploads) a file or directory as a named artifact for the +/// current pipeline run. The artifact is stored in Azure Pipelines and +/// can be downloaded by subsequent jobs or pipelines via +/// `DownloadPipelineArtifact@2`. +/// +/// - `target_path` — path of the file or directory to publish. Can be +/// absolute or relative to the default working directory. Supports +/// ADO macro variables (e.g. `$(Build.ArtifactStagingDirectory)`), +/// but wildcards are **not** supported. +/// +/// Optional inputs (applied with `.with_input(…)` on the returned +/// value): +/// +/// | Input key | Alias | Type | Default | Description | +/// |---|---|---|---|---| +/// | `artifact` | `artifactName` | string | *(unique job-scoped ID)* | Name of the published artifact (e.g. `"drop"`). May not contain `\`, `/`, `"`, `:`, `<`, `>`, `\|`, `*`, or `?`. | +/// | `publishLocation` | `artifactType` | string | `"pipeline"` | Where to store the artifact: `"pipeline"` (Azure Pipelines) or `"filepath"` (a UNC file share). | +/// | `fileSharePath` | — | string | — | Required when `publishLocation = filepath`. UNC path of the file share. | +/// | `parallel` | — | bool string | `"false"` | Enable multi-threaded copy when `publishLocation = filepath`. | +/// | `parallelCount` | — | string | `"8"` | Thread count for parallel copy (1–128). Applies when `parallel = true`. | +/// | `properties` | — | string | — | JSON string of custom properties to associate with the artifact (keys must start with `user-`). | +/// +/// ADO task reference: +/// +pub fn publish_pipeline_artifact_step(target_path: impl Into) -> TaskStep { + TaskStep::new("PublishPipelineArtifact@1", "Publish Pipeline Artifact") + .with_input("targetPath", target_path) +} + #[cfg(test)] mod tests { use super::*; @@ -841,4 +872,61 @@ mod tests { Some("$version = Get-Content VERSION\nWrite-Host \"Building version $version\"") ); } + + // ── PublishPipelineArtifact@1 ───────────────────────────────────────── + + #[test] + fn publish_pipeline_artifact_step_sets_task_and_target_path() { + let t = publish_pipeline_artifact_step("$(Build.ArtifactStagingDirectory)"); + assert_eq!(t.task, "PublishPipelineArtifact@1"); + assert_eq!(t.display_name, "Publish Pipeline Artifact"); + assert_eq!( + t.inputs.get("targetPath").map(|s| s.as_str()), + Some("$(Build.ArtifactStagingDirectory)") + ); + // only the required input is set by default + assert_eq!(t.inputs.len(), 1); + } + + #[test] + fn publish_pipeline_artifact_step_accepts_artifact_name() { + let t = publish_pipeline_artifact_step("$(Build.ArtifactStagingDirectory)/output") + .with_input("artifact", "drop"); + assert_eq!(t.task, "PublishPipelineArtifact@1"); + assert_eq!( + t.inputs.get("artifact").map(|s| s.as_str()), + Some("drop") + ); + assert_eq!(t.inputs.len(), 2); + } + + #[test] + fn publish_pipeline_artifact_step_accepts_publish_location() { + let t = publish_pipeline_artifact_step("$(Build.ArtifactStagingDirectory)") + .with_input("artifact", "binaries") + .with_input("publishLocation", "pipeline"); + assert_eq!(t.task, "PublishPipelineArtifact@1"); + assert_eq!( + t.inputs.get("publishLocation").map(|s| s.as_str()), + Some("pipeline") + ); + assert_eq!(t.inputs.len(), 3); + } + + #[test] + fn publish_pipeline_artifact_step_accepts_file_share_path() { + let t = publish_pipeline_artifact_step("$(Build.ArtifactStagingDirectory)") + .with_input("publishLocation", "filepath") + .with_input("fileSharePath", "\\\\myserver\\share\\$(Build.DefinitionName)"); + assert_eq!(t.task, "PublishPipelineArtifact@1"); + assert_eq!( + t.inputs.get("publishLocation").map(|s| s.as_str()), + Some("filepath") + ); + assert_eq!( + t.inputs.get("fileSharePath").map(|s| s.as_str()), + Some("\\\\myserver\\share\\$(Build.DefinitionName)") + ); + assert_eq!(t.inputs.len(), 3); + } }