Skip to content
This repository was archived by the owner on Jul 14, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/CBT.NuGet.AggregatePackage/CBT.NuGet.AggregatePackage.nuproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Configuration">
<Description>Provides ability to aggregate nuget packages.</Description>
<NoPackageAnalysis>true</NoPackageAnalysis>
<ProjectGuid>525e50eb-c6d9-497d-bc54-c625ab62b7d0</ProjectGuid>
<Tags>CBT Module NuGet Aggregate Packages</Tags>
<Title>CBT NuGet Aggregate Package Restore</Title>
</PropertyGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory).., build.props))\build.props" />
<Import Project="$(NuProjPath)\NuProj.props" Condition="Exists('$(NuProjPath)\NuProj.props')" />
<ItemGroup>
<Dependency Include="CBT.Nuget">
<Version>[1.0,)</Version>
</Dependency>
<Content Include="CBT\*\**" />
<None Include="version.json" />
</ItemGroup>
<Import Project="$(NuProjPath)\NuProj.targets" Condition="Exists('$(NuProjPath)\NuProj.targets')" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<Import Project="$(MSBuildThisFileDirectory)\CBT.Nuget.AggregatePackage.props"/>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" InitialTargets="NuGetRestoreGlobalPackages">

<PropertyGroup>
<RestoreNuGetPackagesDependsOn>$(RestoreNuGetPackagesDependsOn);AggregateNugetPackages</RestoreNuGetPackagesDependsOn>
</PropertyGroup>
<UsingTask AssemblyFile="$(CBTNuGetTasksAssemblyPath)" TaskName="CBT.NuGet.Tasks.AggregatePackages" />

<Target Name="AggregateNugetPackages"
Condition=" '$(CBTAggregatePackage)' != '' And '$(CBTEnableAggregatePackageRestore)' == 'true'"
Inputs="$(MSBuildAllProjects)"
Outputs="$(CBTNuGetAggregatePackagePropertyFile)">

<AggregatePackages
AggregateDestinationRoot="$(CBTAggregateDestPackageRoot)"
PackagesToAggregate="$(CBTAggregatePackage)"
PropsFile="$(CBTNuGetAggregatePackagePropertyFile)"
ImmutableRoots="$(CBTNugetAggregatePackageImmutableRoots)"
/>

</Target>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
</PropertyGroup>

<Import Project="$(CBTLocalBuildExtensionsPath)\Before.$(MSBuildThisFile)" Condition=" '$(CBTLocalBuildExtensionsPath)' != '' And Exists('$(CBTLocalBuildExtensionsPath)\Before.$(MSBuildThisFile)') " />
<Import Project="$(CBTModuleExtensionsPath)\Before.$(MSBuildThisFile)" Condition=" '$(CBTModuleExtensionsPath)' != '' And Exists('$(CBTModuleExtensionsPath)\Before.$(MSBuildThisFile)') " />

<PropertyGroup>
<CBTEnableAggregatePackageRestore Condition=" '$(CBTEnableAggregatePackageRestore)' == '' ">true</CBTEnableAggregatePackageRestore>
<CBTAggregatePackageImport Condition=" '$(CBTAggregatePackageImport)'=='' and Exists('$(MSBuildProjectDirectory)\CBT.AggregatePackages.props')">$(MSBuildProjectDirectory)\CBT.AggregatePackages.props</CBTAggregatePackageImport>
<CBTAggregateDestPackageRoot Condition=" '$(CBTAggregateDestPackageRoot)'=='' ">$(NuGetPackagesPath)\.agg</CBTAggregateDestPackageRoot>
<CBTNugetAggregatePackageImmutableRoots Condition=" '$(CBTNugetAggregatePackageImmutableRoots)'=='' ">$(NuGetPackagesPath)</CBTNugetAggregatePackageImmutableRoots>
</PropertyGroup>

<Import Project="$(CBTAggregatePackageImport)" Condition=" '$(CBTAggregatePackageImport)'!='' and '$(CBTEnableAggregatePackageRestore)'=='true' "/>

<PropertyGroup Condition=" '$(CBTEnableAggregatePackageRestore)' == 'true' ">
<CBTNuGetAggregatePackagePropertyFile Condition=" '$(CBTNuGetAggregatePackagePropertyFile)' == '' ">$(IntermediateOutputPath)\AggregatePackages.props</CBTNuGetAggregatePackagePropertyFile>
<CBTNuGetAggregatePackageGenerated>$(CBTNuGetTasksAssemblyPath.GetType().Assembly.GetType('System.AppDomain').GetProperty('CurrentDomain').GetValue(null).CreateInstanceFromAndUnwrap($(CBTNuGetTasksAssemblyPath), 'CBT.NuGet.Tasks.AggregatePackages').Execute('$(CBTAggregateDestPackageRoot)', '$(CBTAggregatePackage)', '$(CBTNuGetAggregatePackagePropertyFile)', '$(CBTNugetAggregatePackageImmutableRoots)'))</CBTNuGetAggregatePackageGenerated>
</PropertyGroup>

