From 849dc965c1358e090ecdf1f6355311223fc30a0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:53:42 +0000 Subject: [PATCH] feat(ir): add typed helper for MSBuild@1 Adds msbuild_step() factory function to src/compile/ir/tasks.rs for the MSBuild@1 ADO built-in task. The helper accepts the required solution path as a positional parameter and exposes all optional inputs (platform, configuration, msbuildArguments, clean, maximumCpuCount, restoreNugetPackages, msbuildLocationMethod, msbuildVersion, msbuildArchitecture, msbuildLocation, logProjectEvents, createLogFile, logFileVerbosity) via .with_input() on the returned TaskStep. Six unit tests cover: required-only construction, platform/configuration, msbuildArguments, optional boolean flags, explicit msbuild location, and .csproj project targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/compile/ir/tasks.rs | 136 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/src/compile/ir/tasks.rs b/src/compile/ir/tasks.rs index 40bd0bc2..4e09f8ea 100644 --- a/src/compile/ir/tasks.rs +++ b/src/compile/ir/tasks.rs @@ -537,6 +537,40 @@ pub fn docker_logout_step() -> TaskStep { TaskStep::new("Docker@2", "Docker Logout").with_input("command", "logout") } +/// Returns a [`TaskStep`] for `MSBuild@1`. +/// +/// Builds a Visual Studio solution or MSBuild project file using the +/// Microsoft Build Engine. Use this for .NET Framework projects and +/// solutions; for cross-platform .NET (Core / 5+) prefer +/// [`dot_net_core_cli_step`]. +/// +/// The `solution` parameter is the path or glob to the `.sln` or `.*proj` +/// file (e.g. `"**/*.sln"`, `"src/MyApp/MyApp.csproj"`). +/// +/// Optional inputs (applied via `.with_input(…)` on the returned value): +/// +/// | Input key | Type | Default | Description | +/// |---|---|---|---| +/// | `platform` | string | — | Target platform, e.g. `"x64"`, `"x86"`, `"AnyCPU"`. | +/// | `configuration` | string | — | Build configuration, e.g. `"Release"`, `"Debug"`. | +/// | `msbuildArguments` | string | — | Additional MSBuild command-line arguments. | +/// | `clean` | bool string | `"false"` | Run a clean build (`/target:clean`). | +/// | `maximumCpuCount` | bool string | `"false"` | Enable parallel builds (`/m`). | +/// | `restoreNugetPackages` | bool string | `"false"` | Restore NuGet packages before building. | +/// | `msbuildLocationMethod` | string | `"version"` | `"version"` or `"location"`. | +/// | `msbuildVersion` | string | `"latest"` | MSBuild version when `msbuildLocationMethod = version`. | +/// | `msbuildArchitecture` | string | `"x86"` | MSBuild architecture when `msbuildLocationMethod = version`. | +/// | `msbuildLocation` | string | — | Explicit MSBuild path when `msbuildLocationMethod = location`. | +/// | `logProjectEvents` | bool string | `"false"` | Record detailed project events. | +/// | `createLogFile` | bool string | `"false"` | Create a log file. | +/// | `logFileVerbosity` | string | `"normal"` | Log verbosity when `createLogFile = true`. | +/// +/// ADO task reference: +/// +pub fn msbuild_step(solution: impl Into) -> TaskStep { + TaskStep::new("MSBuild@1", "Build Solution").with_input("solution", solution) +} + #[cfg(test)] mod tests { use super::*; @@ -1588,4 +1622,106 @@ assert_eq!(t.inputs.len(), 1); "login and logout must use different command values" ); } + + // ── MSBuild@1 ──────────────────────────────────────────────────────── + + #[test] + fn msbuild_step_sets_task_and_required_input() { + let t = msbuild_step("**/*.sln"); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!(t.display_name, "Build Solution"); + assert_eq!( + t.inputs.get("solution").map(|s| s.as_str()), + Some("**/*.sln") + ); + // only the required input is set by default + assert_eq!(t.inputs.len(), 1); + } + + #[test] + fn msbuild_step_accepts_platform_and_configuration() { + let t = msbuild_step("src/MyApp/MyApp.sln") + .with_input("platform", "x64") + .with_input("configuration", "Release"); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!( + t.inputs.get("solution").map(|s| s.as_str()), + Some("src/MyApp/MyApp.sln") + ); + assert_eq!(t.inputs.get("platform").map(|s| s.as_str()), Some("x64")); + assert_eq!( + t.inputs.get("configuration").map(|s| s.as_str()), + Some("Release") + ); + assert_eq!(t.inputs.len(), 3); + } + + #[test] + fn msbuild_step_accepts_msbuild_arguments() { + let t = msbuild_step("**/*.sln").with_input( + "msbuildArguments", + "/p:DeployOnBuild=true /p:WebPublishMethod=Package", + ); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!( + t.inputs.get("msbuildArguments").map(|s| s.as_str()), + Some("/p:DeployOnBuild=true /p:WebPublishMethod=Package") + ); + assert_eq!(t.inputs.len(), 2); + } + + #[test] + fn msbuild_step_accepts_optional_flags() { + let t = msbuild_step("**/*.sln") + .with_input("clean", "true") + .with_input("maximumCpuCount", "true") + .with_input("restoreNugetPackages", "true"); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!(t.inputs.get("clean").map(|s| s.as_str()), Some("true")); + assert_eq!( + t.inputs.get("maximumCpuCount").map(|s| s.as_str()), + Some("true") + ); + assert_eq!( + t.inputs.get("restoreNugetPackages").map(|s| s.as_str()), + Some("true") + ); + assert_eq!(t.inputs.len(), 4); + } + + #[test] + fn msbuild_step_accepts_explicit_msbuild_location() { + let t = msbuild_step("**/*.sln") + .with_input("msbuildLocationMethod", "location") + .with_input("msbuildLocation", "C:\\Program Files\\MSBuild\\17.0\\Bin\\msbuild.exe"); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!( + t.inputs.get("msbuildLocationMethod").map(|s| s.as_str()), + Some("location") + ); + assert_eq!( + t.inputs + .get("msbuildLocation") + .map(|s| s.as_str()), + Some("C:\\Program Files\\MSBuild\\17.0\\Bin\\msbuild.exe") + ); + assert_eq!(t.inputs.len(), 3); + } + + #[test] + fn msbuild_step_accepts_csproj_target() { + let t = msbuild_step("src/MyLib/MyLib.csproj") + .with_input("platform", "AnyCPU") + .with_input("configuration", "Debug"); + assert_eq!(t.task, "MSBuild@1"); + assert_eq!( + t.inputs.get("solution").map(|s| s.as_str()), + Some("src/MyLib/MyLib.csproj") + ); + assert_eq!( + t.inputs.get("platform").map(|s| s.as_str()), + Some("AnyCPU") + ); + assert_eq!(t.inputs.len(), 3); + } }