Backport improvements from dev branch to repo tasks

- CalculateBuildGraph
 - PinVersion tool
 - Ensuring sub-repositories build with the same version of KoreBuild executing in Universe
This commit is contained in:
Nate McMaster 2017-09-14 15:23:19 -07:00
parent 9eb27fa53f
commit 52757943ac
20 changed files with 269 additions and 186 deletions

View File

@ -1,27 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PinVersions", "tools\PinVersions\PinVersions.csproj", "{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{085280EC-7055-426A-BF9C-1B692B9599AB}"
ProjectSection(SolutionItems) = preProject
tools\shared\DependencyGraphSpecProvider.cs = tools\shared\DependencyGraphSpecProvider.cs
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,10 +1,7 @@
<Project> <Project>
<ItemGroup> <ItemGroup>
<!-- Repos being patched --> <!-- Repos being patched -->
<Repository Include="EntityFrameworkCore" Branch="rel/2.0.1" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" /> <Repository Include="EntityFrameworkCore" Branch="feature/2.0.1" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<Repository Include="Mvc" Branch="rel/2.0.1" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<Repository Include="Identity" Branch="rel/2.0.1" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<Repository Include="MetaPackages" Branch="rel/2.0.1" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<!-- TODO compute downstream repos that should be updated. --> <!-- TODO compute downstream repos that should be updated. -->
@ -13,6 +10,9 @@
Build tools will *verify* that these repos will be unaffected Build tools will *verify* that these repos will be unaffected
by the patch update and do not need updating. by the patch update and do not need updating.
--> -->
<VerifyRepository Include="Mvc" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<VerifyRepository Include="Identity" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<VerifyRepository Include="MetaPackages" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<VerifyRepository Include="Antiforgery" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" /> <VerifyRepository Include="Antiforgery" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<VerifyRepository Include="AzureIntegration" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" /> <VerifyRepository Include="AzureIntegration" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />
<VerifyRepository Include="BasicMiddleware" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" /> <VerifyRepository Include="BasicMiddleware" Branch="rel/2.0.0" Commit="" CloneUrl="git@github.com:aspnet/%(Identity)" />

View File

@ -41,20 +41,14 @@
<Message Text="============ Building $(RepositoryToBuild) ============" Importance="High" /> <Message Text="============ Building $(RepositoryToBuild) ============" Importance="High" />
<!-- Copy Korebuild folder to individual repos to avoid downloading it again and again and also to <!-- Copy Korebuild lock file to individual repos to align version if the repo doesn't already have one -->
prevent from taking in newer Korebuild changes when Universe is building midway --> <Message Text="Copying KoreBuild lockfile from Universe to repository $(BuildRepositoryRoot)"/>
<ItemGroup> <Copy SourceFiles="$(RepositoryRoot)korebuild-lock.txt" DestinationFolder="$(BuildRepositoryRoot)" />
<_KorebuildItems Include="$(RepositoryRoot).build\**\*.*" />
</ItemGroup>
<Message Text="Copying Korebuild from Universe to repository $(BuildRepositoryRoot)"/>
<Copy SourceFiles="@(_KorebuildItems)" DestinationFolder="$(BuildRepositoryRoot).build\%(RecursiveDir)" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />
<Exec <Exec
Command="./$(_BuildScriptToExecute) $(BuildArguments)" Command="./$(_BuildScriptToExecute) -Path $(BuildRepositoryRoot) $(BuildArguments)"
EnvironmentVariables="KOREBUILD_SKIP_RUNTIME_INSTALL=1"
IgnoreStandardErrorWarningFormat="true" IgnoreStandardErrorWarningFormat="true"
WorkingDirectory="$(BuildRepositoryRoot)" /> WorkingDirectory="$(RepositoryRoot)" />
<ItemGroup> <ItemGroup>
<RepositoryArtifacts Include="$(RepositoryArtifactsBuildDirectory)*" /> <RepositoryArtifacts Include="$(RepositoryArtifactsBuildDirectory)*" />
@ -71,24 +65,19 @@
SourceFiles="@(RepositoryMSBuildArtifacts)" SourceFiles="@(RepositoryMSBuildArtifacts)"
DestinationFolder="$(ArtifactsDir)msbuild\$(RepositoryToBuild)\%(RecursiveDir)" /> DestinationFolder="$(ArtifactsDir)msbuild\$(RepositoryToBuild)\%(RecursiveDir)" />
<Message Text="Publishing the following packages to the volatile feed: @(RepositoryNupkgs -> '%(Filename)%(Extension)', ', ')"
Condition="'$(PublishPackages)'=='true' AND '@(RepositoryNupkgs)' != ''" />
<Exec
Command="$(DotNetPath) $(PackagePublisherNetCoreApp) -d $(RepositoryArtifactsBuildDirectory) -f $(NuGetPublishVolatileFeed)"
Condition="'$(PublishPackages)'=='true' AND '@(RepositoryNupkgs)' != ''" />
<Message Text="============ Done building $(RepositoryToBuild) ============" Importance="High" /> <Message Text="============ Done building $(RepositoryToBuild) ============" Importance="High" />
</Target> </Target>
<Target Name="_PinVersions" DependsOnTargets="_FindDotNetPath"> <Target Name="_PinVersions">
<ItemGroup>
<PinPackageSources Include="$(BuildDir)" />
<PinPackageSources Include="$(_DependencyPackagesDirectory)" Condition="Exists('$(_DependencyPackagesDirectory)')" />
</ItemGroup>
<PropertyGroup> <RepoTasks.PinVersions
<PinToolBinary>$(RepositoryRoot)tools\PinVersions\bin\$(Configuration)\netcoreapp1.1\PinVersions.dll</PinToolBinary> GraphSpecsRoot="$(_RestoreGraphSpecsDirectory)"
<PinVersionArgs>$(DotNetPath) $(PinToolBinary) --graph-specs-root &quot;$(_RestoreGraphSpecsDirectory) &quot; -s &quot;$(BuildDir) &quot; &quot;$(BuildRepositoryRoot) &quot;</PinVersionArgs> BuildRepositoryRoot="$(BuildRepositoryRoot)"
<PinVersionArgs Condition="Exists('$(_DependencyPackagesDirectory)')">$(PinVersionArgs) -s &quot;$(_DependencyPackagesDirectory) &quot;</PinVersionArgs> PackageSources="@(PinPackageSources)" />
</PropertyGroup>
<Exec Command="$(PinVersionArgs)" />
</Target> </Target>
</Project>
</Project>

6
build/repo.props Normal file
View File

@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<!-- This repo does not have solutions to build -->
<DisableDefaultTargets>true</DisableDefaultTargets>
</PropertyGroup>
</Project>

View File

@ -23,10 +23,6 @@
<BuildDependsOn>$(BuildDependsOn);CloneRepositories;BuildRepositories</BuildDependsOn> <BuildDependsOn>$(BuildDependsOn);CloneRepositories;BuildRepositories</BuildDependsOn>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="PackagePublisher" Version="2.0.0-*" />
</ItemGroup>
<Import Project="$(_RepositoryListToImport)" /> <Import Project="$(_RepositoryListToImport)" />
<Target Name="CleanUniverseArtifacts"> <Target Name="CleanUniverseArtifacts">
@ -57,7 +53,7 @@
<Target Name="CloneRepositories" DependsOnTargets="_PrepareRepositories"> <Target Name="CloneRepositories" DependsOnTargets="_PrepareRepositories">
<ItemGroup> <ItemGroup>
<_CloneRepositories Include="@(Repository);@(VerifyRepositories)" /> <_CloneRepositories Include="@(Repository);@(VerifyRepository)" />
<_CloneRepository Include="$(MSBuildProjectFullPath)"> <_CloneRepository Include="$(MSBuildProjectFullPath)">
<AdditionalProperties> <AdditionalProperties>
CloneRepository=%(_CloneRepositories.Identity); CloneRepository=%(_CloneRepositories.Identity);
@ -122,7 +118,7 @@
</Target> </Target>
<Target Name="BuildRepositories" <Target Name="BuildRepositories"
DependsOnTargets="_PrepareRepositories;_FindDotNetPath;_CreateRepositoriesListWithCommits;_UpdateNuGetConfig;_GenerateBuildGraph;_BuildRepositories" /> DependsOnTargets="_PrepareRepositories;_CreateRepositoriesListWithCommits;_UpdateNuGetConfig;_GenerateBuildGraph;_BuildRepositories" />
<Target Name="_PrepareRestoreGraphSpecs" DependsOnTargets="_PrepareRepositories"> <Target Name="_PrepareRestoreGraphSpecs" DependsOnTargets="_PrepareRepositories">
<ItemGroup> <ItemGroup>
@ -213,9 +209,4 @@
</PropertyGroup> </PropertyGroup>
</Target> </Target>
<Target Name="_FindDotNetPath">
<GetDotNetHost>
<Output TaskParameter="ExecutablePath" PropertyName="DotNetPath" />
</GetDotNetHost>
</Target>
</Project> </Project>

View File

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO; using System.IO;
using NuGet.ProjectModel; using NuGet.ProjectModel;
@ -15,6 +18,12 @@ namespace RepoTools.BuildGraph
public DependencyGraphSpec GetDependencyGraphSpec(string repositoryName, string solutionPath) public DependencyGraphSpec GetDependencyGraphSpec(string repositoryName, string solutionPath)
{ {
var outputFile = Path.Combine(_packageSpecDirectory, repositoryName, Path.GetFileName(solutionPath) + ".json"); var outputFile = Path.Combine(_packageSpecDirectory, repositoryName, Path.GetFileName(solutionPath) + ".json");
if (!File.Exists(outputFile))
{
return null;
}
return DependencyGraphSpec.Load(outputFile); return DependencyGraphSpec.Load(outputFile);
} }
} }