<ItemGroup>
<CBTParseError Condition=" '$(CBTNuGetAggregatePackageGenerated)' == 'false' " Include="Aggregate packages were not generated and the build cannot continue. Refer to other errors for more information.">
<Code>CBT.Nuget.AggregatePackage.1000</Code>
</CBTParseError>
</ItemGroup>

<Import Project="$(CBTNuGetAggregatePackagePropertyFile)" Condition=" '$(CBTNuGetAggregatePackageGenerated)' == 'true' And Exists('$(CBTNuGetAggregatePackagePropertyFile)') "/>

<Import Project="$(CBTLocalBuildExtensionsPath)\After.$(MSBuildThisFile)" Condition=" '$(CBTLocalBuildExtensionsPath)' != '' And Exists('$(CBTLocalBuildExtensionsPath)\After.$(MSBuildThisFile)') " />
<Import Project="$(CBTModuleExtensionsPath)\After.$(MSBuildThisFile)" Condition=" '$(CBTModuleExtensionsPath)' != '' And Exists('$(CBTModuleExtensionsPath)\After.$(MSBuildThisFile)') " />

</Project>
7 changes: 7 additions & 0 deletions src/CBT.NuGet.AggregatePackage/CBT/Module/module.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<configuration>
<extensionImports>
<add name="Before.CBT.NuGet.AggregatePackage.props" />
<add name="After.CBT.NuGet.AggregatePackage.props" />
</extensionImports>
</configuration>
11 changes: 11 additions & 0 deletions src/CBT.NuGet.AggregatePackage/version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0-dev",
"buildNumberOffset": 0,
"publicReleaseRefSpec": [
"^refs/tags/CBT\\.NuGet\\.AggregatePackage.*"
],
"cloudBuild": {
"setVersionVariables": false
}
}
8 changes: 7 additions & 1 deletion src/CBT.NuGet.Package/CBT/Module/CBT.NuGet.props
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@
<Code>CBT.Nuget.1002</Code>
</CBTParseError>
</ItemGroup>


<Import Project="$(CBTNuGetPackagePropertyFile)" Condition=" '$(CBTNuGetPackagePropertiesCreated)' == 'true' And Exists('$(CBTNuGetPackagePropertyFile)') "/>

<Import Project="$(MSBuildThisFileDirectory)Microsoft.NuGet\Microsoft.NuGet.props" Condition=" '$(IncludeNuGetImports)' != 'false' And !Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.Props\ImportBefore\Microsoft.NuGet.ImportBefore.props') "/>

<Import Project="$(CBTBuildPackagePropsFile)" Condition=" '$(CBTEnableImportBuildPackages)' != 'false' "/>

<Import Project="$(MSBuildThisFileDirectory)Extensions\After.CBT.NuGet.props" />
</Project>
6 changes: 0 additions & 6 deletions src/CBT.NuGet.Package/CBT/Module/build.props
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,4 @@

<Import Project="$(MSBuildThisFileDirectory)CBT.NuGet.targets" />

<Import Project="$(CBTNuGetPackagePropertyFile)" Condition=" '$(CBTNuGetPackagePropertiesCreated)' == 'true' And Exists('$(CBTNuGetPackagePropertyFile)') "/>

<Import Project="$(MSBuildThisFileDirectory)Microsoft.NuGet\Microsoft.NuGet.props" Condition=" '$(IncludeNuGetImports)' != 'false' And !Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Imports\Microsoft.Common.Props\ImportBefore\Microsoft.NuGet.ImportBefore.props') "/>

<Import Project="$(CBTBuildPackagePropsFile)" Condition=" '$(CBTEnableImportBuildPackages)' != 'false' "/>

