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;
}
}
}