diff --git a/src/ModularPipelines.Build/ModularPipelines.Build.csproj b/src/ModularPipelines.Build/ModularPipelines.Build.csproj index a329e88bda..d9cb5f2ee7 100644 --- a/src/ModularPipelines.Build/ModularPipelines.Build.csproj +++ b/src/ModularPipelines.Build/ModularPipelines.Build.csproj @@ -24,6 +24,7 @@ + PreserveNewest diff --git a/src/ModularPipelines.SourceGenerator/BuildMethodGenerator.cs b/src/ModularPipelines.SourceGenerator/BuildMethodGenerator.cs index 69e1f92bc0..c6df587dc7 100644 --- a/src/ModularPipelines.SourceGenerator/BuildMethodGenerator.cs +++ b/src/ModularPipelines.SourceGenerator/BuildMethodGenerator.cs @@ -4,8 +4,18 @@ namespace ModularPipelines.SourceGenerator; /// -/// Generates the Build() method for options classes. +/// Generates the BuildCommandLine() method for options classes. /// +/// +/// The method is named BuildCommandLine() instead of Build() to avoid conflicts with +/// options classes that have properties named "Build" (e.g., AptGetOptions has a Build +/// property for the --build flag). In C#, a property and parameterless method cannot +/// share the same name. +/// +/// This method is internal infrastructure called by ICommandLineBuilder - users don't +/// call it directly on options instances. They pass options to command methods like +/// context.Git().Checkout(options). +/// internal static class BuildMethodGenerator { /// @@ -19,7 +29,7 @@ internal static class BuildMethodGenerator private const string GeneratorVersion = "1.0.0"; /// - /// Generates the source code for the Build() method. + /// Generates the source code for the BuildCommandLine() method. /// /// The options class information. /// The generated source code. @@ -46,7 +56,7 @@ public static string Generate(OptionsClassInfo info) sb.AppendLine(" /// "); sb.AppendLine(" /// Builds the command line arguments from this options instance."); sb.AppendLine(" /// "); - sb.AppendLine(" public ModularPipelines.Models.CommandLine Build()"); + sb.AppendLine(" public ModularPipelines.Models.CommandLine BuildCommandLine()"); sb.AppendLine(" {"); sb.AppendLine(" var args = new System.Collections.Generic.List();"); diff --git a/src/ModularPipelines.SourceGenerator/CommandOptionsGenerator.cs b/src/ModularPipelines.SourceGenerator/CommandOptionsGenerator.cs index d327a155f3..8ea7fa02c1 100644 --- a/src/ModularPipelines.SourceGenerator/CommandOptionsGenerator.cs +++ b/src/ModularPipelines.SourceGenerator/CommandOptionsGenerator.cs @@ -8,7 +8,7 @@ namespace ModularPipelines.SourceGenerator; /// /// Source generator that validates CommandLineToolOptions classes -/// and generates optimized Build() methods. +/// and generates optimized BuildCommandLine() methods. /// [Generator] public sealed class CommandOptionsGenerator : IIncrementalGenerator @@ -237,7 +237,7 @@ private static void GenerateCode(SourceProductionContext context, OptionsClassIn propNames, group.Key)); } - // Generate Build() method + // Generate BuildCommandLine() method var source = BuildMethodGenerator.Generate(info); context.AddSource($"{info.ClassName}.g.cs", source); } diff --git a/src/ModularPipelines.SourceGenerator/ModuleClassInfo.cs b/src/ModularPipelines.SourceGenerator/ModuleClassInfo.cs new file mode 100644 index 0000000000..8284c374a1 --- /dev/null +++ b/src/ModularPipelines.SourceGenerator/ModuleClassInfo.cs @@ -0,0 +1,14 @@ +using Microsoft.CodeAnalysis; + +namespace ModularPipelines.SourceGenerator; + +/// +/// Information about a Module class discovered by the generator. +/// +internal sealed record ModuleClassInfo( + string Namespace, + string ClassName, + string ResultTypeName, + string ResultTypeFullName, + Location Location +); diff --git a/src/ModularPipelines.SourceGenerator/ModuleExtensionsGenerator.cs b/src/ModularPipelines.SourceGenerator/ModuleExtensionsGenerator.cs new file mode 100644 index 0000000000..fbee419fd6 --- /dev/null +++ b/src/ModularPipelines.SourceGenerator/ModuleExtensionsGenerator.cs @@ -0,0 +1,233 @@ +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace ModularPipelines.SourceGenerator; + +/// +/// Source generator that creates type-safe GetModule extension methods for Module classes. +/// For each class that inherits from Module<T>, generates: +/// - GetXxxModuleResult(this IModuleContext context) extension method (strips "Module" suffix) +/// - GetXxxModuleResultIfRegistered(this IModuleContext context) extension method +/// +[Generator] +public sealed class ModuleExtensionsGenerator : IIncrementalGenerator +{ + /// + /// The fully qualified name of the Module<T> base class. + /// + internal const string ModuleBaseFullName = "ModularPipelines.Modules.Module`1"; + + private const string GeneratorName = "ModularPipelines.SourceGenerator"; + private const string GeneratorVersion = "1.0.0"; + + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Create syntax provider that finds class declarations with base types + var moduleClasses = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: static (node, _) => IsCandidate(node), + transform: static (ctx, _) => GetModuleClassInfo(ctx)) + .Where(static info => info is not null) + .Select(static (info, _) => info!); + + // Collect all modules and generate a single extensions file + var collectedModules = moduleClasses.Collect(); + context.RegisterSourceOutput(collectedModules, static (ctx, modules) => GenerateExtensions(ctx, modules)); + } + + /// + /// Checks if a syntax node is a potential candidate for module discovery. + /// Returns true for class declarations with base types. + /// + private static bool IsCandidate(SyntaxNode node) + { + return node is ClassDeclarationSyntax classDeclaration && + classDeclaration.BaseList != null && + classDeclaration.BaseList.Types.Count > 0; + } + + /// + /// Extracts ModuleClassInfo from a type declaration if it inherits from Module<T>. + /// + private static ModuleClassInfo? GetModuleClassInfo(GeneratorSyntaxContext context) + { + var classDeclaration = (ClassDeclarationSyntax)context.Node; + var semanticModel = context.SemanticModel; + + // Get the declared symbol for this type + if (semanticModel.GetDeclaredSymbol(classDeclaration) is not INamedTypeSymbol typeSymbol) + { + return null; + } + + // Skip abstract classes - they can't be instantiated as modules + if (typeSymbol.IsAbstract) + { + return null; + } + + // Check if this type inherits from Module and get the result type + var resultType = GetModuleResultType(typeSymbol, semanticModel.Compilation); + if (resultType is null) + { + return null; + } + + // Extract namespace + var namespaceName = typeSymbol.ContainingNamespace.IsGlobalNamespace + ? string.Empty + : typeSymbol.ContainingNamespace.ToDisplayString(); + + // Get type names for code generation + var resultTypeName = resultType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + var resultTypeFullName = resultType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + return new ModuleClassInfo( + Namespace: namespaceName, + ClassName: typeSymbol.Name, + ResultTypeName: resultTypeName, + ResultTypeFullName: resultTypeFullName, + Location: classDeclaration.Identifier.GetLocation() + ); + } + + /// + /// Gets the result type T from Module<T> if the type inherits from it. + /// + private static ITypeSymbol? GetModuleResultType(INamedTypeSymbol type, Compilation compilation) + { + var moduleBaseType = compilation.GetTypeByMetadataName(ModuleBaseFullName); + if (moduleBaseType is null) + { + return null; + } + + var current = type.BaseType; + while (current is not null) + { + if (current.IsGenericType && + SymbolEqualityComparer.Default.Equals(current.OriginalDefinition, moduleBaseType)) + { + return current.TypeArguments[0]; + } + + current = current.BaseType; + } + + return null; + } + + /// + /// Generates the extension methods file containing all module accessors. + /// + private static void GenerateExtensions(SourceProductionContext context, ImmutableArray modules) + { + if (modules.IsEmpty) + { + return; + } + + var sb = new StringBuilder(); + sb.AppendLine("// "); + sb.AppendLine("#nullable enable"); + sb.AppendLine(); + sb.AppendLine("using System.CodeDom.Compiler;"); + sb.AppendLine("using ModularPipelines.Context;"); + sb.AppendLine("using ModularPipelines.Models;"); + sb.AppendLine("using ModularPipelines.Modules;"); + sb.AppendLine(); + + // Add using statements for module namespaces + var distinctNamespaces = modules + .Select(m => m.Namespace) + .Where(ns => !string.IsNullOrEmpty(ns)) + .Distinct() + .OrderBy(ns => ns); + + foreach (var ns in distinctNamespaces) + { + sb.AppendLine($"using {ns};"); + } + + sb.AppendLine(); + sb.AppendLine("namespace ModularPipelines.Generated;"); + sb.AppendLine(); + sb.AppendLine("/// "); + sb.AppendLine("/// Type-safe extension methods for retrieving module results."); + sb.AppendLine("/// "); + sb.AppendLine($"[GeneratedCode(\"{GeneratorName}\", \"{GeneratorVersion}\")]"); + sb.AppendLine("public static class ModuleContextExtensions"); + sb.AppendLine("{"); + + // Group modules by class name to handle potential duplicates + var modulesByName = modules.GroupBy(m => m.ClassName).ToList(); + + foreach (var moduleGroup in modulesByName) + { + var module = moduleGroup.First(); + var methodName = StripModuleSuffix(module.ClassName); + + // GetXxxModuleResult method - returns non-nullable (e.g., GetBuildModuleResult for BuildModule) + sb.AppendLine($" /// "); + sb.AppendLine($" /// Gets the result of ."); + sb.AppendLine($" /// "); + sb.AppendLine($" /// The module context."); + sb.AppendLine($" /// The module result."); + sb.AppendLine($" /// Thrown when the module is not registered."); + sb.AppendLine($" public static ModuleResult<{module.ResultTypeFullName}> Get{methodName}ModuleResult(this IModuleContext context)"); + sb.AppendLine($" => context.GetModule<{module.ClassName}, {module.ResultTypeFullName}>();"); + sb.AppendLine(); + + // GetXxxModuleResultIfRegistered method - returns nullable + sb.AppendLine($" /// "); + sb.AppendLine($" /// Gets the result of if it is registered, otherwise null."); + sb.AppendLine($" /// "); + sb.AppendLine($" /// The module context."); + sb.AppendLine($" /// The module result, or null if not registered."); + sb.AppendLine($" public static ModuleResult<{module.ResultTypeFullName}>? Get{methodName}ModuleResultIfRegistered(this IModuleContext context)"); + sb.AppendLine($" => context.GetModuleIfRegistered<{module.ClassName}, {module.ResultTypeFullName}>();"); + sb.AppendLine(); + } + + sb.AppendLine("}"); + + context.AddSource("ModuleContextExtensions.g.cs", sb.ToString()); + } + + /// + /// Escapes a string for use in XML documentation comments. + /// + private static string EscapeXmlComment(string text) + { + return text.Replace("&", "&").Replace("<", "<").Replace(">", ">"); + } + + /// + /// Strips the "Module" suffix from a class name to create a cleaner method name. + /// + /// + /// Examples: + /// - "BuildModule" → "Build" (generates GetBuildModuleResult) + /// - "DeployToProduction" → "DeployToProduction" (generates GetDeployToProductionModuleResult) + /// - "Module" → "Module" (edge case: keeps name to avoid empty string) + /// + /// The length check (className.Length > suffix.Length) ensures that a class named + /// exactly "Module" won't be stripped to an empty string. + /// + private static string StripModuleSuffix(string className) + { + const string suffix = "Module"; + + // Only strip if there's actual content before "Module" (prevents "Module" → "") + if (className.Length > suffix.Length && className.EndsWith(suffix)) + { + return className.Substring(0, className.Length - suffix.Length); + } + + return className; + } +} diff --git a/src/ModularPipelines/ModularPipelines.csproj b/src/ModularPipelines/ModularPipelines.csproj index 0dca77833f..f895cac18d 100644 --- a/src/ModularPipelines/ModularPipelines.csproj +++ b/src/ModularPipelines/ModularPipelines.csproj @@ -78,5 +78,15 @@ + + + + $(TargetsForTfmSpecificContentInPackage);_AddSourceGeneratorToPackage + + + + + + \ No newline at end of file diff --git a/src/ModularPipelines/Options/BashCommandOptions.cs b/src/ModularPipelines/Options/BashCommandOptions.cs index be2ec33cb9..19adf3eb12 100644 --- a/src/ModularPipelines/Options/BashCommandOptions.cs +++ b/src/ModularPipelines/Options/BashCommandOptions.cs @@ -8,4 +8,4 @@ namespace ModularPipelines.Options; /// /// The bash command to execute. [ExcludeFromCodeCoverage] -public record BashCommandOptions([property: CliOption("-c")] string Command) : BashOptions; \ No newline at end of file +public partial record BashCommandOptions([property: CliOption("-c")] string Command) : BashOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/BashFileOptions.cs b/src/ModularPipelines/Options/BashFileOptions.cs index 0ccc425dc0..e8a4d2a884 100644 --- a/src/ModularPipelines/Options/BashFileOptions.cs +++ b/src/ModularPipelines/Options/BashFileOptions.cs @@ -4,4 +4,4 @@ namespace ModularPipelines.Options; [ExcludeFromCodeCoverage] -public record BashFileOptions([property: CliArgument(Placement = ArgumentPlacement.BeforeOptions)] string FilePath) : BashOptions; \ No newline at end of file +public partial record BashFileOptions([property: CliArgument(Placement = ArgumentPlacement.BeforeOptions)] string FilePath) : BashOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/BashOptions.cs b/src/ModularPipelines/Options/BashOptions.cs index 5b3e38fe2b..5895bc0b9b 100644 --- a/src/ModularPipelines/Options/BashOptions.cs +++ b/src/ModularPipelines/Options/BashOptions.cs @@ -8,4 +8,4 @@ namespace ModularPipelines.Options; /// [ExcludeFromCodeCoverage] [CliTool("bash")] -public record BashOptions : CommandLineToolOptions; \ No newline at end of file +public partial record BashOptions : CommandLineToolOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetAutocleanOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetAutocleanOptions.cs index f4720cad19..b6acac965b 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetAutocleanOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetAutocleanOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetAutocleanOptions : AptGetOptions +public partial record AptGetAutocleanOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "autoclean"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetBuildDepOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetBuildDepOptions.cs index d3a1af6c9e..92b81580c9 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetBuildDepOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetBuildDepOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetBuildDepOptions : AptGetOptions +public partial record AptGetBuildDepOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "build-dep"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetCheckOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetCheckOptions.cs index 7585117bab..9d2dc6ca7c 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetCheckOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetCheckOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetCheckOptions : AptGetOptions +public partial record AptGetCheckOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "check"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetCleanOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetCleanOptions.cs index fbba5ac224..a6e640f291 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetCleanOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetCleanOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetCleanOptions : AptGetOptions +public partial record AptGetCleanOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "clean"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetDistUpgradeOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetDistUpgradeOptions.cs index b5b62ee94e..66527c50ce 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetDistUpgradeOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetDistUpgradeOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetDistUpgradeOptions : AptGetOptions +public partial record AptGetDistUpgradeOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "dist-upgrade"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetInstallOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetInstallOptions.cs index d312654e1e..6c91edaafa 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetInstallOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetInstallOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetInstallOptions : AptGetOptions +public partial record AptGetInstallOptions : AptGetOptions { /// /// Initializes a new instance of the class diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetOptions.cs index bce98187b9..ddf01de239 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetOptions.cs @@ -5,7 +5,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] [CliTool("apt-get")] -public record AptGetOptions : CommandLineToolOptions +public partial record AptGetOptions : CommandLineToolOptions { [CliFlag("--download-only")] diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetPackageOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetPackageOptions.cs index 8ca48cad96..92b821cc52 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetPackageOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetPackageOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetPackageOptions : AptGetOptions +public partial record AptGetPackageOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "package"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetRemoveOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetRemoveOptions.cs index 7557bbda38..64ad29fd22 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetRemoveOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetRemoveOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetRemoveOptions : AptGetOptions +public partial record AptGetRemoveOptions : AptGetOptions { public AptGetRemoveOptions(string package) { diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetSourceOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetSourceOptions.cs index d1688e1bd0..c7d273e99b 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetSourceOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetSourceOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetSourceOptions : AptGetOptions +public partial record AptGetSourceOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "source"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetUpdateOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetUpdateOptions.cs index 21f5b07229..bc8a30f988 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetUpdateOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetUpdateOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetUpdateOptions : AptGetOptions +public partial record AptGetUpdateOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "update"; diff --git a/src/ModularPipelines/Options/Linux/AptGet/AptGetUpgradeOptions.cs b/src/ModularPipelines/Options/Linux/AptGet/AptGetUpgradeOptions.cs index d90aa83573..c13d94f64e 100644 --- a/src/ModularPipelines/Options/Linux/AptGet/AptGetUpgradeOptions.cs +++ b/src/ModularPipelines/Options/Linux/AptGet/AptGetUpgradeOptions.cs @@ -4,7 +4,7 @@ namespace ModularPipelines.Options.Linux.AptGet; [ExcludeFromCodeCoverage] -public record AptGetUpgradeOptions : AptGetOptions +public partial record AptGetUpgradeOptions : AptGetOptions { [CliArgument(Placement = ArgumentPlacement.AfterOptions)] public virtual string CommandName { get; } = "upgrade"; diff --git a/src/ModularPipelines/Options/Linux/DpkgInstallOptions.cs b/src/ModularPipelines/Options/Linux/DpkgInstallOptions.cs index ae9739e4de..2950f6d5ea 100644 --- a/src/ModularPipelines/Options/Linux/DpkgInstallOptions.cs +++ b/src/ModularPipelines/Options/Linux/DpkgInstallOptions.cs @@ -5,7 +5,7 @@ namespace ModularPipelines.Options.Linux; [ExcludeFromCodeCoverage] [CliTool("dpkg")] -public record DpkgInstallOptions : CommandLineToolOptions +public partial record DpkgInstallOptions : CommandLineToolOptions { public DpkgInstallOptions(string path) { diff --git a/src/ModularPipelines/Options/Mac/MacBrewOptions.cs b/src/ModularPipelines/Options/Mac/MacBrewOptions.cs index 618cdaac74..5a896a4880 100644 --- a/src/ModularPipelines/Options/Mac/MacBrewOptions.cs +++ b/src/ModularPipelines/Options/Mac/MacBrewOptions.cs @@ -5,4 +5,4 @@ namespace ModularPipelines.Options.Mac; [ExcludeFromCodeCoverage] [CliTool("brew")] -public record MacBrewOptions([property: CliOption("--cask")] string PackageName) : CommandLineToolOptions; \ No newline at end of file +public partial record MacBrewOptions([property: CliOption("--cask")] string PackageName) : CommandLineToolOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/PowershellFileOptions.cs b/src/ModularPipelines/Options/PowershellFileOptions.cs index fd0ec7fd57..e1adec92be 100644 --- a/src/ModularPipelines/Options/PowershellFileOptions.cs +++ b/src/ModularPipelines/Options/PowershellFileOptions.cs @@ -8,4 +8,4 @@ namespace ModularPipelines.Options; /// /// The path to the PowerShell script file to execute. [ExcludeFromCodeCoverage] -public record PowershellFileOptions([property: CliOption("-File")] string FilePath) : PowershellOptions; \ No newline at end of file +public partial record PowershellFileOptions([property: CliOption("-File")] string FilePath) : PowershellOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/PowershellOptions.cs b/src/ModularPipelines/Options/PowershellOptions.cs index e744885e61..f82aee01e8 100644 --- a/src/ModularPipelines/Options/PowershellOptions.cs +++ b/src/ModularPipelines/Options/PowershellOptions.cs @@ -6,4 +6,4 @@ namespace ModularPipelines.Options; /// Options for executing PowerShell commands using the pwsh executable. /// [CliTool("pwsh")] -public record PowershellOptions : CommandLineToolOptions; \ No newline at end of file +public partial record PowershellOptions : CommandLineToolOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/PowershellScriptOptions.cs b/src/ModularPipelines/Options/PowershellScriptOptions.cs index 6439221867..b5986dc79a 100644 --- a/src/ModularPipelines/Options/PowershellScriptOptions.cs +++ b/src/ModularPipelines/Options/PowershellScriptOptions.cs @@ -2,4 +2,4 @@ namespace ModularPipelines.Options; -public record PowershellScriptOptions([property: CliOption("-Command")] string Script) : PowershellOptions; \ No newline at end of file +public partial record PowershellScriptOptions([property: CliOption("-Command")] string Script) : PowershellOptions; \ No newline at end of file diff --git a/src/ModularPipelines/Options/Windows/MsiInstallerOptions.cs b/src/ModularPipelines/Options/Windows/MsiInstallerOptions.cs index 4fbd6f7dd2..f78795a5cf 100644 --- a/src/ModularPipelines/Options/Windows/MsiInstallerOptions.cs +++ b/src/ModularPipelines/Options/Windows/MsiInstallerOptions.cs @@ -8,4 +8,4 @@ namespace ModularPipelines.Options.Windows; /// [ExcludeFromCodeCoverage] [CliTool("msiexec.exe")] -public record MsiInstallerOptions([property: CliOption("/package")] string MsiPath) : WindowsInstallerOptionsBase; +public partial record MsiInstallerOptions([property: CliOption("/package")] string MsiPath) : WindowsInstallerOptionsBase;