</Project>
3 changes: 3 additions & 0 deletions src/CBT.NuGet/CBT.NuGet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
<Compile Include="DownloadCommandBase.cs" />
<Compile Include="Internal\CBTBuildEngine.cs" />
<Compile Include="Internal\ExtensionMethods.cs" />
<Compile Include="Internal\FileUtilities.cs" />
<Compile Include="Internal\MSBuildProjectLoader.cs" />
<Compile Include="Internal\NuGetPropertyGenerator.cs" />
<Compile Include="Internal\AggregatePackage.cs" />
<Compile Include="Internal\PackageInfo.cs" />
<Compile Include="Tasks\AggregatePackages.cs" />
<Compile Include="Tasks\GenerateNuGetProperties.cs" />
<Compile Include="Tasks\ImportBuildPackages.cs" />
<Compile Include="Tasks\NuGetAdd.cs" />
Expand Down
81 changes: 81 additions & 0 deletions src/CBT.NuGet/Internal/AggregatePackage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace CBT.NuGet.Internal
{
internal sealed class AggregatePackage
{
public enum AggregateOperation
{
Add = '*',
Remove = '!',
}

internal class PackageOperation
{
private string folder = string.Empty;
internal AggregateOperation Operation { get; set; }

internal string Folder
{
get
{
return folder;
}
set
{
folder = Path.GetFullPath(value);
}
}
}

private string[] immutableRootPaths = null;

public AggregatePackage(string outPropertyId, ICollection<PackageOperation> packagesToAggregate, string destinationRoot, string immutableRoots)
{
OutPropertyId = outPropertyId;
PackagesToAggregate = packagesToAggregate;
immutableRootPaths = immutableRoots.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(i => Path.GetFullPath(i.Trim())).Where(j => !string.IsNullOrWhiteSpace(j)).ToArray();
OutPropertyValue = Path.Combine(destinationRoot, $"{outPropertyId}.{GetOutputPropertyHash()}");
}

public string OutPropertyId { get; private set; }

public string OutPropertyValue { get; private set; }

public ICollection<PackageOperation> PackagesToAggregate { get; private set; }

private string GetOutputPropertyHash()
{
StringBuilder valueToHash = new StringBuilder();
foreach (var pkg in PackagesToAggregate)
{
valueToHash.Append(pkg.Folder.ToLower());
// Include last write time on all files to ensure that if the source folder was updated a new aggregate package will be created.
// if a file was removed then it will not be in the value to hash so the hash will still change to trigger a new aggregation as desired.
// We don't care about directories in aggregation since removing directories is not possible given current implementation.
// This is in the scenario where the user aggregates against a nuget package and a checked in folder under source control that contains a config file or whatever.
// Since this is a string contains match an immutable root match could be problematic in corner cases. d:\tmp\src and d:\tmp\src2 will both match d:\tmp\sr as an immutable root.
valueToHash.Append(Directory.GetLastWriteTime(pkg.Folder).ToString());
if ( !immutableRootPaths.Any() || !immutableRootPaths.Where(i => pkg.Folder.IndexOf(i, StringComparison.OrdinalIgnoreCase) >= 0).Any())
{
foreach (var f in Directory.EnumerateFiles(pkg.Folder, "*.*", SearchOption.AllDirectories))
{
valueToHash.Append(File.GetLastWriteTime(f).ToString());
}
}
}

string hashValue = Convert.ToBase64String((new SHA1CryptoServiceProvider()).ComputeHash(Encoding.UTF8.GetBytes(valueToHash.ToString())));
Regex pattern = new Regex("[/=+]");
hashValue = pattern.Replace(hashValue, "x");
return string.Format("{0:X}", hashValue);
}

}
}
99 changes: 99 additions & 0 deletions src/CBT.NuGet/Internal/FileUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CBT.NuGet.Internal
{
class FileUtilities
{
// Need to find a proper directory copy routine.
public static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs, bool overWriteFiles)
{
if (!Directory.Exists(sourceDirName))
{
throw new DirectoryNotFoundException("Source Directory does not exist or could not be found: " + sourceDirName);
}
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
DirectoryInfo[] dirs = dir.GetDirectories();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use EnumerateDirectories instead and EnumerateFiles instead of GetFiles below. We should probably revisit this method and see if Async copies or parallelism would help here.

if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, overWriteFiles);
}
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs, overWriteFiles);
}
}
}

public static void DirectoryRemove(string sourceDirName, string destDirName, bool recurseSubDirs)
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException("Source Directory does not exist or could not be found: " + sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();

FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string tempPath = Path.Combine(destDirName, file.Name);
if (File.Exists(tempPath))
{
File.SetAttributes(tempPath, File.GetAttributes(tempPath) & ~FileAttributes.ReadOnly);
File.Delete(tempPath);
}
}
if (recurseSubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryRemove(subdir.FullName, temppath, recurseSubDirs);
}
}
}

// Mutex isn't platform agnostic need to consider options.
public static string ComputeMutexName(string sessionString)
{
// get a hash of the file path; reason: there's a limit on name length for named mutexes
using (var algo = SHA256.Create())
{
// Global: make it work across TS sessions; not that we should need this, but just to be super-extra safe
return "Global\\" + Convert.ToBase64String(algo.ComputeHash(Encoding.UTF8.GetBytes(sessionString.ToUpperInvariant())));
}
}

public static void AcquireMutex(Mutex mutex)
{
try
{
bool owner = mutex.WaitOne(); // Perhaps specify a timeout value so builds don't hang.
if (!owner)
throw new TimeoutException("Timeout waiting for mutex");
}
catch (AbandonedMutexException)
{
// Unlikely we care.
Trace.TraceWarning("mutex for Aggregate package was abandoned.");
}
}
}
}
Loading