View File

@ -1,12 +1,17 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using Microsoft.Build.Utilities;
namespace RepoTools.BuildGraph namespace RepoTools.BuildGraph
{ {
public static class GraphBuilder public static class GraphBuilder
{ {
public static IList<GraphNode> Generate(IList<Repository> repositories, string root) public static IList<GraphNode> Generate(IList<Repository> repositories, string root, TaskLoggingHelper log)
{ {
// Build global list of primary projects // Build global list of primary projects
var primaryProjects = repositories.SelectMany(c => c.Projects) var primaryProjects = repositories.SelectMany(c => c.Projects)
@ -31,7 +36,15 @@ namespace RepoTools.BuildGraph
var dependencyRepository = dependencyProject.Repository; var dependencyRepository = dependencyProject.Repository;
var dependencyNode = graphNodes[dependencyRepository]; var dependencyNode = graphNodes[dependencyRepository];
thisProjectRepositoryNode.Incoming.Add(dependencyNode); if (ReferenceEquals(thisProjectRepositoryNode, dependencyNode))
{
log.LogWarning("{0} has a package reference to a package produced in the same repo. {1} -> {2}", project.Repository.Name, Path.GetFileName(project.Path), packageDependency);
}
else
{
thisProjectRepositoryNode.Incoming.Add(dependencyNode);
}
dependencyNode.Outgoing.Add(thisProjectRepositoryNode); dependencyNode.Outgoing.Add(thisProjectRepositoryNode);
} }
} }

View File

@ -1,4 +1,7 @@
using System.Collections.Generic; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
namespace RepoTools.BuildGraph namespace RepoTools.BuildGraph

View File

@ -1,4 +1,7 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -14,6 +17,8 @@ namespace RepoTools.BuildGraph
public string Name { get; } public string Name { get; }
public string Version { get; set; }
public string Path { get; set; } public string Path { get; set; }
public Repository Repository { get; set; } public Repository Repository { get; set; }

View File

@ -1,4 +1,7 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
@ -49,6 +52,7 @@ namespace RepoTools.BuildGraph
var repository = new Repository(name); var repository = new Repository(name);
ReadSharedSourceProjects(Path.Combine(repositoryPath, "shared"), repository, repository.Projects); ReadSharedSourceProjects(Path.Combine(repositoryPath, "shared"), repository, repository.Projects);
var srcDirectory = Path.GetFullPath(Path.Combine(repositoryPath, "src")) var srcDirectory = Path.GetFullPath(Path.Combine(repositoryPath, "src"))
.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
@ -56,6 +60,11 @@ namespace RepoTools.BuildGraph
foreach (var file in solutionFiles) foreach (var file in solutionFiles)
{ {
var spec = provider.GetDependencyGraphSpec(name, file); var spec = provider.GetDependencyGraphSpec(name, file);
if (spec == null)
{
continue;
}
var projects = spec.Projects.OrderBy(p => p.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference ? 0 : 1); var projects = spec.Projects.OrderBy(p => p.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference ? 0 : 1);
foreach (var specProject in projects) foreach (var specProject in projects)
{ {
@ -73,6 +82,7 @@ namespace RepoTools.BuildGraph
{ {
Repository = repository, Repository = repository,
Path = specProject.FilePath, Path = specProject.FilePath,
Version = specProject.Version?.ToString(),
}; };
projectGroup.Add(project); projectGroup.Add(project);

View File

@ -1,4 +1,7 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;

View File

@ -15,19 +15,19 @@ namespace RepoTasks
[Required] [Required]
public ITaskItem[] Repositories { get; set; } public ITaskItem[] Repositories { get; set; }
[Output] /// <summary>
public ITaskItem[] RepositoriesToBuildInOrder { get; set; } /// Directory that contains the package spec files.
/// </summary>
[Required]
public string PackageSpecsDirectory { get; set; }
/// <summary> /// <summary>
/// The repository at which to root the graph at /// The repository at which to root the graph at
/// </summary> /// </summary>
public string StartGraphAt { get; set; } public string StartGraphAt { get; set; }
/// <summary> [Output]
/// Directory that contains the package spec files. public ITaskItem[] RepositoriesToBuildInOrder { get; set; }
/// </summary>
[Required]
public string PackageSpecsDirectory { get; set; }
public override bool Execute() public override bool Execute()
{ {
@ -36,12 +36,18 @@ namespace RepoTasks
var repositoryPaths = Repositories.Select(r => r.GetMetadata("RepositoryPath")).ToList(); var repositoryPaths = Repositories.Select(r => r.GetMetadata("RepositoryPath")).ToList();
var repositories = Repository.ReadAllRepositories(repositoryPaths, graphSpecProvider); var repositories = Repository.ReadAllRepositories(repositoryPaths, graphSpecProvider);
var graph = GraphBuilder.Generate(repositories, StartGraphAt); var graph = GraphBuilder.Generate(repositories, StartGraphAt, Log);
var repositoriesWithOrder = new List<(ITaskItem repository, int order)>(); var repositoriesWithOrder = new List<(ITaskItem repository, int order)>();
foreach (var repositoryTaskItem in Repositories) foreach (var repositoryTaskItem in Repositories)
{ {
var repositoryName = repositoryTaskItem.ItemSpec; var repositoryName = repositoryTaskItem.ItemSpec;
var graphNodeRepository = graph.First(g => g.Repository.Name == repositoryName); var graphNodeRepository = graph.FirstOrDefault(g => g.Repository.Name == repositoryName);
if (graphNodeRepository == null)
{
// StartGraphAt was specified so the graph is incomplete.
continue;
}
var order = TopologicalSort.GetOrder(graphNodeRepository); var order = TopologicalSort.GetOrder(graphNodeRepository);
repositoryTaskItem.SetMetadata("Order", order.ToString()); repositoryTaskItem.SetMetadata("Order", order.ToString());
repositoriesWithOrder.Add((repositoryTaskItem, order)); repositoriesWithOrder.Add((repositoryTaskItem, order));

View File

@ -0,0 +1,61 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
namespace RepoTasks
{
public class FlowLogger : ConsoleLogger
{
private volatile bool _initialized;
public FlowLogger()
{
}
public override void Initialize(IEventSource eventSource, int nodeCount)
{
PreInit(eventSource);
base.Initialize(eventSource, nodeCount);
}
public override void Initialize(IEventSource eventSource)
{
PreInit(eventSource);
base.Initialize(eventSource);
}
private void PreInit(IEventSource eventSource)
{
if (_initialized) return;
_initialized = true;
var flowId = GetFlowId();
var prefix = $"{flowId,-22}| ";
var write = WriteHandler;
WriteHandler = msg => write(prefix + msg);
eventSource.BuildStarted += (o, e) =>
{
WriteHandler(e.Message + Environment.NewLine);
};
}
private string GetFlowId()
{
var parameters = Parameters?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
if (parameters == null || parameters.Length == 0)
{
return null;
}
const string flowIdParamName = "FlowId=";
return parameters
.FirstOrDefault(p => p.StartsWith(flowIdParamName, StringComparison.Ordinal))
?.Substring(flowIdParamName.Length);
}
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.Build.Framework;
using RepoTasks.VersionPinning;
namespace RepoTasks
{
public class PinVersions : Microsoft.Build.Utilities.Task
{
[Required]
public string BuildRepositoryRoot { get; set; }
[Required]
public ITaskItem[] PackageSources { get; set; }
public string GraphSpecsRoot { get; set; }
public override bool Execute()
{
if (PackageSources?.Length == 0)
{
Log.LogError($"Missing PackageSources. At least one item source must be specified.");
return false;
}
var graphSpecProvider = !string.IsNullOrEmpty(GraphSpecsRoot)
? new DependencyGraphSpecProvider(GraphSpecsRoot)
: DependencyGraphSpecProvider.Default;
using (graphSpecProvider)
{
var pinVersionUtility = new PinVersionUtility(
BuildRepositoryRoot,
PackageSources.Select(i => i.ItemSpec).ToList(),
graphSpecProvider,
Log);
pinVersionUtility.Execute();
}
return true;
}
}
}

View File

@ -6,7 +6,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="NuGet.ProjectModel" Version="4.0.0" /> <PackageReference Include="NuGet.ProjectModel" Version="4.3.0" PrivateAssets="All" />
</ItemGroup> </ItemGroup>
<Import Project="$(RepoTasksSdkPath)\Sdk.targets" Condition="'$(RepoTasksSdkPath)' != '' "/> <Import Project="$(RepoTasksSdkPath)\Sdk.targets" Condition="'$(RepoTasksSdkPath)' != '' "/>

View File

@ -5,4 +5,5 @@
<UsingTask TaskName="RepoTasks.CalculateBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" /> <UsingTask TaskName="RepoTasks.CalculateBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.VerifyBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" /> <UsingTask TaskName="RepoTasks.VerifyBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.PinVersions" AssemblyFile="$(_RepoTaskAssembly)" />
</Project> </Project>

View File

@ -1,17 +1,19 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using Microsoft.DotNet.Cli.Utils;
using NuGet.ProjectModel; using NuGet.ProjectModel;
namespace UniverseTools namespace RepoTasks.VersionPinning
{ {
public class DependencyGraphSpecProvider : IDisposable public class DependencyGraphSpecProvider : IDisposable
{ {
private readonly string _packageSpecDirectory; private readonly string _packageSpecDirectory;
private readonly bool _deleteSpecDirectoryOnDispose; private readonly bool _deleteSpecDirectoryOnDispose;
private readonly string _muxerPath; private readonly string _dotnetPath;
public DependencyGraphSpecProvider(string packageSpecDirectory) public DependencyGraphSpecProvider(string packageSpecDirectory)
: this(packageSpecDirectory, deleteSpecDirectoryOnDispose: false) : this(packageSpecDirectory, deleteSpecDirectoryOnDispose: false)
@ -22,7 +24,7 @@ namespace UniverseTools
{ {
_packageSpecDirectory = packageSpecDirectory; _packageSpecDirectory = packageSpecDirectory;
_deleteSpecDirectoryOnDispose = deleteSpecDirectoryOnDispose; _deleteSpecDirectoryOnDispose = deleteSpecDirectoryOnDispose;
_muxerPath = new Muxer().MuxerPath; _dotnetPath = Process.GetCurrentProcess().MainModule.FileName;
} }
public static DependencyGraphSpecProvider Default { get; } = public static DependencyGraphSpecProvider Default { get; } =
@ -42,7 +44,7 @@ namespace UniverseTools
private void RunMSBuild(string solutionPath, string outputFile) private void RunMSBuild(string solutionPath, string outputFile)
{ {
var psi = new ProcessStartInfo(_muxerPath); var psi = new ProcessStartInfo(_dotnetPath);
var arguments = new List<string> var arguments = new List<string>
{ {
@ -53,6 +55,7 @@ namespace UniverseTools
"/v:q", "/v:q",
"/p:BuildProjectReferences=false", "/p:BuildProjectReferences=false",
$"/p:RestoreGraphOutputPath=\"{outputFile}\"", $"/p:RestoreGraphOutputPath=\"{outputFile}\"",
"/p:KoreBuildRestoreTargetsImported=true",
}; };
psi.Arguments = string.Join(" ", arguments); psi.Arguments = string.Join(" ", arguments);

View File

@ -1,4 +1,7 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -6,6 +9,8 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Common; using NuGet.Common;
using NuGet.Frameworks; using NuGet.Frameworks;
using NuGet.LibraryModel; using NuGet.LibraryModel;
@ -13,19 +18,23 @@ using NuGet.ProjectModel;
using NuGet.Protocol; using NuGet.Protocol;
using NuGet.Protocol.Core.Types; using NuGet.Protocol.Core.Types;
using NuGet.Versioning; using NuGet.Versioning;
using UniverseTools;
namespace PinVersions namespace RepoTasks.VersionPinning
{ {
class PinVersionUtility internal class PinVersionUtility
{ {
private readonly string _repositoryRoot; private readonly string _repositoryRoot;
private readonly FindPackageByIdResource[] _findPackageResources; private readonly FindPackageByIdResource[] _findPackageResources;
private readonly ConcurrentDictionary<string, Task<NuGetVersion>> _exactMatches = new ConcurrentDictionary<string, Task<NuGetVersion>>(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, Task<NuGetVersion>> _exactMatches = new ConcurrentDictionary<string, Task<NuGetVersion>>(StringComparer.OrdinalIgnoreCase);
private readonly DependencyGraphSpecProvider _provider; private readonly DependencyGraphSpecProvider _provider;
private readonly SourceCacheContext _sourceCacheContext; private readonly SourceCacheContext _sourceCacheContext;
private readonly TaskLoggingHelper _logger;
public PinVersionUtility(string repositoryRoot, List<string> pinSources, DependencyGraphSpecProvider provider) public PinVersionUtility(
string repositoryRoot,
List<string> pinSources,
DependencyGraphSpecProvider provider,
TaskLoggingHelper logger)
{ {
_repositoryRoot = repositoryRoot; _repositoryRoot = repositoryRoot;
_findPackageResources = new FindPackageByIdResource[pinSources.Count]; _findPackageResources = new FindPackageByIdResource[pinSources.Count];
@ -36,19 +45,27 @@ namespace PinVersions
} }
_provider = provider; _provider = provider;
_sourceCacheContext = new SourceCacheContext(); _sourceCacheContext = new SourceCacheContext();
_logger = logger;
} }
public void Execute() public void Execute()
{ {
var solutionPinMetadata = GetPinVersionMetadata(); _logger.LogMessage(MessageImportance.High, $"Pinning package references for projects in {_repositoryRoot}");
foreach (var item in solutionPinMetadata)
var solutionPinMetadata = GetProjectPinVersionMetadata();
foreach (var cliToolReference in solutionPinMetadata.CLIToolReferences)
{
_logger.LogMessage(MessageImportance.Normal, $"Pinning CLI Tool {cliToolReference.Item1.Name}({cliToolReference.Item1.VersionRange} to {cliToolReference.Item2} for all projects in {_repositoryRoot}.");
}
foreach (var item in solutionPinMetadata.PinVersionLookup)
{ {
var projectPinMetadata = item.Value; var projectPinMetadata = item.Value;
var specProject = projectPinMetadata.PackageSpec; var specProject = projectPinMetadata.PackageSpec;
if (!(projectPinMetadata.Packages.Any() || projectPinMetadata.CLIToolReferences.Any())) if (!(projectPinMetadata.Packages.Any() || solutionPinMetadata.CLIToolReferences.Any()))
{ {
Console.WriteLine($"No package or tool references to pin for {specProject.FilePath}."); _logger.LogMessage(MessageImportance.Normal, $"No package or tool references to pin for {specProject.FilePath}.");
continue; continue;
} }
@ -59,12 +76,16 @@ namespace PinVersions
Directory.CreateDirectory(Path.GetDirectoryName(pinnedReferencesFile)); Directory.CreateDirectory(Path.GetDirectoryName(pinnedReferencesFile));
Console.WriteLine($"Pinning package versions for {specProject.FilePath}."); if (projectPinMetadata.Packages.Any())
var pinnedReferences = new XElement("ItemGroup"); {
_logger.LogMessage(MessageImportance.Normal, $"Pinning package versions for {specProject.FilePath}.");
}
var pinnedReferences = new XElement("ItemGroup", new XAttribute("Condition", "'$(PolicyDesignTimeBuild)' != 'true' AND !Exists('$(MSBuildThisFileDirectory)$(MSBuildProjectFile).nugetpolicy.g.targets')"));
foreach (var packageReference in projectPinMetadata.Packages) foreach (var packageReference in projectPinMetadata.Packages)
{ {
(var tfm, var libraryRange, var exactVersion) = packageReference; (var tfm, var libraryRange, var exactVersion) = packageReference;
Console.WriteLine($"Pinning reference {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}."); _logger.LogMessage(MessageImportance.Normal, $"Pinning reference {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}.");
var metadata = new List<XAttribute> var metadata = new List<XAttribute>
{ {
new XAttribute("Update", libraryRange.Name), new XAttribute("Update", libraryRange.Name),
@ -79,10 +100,10 @@ namespace PinVersions
pinnedReferences.Add(new XElement("PackageReference", metadata)); pinnedReferences.Add(new XElement("PackageReference", metadata));
} }
foreach (var toolReference in projectPinMetadata.CLIToolReferences) // CLI Tool references are specified at solution level.
foreach (var toolReference in solutionPinMetadata.CLIToolReferences)
{ {
(var libraryRange, var exactVersion) = toolReference; (var libraryRange, var exactVersion) = toolReference;
Console.WriteLine($"Pinning CLI Tool {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}.");
var metadata = new List<XAttribute> var metadata = new List<XAttribute>
{ {
new XAttribute("Update", libraryRange.Name), new XAttribute("Update", libraryRange.Name),
@ -97,10 +118,11 @@ namespace PinVersions
} }
} }
private IDictionary<string, PinVersionMetadata> GetPinVersionMetadata() private SolutionPinVersionMetadata GetProjectPinVersionMetadata()
{ {
var repositoryDirectoryInfo = new DirectoryInfo(_repositoryRoot); var repositoryDirectoryInfo = new DirectoryInfo(_repositoryRoot);
var projects = new Dictionary<string, PinVersionMetadata>(StringComparer.OrdinalIgnoreCase); var projects = new Dictionary<string, ProjectPinVersionMetadata>(StringComparer.OrdinalIgnoreCase);
var cliToolReferences = new List<(LibraryRange, NuGetVersion)>();
foreach (var slnFile in repositoryDirectoryInfo.EnumerateFiles("*.sln")) foreach (var slnFile in repositoryDirectoryInfo.EnumerateFiles("*.sln"))
{ {
@ -109,7 +131,7 @@ namespace PinVersions
{ {
if (!projects.TryGetValue(specProject.FilePath, out var pinMetadata)) if (!projects.TryGetValue(specProject.FilePath, out var pinMetadata))
{ {
pinMetadata = new PinVersionMetadata(specProject); pinMetadata = new ProjectPinVersionMetadata(specProject);
projects[specProject.FilePath] = pinMetadata; projects[specProject.FilePath] = pinMetadata;
} }
@ -139,7 +161,7 @@ namespace PinVersions
} }
else if (projectStyle == ProjectStyle.DotnetCliTool) else if (projectStyle == ProjectStyle.DotnetCliTool)
{ {
pinMetadata.CLIToolReferences.Add((reference.LibraryRange, exactVersion)); cliToolReferences.Add((reference.LibraryRange, exactVersion));
} }
else else
{ {
@ -149,7 +171,7 @@ namespace PinVersions
} }
} }
return projects; return new SolutionPinVersionMetadata(projects, cliToolReferences);
} }
private NuGetVersion GetExactVersion(string name, VersionRange range) private NuGetVersion GetExactVersion(string name, VersionRange range)
@ -188,20 +210,32 @@ namespace PinVersions
return null; return null;
} }
private struct PinVersionMetadata private struct SolutionPinVersionMetadata
{ {
public PinVersionMetadata(PackageSpec packageSpec) public SolutionPinVersionMetadata(
IDictionary<string, ProjectPinVersionMetadata> pinVersionLookup,
List<(LibraryRange, NuGetVersion)> cliToolReferences)
{
PinVersionLookup = pinVersionLookup;
CLIToolReferences = cliToolReferences;
}
public IDictionary<string, ProjectPinVersionMetadata> PinVersionLookup { get; }
public List<(LibraryRange, NuGetVersion)> CLIToolReferences { get; }
}
private struct ProjectPinVersionMetadata
{
public ProjectPinVersionMetadata(PackageSpec packageSpec)
{ {
PackageSpec = packageSpec; PackageSpec = packageSpec;
Packages = new List<(NuGetFramework, LibraryRange, NuGetVersion)>(); Packages = new List<(NuGetFramework, LibraryRange, NuGetVersion)>();
CLIToolReferences = new List<(LibraryRange, NuGetVersion)>();
} }
public PackageSpec PackageSpec { get; } public PackageSpec PackageSpec { get; }
public List<(NuGetFramework, LibraryRange, NuGetVersion)> Packages { get; } public List<(NuGetFramework, LibraryRange, NuGetVersion)> Packages { get; }
public List<(LibraryRange, NuGetVersion)> CLIToolReferences { get; }
} }
} }
} }

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\shared\*.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.Cli.Utils" Version="1.0.1" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>
</Project>

View File

@ -1,53 +0,0 @@
using System;
using Microsoft.Extensions.CommandLineUtils;
using UniverseTools;
namespace PinVersions
{
class Program
{
static int Main(string[] args)
{
var app = new CommandLineApplication();
var pinSourceOption = app.Option("-s|--source",
"Feed containing packages to pin.",
CommandOptionType.MultipleValue);
var packageSpecsDirectoryOption = app.Option("--graph-specs-root",
"Directory containing package specs. (Optional)",
CommandOptionType.SingleValue);
var repositoryArgument = app.Argument("Repository", "Repository directory");
app.OnExecute(() =>
{
if (!pinSourceOption.HasValue())
{
Console.Error.WriteLine($"Option {pinSourceOption.Template} must have a value.");
return 1;
}
if (string.IsNullOrEmpty(repositoryArgument.Value))
{
Console.Error.WriteLine($"Repository argument must be specified.");
return 1;
}
var graphSpecProvider = packageSpecsDirectoryOption.HasValue() ?
new DependencyGraphSpecProvider(packageSpecsDirectoryOption.Value().Trim()) :
DependencyGraphSpecProvider.Default;
using (graphSpecProvider)
{
var pinVersionUtility = new PinVersionUtility(repositoryArgument.Value.Trim(), pinSourceOption.Values, graphSpecProvider);
pinVersionUtility.Execute();
}
return 0;
});
return app.Execute(args);
}
}
}