Skip to content

Instantly share code, notes, and snippets.

@natemcmaster
Created March 7, 2018 22:06
Show Gist options
  • Save natemcmaster/abc882abae0f567eca6496624dc02c63 to your computer and use it in GitHub Desktop.
Save natemcmaster/abc882abae0f567eca6496624dc02c63 to your computer and use it in GitHub Desktop.
Custom restore
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.1.1" />
<PackageReference Include="NuGet.Build.Tasks" Version="4.5.0" />
</ItemGroup>
</Project>
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using NuGet.Common;
namespace custom_restore
{
internal class MyNuGetLogger : LoggerBase
{
private IReporter _reporter;
public MyNuGetLogger(IReporter reporter)
{
this._reporter = reporter;
}
public override void Log(ILogMessage message)
{
LogAsync(message);
}
public override Task LogAsync(ILogMessage message)
{
if (message.Level == LogLevel.Warning)
{
_reporter.Warn(message.FormatWithCode());
}
else if (message.Level == LogLevel.Error)
{
_reporter.Error(message.FormatWithCode());
}
else if (message.Level > LogLevel.Information)
{
_reporter.Output(message.FormatWithCode());
}
else
{
_reporter.Verbose(message.FormatWithCode());
}
return Task.CompletedTask;
}
}
}
using System;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using McMaster.Extensions.CommandLineUtils;
using NuGet.Commands;
using NuGet.Configuration;
using NuGet.Versioning;
namespace custom_restore
{
class Program
{
static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);
[Argument(0)]
[Required]
public string PackageId { get; set; }
[Option("--version")]
public string Version { get; set; }
[Option("--verbose")]
public bool Verbose { get; set; }
[Option]
public bool Prerelease { get; set; }
private async Task<int> OnExecuteAsync()
{
var reporter = new ConsoleReporter(PhysicalConsole.Singleton)
{
IsVerbose = Verbose
};
var installDir = Path.Combine(Directory.GetCurrentDirectory(), "packages");
var tempFilePath = Path.Combine(AppContext.BaseDirectory, "projectThatNeverExists.csproj");
ISettings settings = Settings.LoadDefaultSettings(tempFilePath);
VersionRange versionRange;
if (!string.IsNullOrEmpty(Version))
{
if (!VersionRange.TryParse(Version, out versionRange))
{
reporter.Error($"Invalid nuget version '{Version}'");
return 1;
}
}
else
{
versionRange = Prerelease
? VersionRange.AllFloating
: VersionRange.AllStableFloating;
}
var logger = new MyNuGetLogger(reporter);
var results = await RestoreRunnerEx.RunWithoutCommit(tempFilePath, installDir, PackageId, versionRange, settings, logger);
var success = false;
foreach (var result in results)
{
if (result.Result.Success)
{
var installedVersion = result.Result.LockFile.Libraries.FirstOrDefault(l => string.Equals(PackageId, l.Name, StringComparison.OrdinalIgnoreCase));
if (installedVersion != null)
{
var path = installedVersion.Path;
reporter.Output($"Installed {installedVersion.Name} {installedVersion.Version}");
foreach(var file in installedVersion.Files)
{
reporter.Verbose("Package file: " + file);
}
success = true;
break;
}
}
else
{
foreach (var unresolved in result.Result.GetAllUnresolved())
{
reporter.Warn($"Could not find a package {unresolved.Name} {unresolved.VersionRange}");
}
}
}
if (success)
{
reporter.Output("Installation succeeded");
return 0;
}
reporter.Error("Installation failed");
return success ? 1 : 0;
}
}
}
// copied from https://github.com/Microsoft/msbuild/blob/master/src/NuGetSdkResolver/RestoreRunnerEx.cs with a few tweaks
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.ProjectModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using ILogger = NuGet.Common.ILogger;
namespace NuGet.Commands
{
/// <summary>
/// An extension of the NuGet.Commands.RestoreRunner class that contains APIs we do not yet have.
/// https://github.com/NuGet/Home/issues/5919
/// </summary>
internal static class RestoreRunnerEx
{
// NuGet requires at least one framework, we use .NET Standard here just to get the API to do work. The framework is not actually used.
private static readonly List<NuGetFramework> TargetFrameworks = new List<NuGetFramework>
{
FrameworkConstants.CommonFrameworks.NetStandard
};
/// <summary>
/// Restores a package by querying, downloading, and unzipping it without generating any other files (like project.assets.json).
/// </summary>
/// <param name="projectPath">The full path to the project.</param>
/// <param name="id">The ID of the package.</param>
/// <param name="version">The version of the package.</param>
/// <param name="settings">The NuGet settings to use.</param>
/// <param name="logger">An <see cref="ILogger"/> to use for logging.</param>
/// <returns></returns>
public static Task<IReadOnlyList<RestoreResultPair>> RunWithoutCommit(string projectPath, string outputPath, string id, VersionRange version, ISettings settings, ILogger logger)
{
using (SourceCacheContext sourceCacheContext = new SourceCacheContext
{
IgnoreFailedSources = true,
})
{
// The package spec details what packages to restore
PackageSpec packageSpec = new PackageSpec(TargetFrameworks.Select(i => new TargetFrameworkInformation
{
FrameworkName = i,
}).ToList())
{
Dependencies = new List<LibraryDependency>
{
new LibraryDependency
{
LibraryRange = new LibraryRange(id, version, LibraryDependencyTarget.Package),
SuppressParent = LibraryIncludeFlags.All,
AutoReferenced = true,
IncludeType = LibraryIncludeFlags.None,
Type = LibraryDependencyType.Build
}
},
RestoreMetadata = new ProjectRestoreMetadata
{
ProjectPath = projectPath,
ProjectName = Path.GetFileNameWithoutExtension(projectPath),
ProjectStyle = ProjectStyle.PackageReference,
ProjectUniqueName = projectPath,
OutputPath = Path.GetTempPath(),
OriginalTargetFrameworks = TargetFrameworks.Select(i => i.ToString()).ToList(),
ConfigFilePaths = SettingsUtility.GetConfigFilePaths(settings).ToList(),
PackagesPath = outputPath,
Sources = SettingsUtility.GetEnabledSources(settings).ToList(),
FallbackFolders = SettingsUtility.GetFallbackPackageFolders(settings).ToList()
},
FilePath = projectPath,
Name = Path.GetFileNameWithoutExtension(projectPath),
};
DependencyGraphSpec dependencyGraphSpec = new DependencyGraphSpec();
dependencyGraphSpec.AddProject(packageSpec);
dependencyGraphSpec.AddRestore(packageSpec.RestoreMetadata.ProjectUniqueName);
IPreLoadedRestoreRequestProvider requestProvider = new DependencyGraphSpecRequestProvider(new RestoreCommandProvidersCache(), dependencyGraphSpec);
RestoreArgs restoreArgs = new RestoreArgs
{
AllowNoOp = true,
CacheContext = sourceCacheContext,
CachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(settings)),
Log = logger,
};
// Create requests from the arguments
IReadOnlyList<RestoreSummaryRequest> requests = requestProvider.CreateRequests(restoreArgs).Result;
// Restore the package without generating extra files
return RestoreRunner.RunWithoutCommit(requests, restoreArgs);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment