using System; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using AggregateConfigBuildTask.FileHandlers; using Microsoft.Build.Framework; using Task = Microsoft.Build.Utilities.Task; [assembly: InternalsVisibleTo("AggregateConfig.Tests.UnitTests")] namespace AggregateConfigBuildTask { /// /// Represents a task that aggregates configuration files from a directory and outputs a merged file with optional modifications. /// public class AggregateConfig : Task { private readonly IFileSystem fileSystem; private ITaskLogger logger; /// /// The directory path where input files are located. This property is required. /// [Required] public string InputDirectory { get; set; } /// /// The type of input file type to be processed from . If not specified, the default type is used. /// public string InputType { get; set; } /// /// The output file path where the merged result will be saved. This property is required. /// [Required] public string OutputFile { get; set; } /// /// The type of the output file type to be created from . This property is required. /// [Required] public string OutputType { get; set; } /// /// Specifies whether the source property (i.e., the file name) should be added to each merged object. /// public bool AddSourceProperty { get; set; } /// /// An array of additional properties that can be included in the output. These are user-specified key-value pairs. /// public ITaskItem[] AdditionalProperties { get; set; } /// /// Gets or sets whether quiet mode is enabled. When enabled, the logger will suppress non-critical messages. /// public bool IsQuietMode { get { return logger is QuietTaskLogger; } set { logger = value && !(logger is QuietTaskLogger) ? new QuietTaskLogger(Log) : logger; } } /// /// Initializes a new instance of the class with default file system and logger. /// public AggregateConfig() { this.fileSystem = new FileSystem(); this.logger = new TaskLogger(Log); } internal AggregateConfig(IFileSystem fileSystem, ITaskLogger logger) { this.fileSystem = fileSystem; this.logger = logger; } /// /// The entry point for the task. /// /// A boolean that is true if processing was successful. public override bool Execute() { try { EmitHeader(); InputDirectory = Path.GetFullPath(InputDirectory); OutputFile = Path.GetFullPath(OutputFile); if (!Enum.TryParse(OutputType, true, out FileType outputType) || !Enum.IsDefined(typeof(FileType), outputType)) { logger.LogError(message: "Invalid FileType: {0}. Available options: {1}", OutputType, string.Join(", ", Enum.GetNames(typeof(FileType)))); return false; } FileType inputType = FileType.Yaml; if (!string.IsNullOrEmpty(InputType) && (!Enum.TryParse(InputType, true, out inputType) || !Enum.IsDefined(typeof(FileType), inputType))) { logger.LogError(message: "Invalid FileType: {0}. Available options: {1}", InputType, string.Join(", ", Enum.GetNames(typeof(FileType)))); return false; } logger.LogMessage(MessageImportance.High, "Aggregating {0} to {1} in folder {2}", inputType, outputType, InputDirectory); string directoryPath = Path.GetDirectoryName(OutputFile); if (!fileSystem.DirectoryExists(directoryPath)) { fileSystem.CreateDirectory(directoryPath); } var finalResult = ObjectManager.MergeFileObjects(InputDirectory, inputType, AddSourceProperty, fileSystem, logger).GetAwaiter().GetResult(); if (finalResult == null) { logger.LogError(message: "No input was found! Check the input directory."); return false; } var additionalPropertiesDictionary = JsonHelper.ParseAdditionalProperties(AdditionalProperties); finalResult = ObjectManager.InjectAdditionalProperties(finalResult, additionalPropertiesDictionary, logger).GetAwaiter().GetResult(); var writer = FileHandlerFactory.GetFileHandlerForType(fileSystem, outputType); writer.WriteOutput(finalResult, OutputFile); logger.LogMessage(MessageImportance.High, "Wrote aggregated configuration file to {0}", OutputFile); return true; } catch (Exception ex) { logger.LogError(message: "An unknown exception occurred: {0}", ex.Message); logger.LogErrorFromException(ex, true, true, null); return false; } } private void EmitHeader() { var assembly = Assembly.GetExecutingAssembly(); var informationalVersion = assembly .GetCustomAttribute()? .InformationalVersion; logger.LogMessage(MessageImportance.Normal, $"AggregateConfig Version: {informationalVersion}"); } } }