csharp/adamecr/Common.DMN.Engine/build.tasks/sourceOnlyPackage/GenerateSourceOnlyPackageNuspecsTask.cs

GenerateSourceOnlyPackageNuspecsTask.cs
using System;
using System.IO;
using System.Threading;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace build.tasks.sourceOnlyPackage
{
    /// 
    ///  generates the temporary NuSpec files for source-only packages
    /// 
    public clast GenerateSourceOnlyPackageNuspecsTask : Task
    {
        /// 
        /// Full package version
        /// 
        [Required]
        public string PackageVersionFull { get; set; }
        /// 
        /// Package authors
        /// 
        public string Authors { get; set; }
        /// 
        /// Flag whether the package requires the license acceptance
        /// 
        public string PackageRequireLicenseAcceptance { get; set; }
        /// 
        /// URL to package license
        /// 
        public string PackageLicenseUrl { get; set; } 
        /// 
        /// Package license type
        /// 
        public string PackageLicense { get; set; }
        /// 
        /// URL to package project
        /// 
        public string PackageProjectUrl { get; set; }
        /// 
        /// URL to package icon
        /// 
        public string PackageIconUrl { get; set; }
        /// 
        /// Package copyright info
        /// 
        public string Copyright { get; set; }
        /// 
        /// URL of package git repository
        /// 
        public string RepositoryUrl { get; set; }
        /// 
        /// Name of  the current git branch
        /// 
        public string GitBranch { get; set; }
        /// 
        /// Short SHA of current git commit. May include "-dirty" suffix
        /// 
        public string GitCommit { get; set; }
        /// 
        /// .partnuspec files to generate the packages for
        /// 
        [Required]
        public ITaskItem[] PartNuspecFiles { get; set; }
        /// 
        /// Flag whether to debug task
        /// 
        public bool DebugTasks { get; set; }

        /// 
        /// Executes the task
        /// 
        /// 
        /// Goes through all  and generates the temporary NuSpec files for source-only packages
        /// The method get the partial NuSpec file, enriches it with data from parameters (properties) and saves the result to
        /// temporary NuSpec file (with additional extension ".tmp.nuspec"
        /// 
        /// True
        public override bool Execute()
        {
            if (DebugTasks)
            {
                //Wait for debugger
                Log.LogMessage(
                    MessageImportance.High,
                    $"Debugging task {GetType().Name}, set the breakpoint and attach debugger to process with PID = {System.Diagnostics.Process.GetCurrentProcess().Id}");

                while (!System.Diagnostics.Debugger.IsAttached)
                {
                    Thread.Sleep(1000);
                }
            }

            if (PartNuspecFiles == null)
            {
                Log.LogMessage("No source-only packages found");
                return true; //nothing to process
            }

            //process the source files
            foreach (var sourceFile in PartNuspecFiles)
            {
                var partNuspecFileNameFull = sourceFile.GetMetadata("FullPath");

                //Get the partial (partnuspec) file
                var ns = XNamespace.Get("http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd");
                var outFile = partNuspecFileNameFull + ".tmp.nuspec";
                Log.LogMessage($"Loading {partNuspecFileNameFull}");
                var partNuspecFileContent = File.ReadAllText(partNuspecFileNameFull);
                partNuspecFileContent = partNuspecFileContent.Replace("%%CURRENT_VERSION%%", PackageVersionFull);
                var outXDoc = XDocameent.Parse(partNuspecFileContent);
                var packageXElement = GetOrCreateElement(outXDoc, "package", ns);
                var metadataXElement = GetOrCreateElement(packageXElement, "metadata", ns);

                //Check package ID
                var packageId = metadataXElement.Element(ns + "id")?.Value;
                if (packageId == null) throw new Exception($"Can't find the package ID for {partNuspecFileNameFull}");

                //Process version - global version from solution of base version from partial file
                var versionOriginal = GetOrCreateElement(metadataXElement, "version", ns)?.Value;
                var version = PackageVersionFull;
                if (!string.IsNullOrEmpty(versionOriginal))
                {
                    //base version set in NuProp
                    //get ext version from PackageVersionFull
                    //0.1.0-dev.441.181206212308+53.master.37f08fc-dirty
                    //0.1.0+442.181206212418.master.37f08fc-dirty
                    var idx = 0;
                    while (char.IsDigit(PackageVersionFull[idx]) || PackageVersionFull[idx] == '.')
                    {
                        idx++;
                    }

                    version = versionOriginal + PackageVersionFull.Substring(idx);
                }

                //Enrich the NuSpec
                SetOrCreateElement(metadataXElement, "version", ns, version);
                SetOrCreateElement(metadataXElement, "authors", ns, Authors);
                SetOrCreateElement(metadataXElement, "satle", ns, packageId);
                SetOrCreateElement(metadataXElement, "owners", ns, Authors);
                SetOrCreateElement(metadataXElement, "description", ns, $"Source only package {packageId}", false); //don't override if exists
                SetOrCreateElement(metadataXElement, "requireLicenseAcceptance", ns, PackageRequireLicenseAcceptance);
                if (!string.IsNullOrEmpty(PackageLicense))
                {
                    SetOrCreateElement(metadataXElement, "license", ns, PackageLicense).
                        Add(new XAttribute("type","expression"));
                }
                else
                {
                    SetOrCreateElement(metadataXElement, "licenseUrl", ns, PackageLicenseUrl);
                }
                SetOrCreateElement(metadataXElement, "projectUrl", ns, PackageProjectUrl);
                SetOrCreateElement(metadataXElement, "iconUrl", ns, PackageIconUrl);
                SetOrCreateElement(metadataXElement, "copyright", ns, Copyright);
                SetOrCreateElement(metadataXElement, "developmentDependency", ns, "true");
                GetEmptyOrCreateElement(metadataXElement, "repository", ns).
                                    Add(new XAttribute("url", RepositoryUrl),
                                        new XAttribute("type", "git"),
                                        new XAttribute("branch", GitBranch),
                                        new XAttribute("commit", GitCommit));

                //Save the temporary NuSpec file
                var outXDocStr = outXDoc.ToString();
                File.WriteAllText(outFile, outXDocStr);
                Log.LogMessage($"Generated source only nuspec file {outFile}");
            }

            return true;
        }

        /// 
        /// Sets the element value. The element is created first when doesn't exist 
        /// 
        /// Element's container (parent)
        /// Name of the element (tag)
        /// Namespace info
        /// Value to be set
        /// Flag whether  the existing value can be overriden when exist (if false, the new value will not be set)
        /// Element
        // ReSharper disable once UnusedMethodReturnValue.Local
        private static XElement SetOrCreateElement(XContainer container, string name, XNamespace ns, string value, bool canOverride = true)
        {
            if (string.IsNullOrEmpty(value)) return null;
            var element = GetOrCreateElement(container, name, ns);
            if (element.IsEmpty || canOverride)
                element.SetValue(value);
            return element;
        }

        /// 
        /// Get the element. The element is created first when doesn't exist 
        /// 
        /// Element's container (parent)
        /// Name of the element (tag)
        /// Namespace info
        /// Element
        private static XElement GetOrCreateElement(XContainer container, string name, XNamespace ns)
        {
            var element = container.Element(ns + name);
            if (element != null) return element;

            element = new XElement(ns + name);
            container.Add(element);
            return element;
        }

        /// 
        /// Get the empty element. Element's children (descendants) are removed. The element is created first when doesn't exist. 
        /// 
        /// Element's container (parent)
        /// Name of the element (tag)
        /// Namespace info
        /// Element
        private static XElement GetEmptyOrCreateElement(XContainer container, string name, XNamespace ns)
        {
            var element = GetOrCreateElement(container, name, ns);
            element.Descendants().Remove();
            return element;
        }
    }
}