├── nue.png ├── Nue ├── Nue.Core │ ├── IResolver.cs │ ├── TfmAtom.cs │ ├── IPackageResolver.cs │ ├── Nue.Core.csproj │ ├── RunSettings.cs │ ├── PackageInformation.cs │ ├── PackageAtom.cs │ └── Helpers.cs ├── Nue.StandardResolver │ ├── packages.config │ ├── app.config │ ├── custom.nuget.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PackageDownloder.cs │ ├── Nue.StandardResolver.csproj │ └── Resolver.cs ├── Nue │ ├── Core │ │ ├── DualOutput.cs │ │ └── Extractor.cs │ ├── Models │ │ └── CommandLineOptions.cs │ ├── packages.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Program.cs │ ├── App.config │ └── Nue.csproj └── Nue.sln ├── packagedefs ├── devices.csv └── azuresdkfornet.csv ├── LICENSE ├── README.md └── .gitignore /nue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/docascode/nue/HEAD/nue.png -------------------------------------------------------------------------------- /Nue/Nue.Core/IResolver.cs: -------------------------------------------------------------------------------- 1 | namespace Nue.Core 2 | { 3 | public interface IResolver 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /packagedefs/devices.csv: -------------------------------------------------------------------------------- 1 | xboxlive,[resolver=xbl;target=x64;ide=v140;build=release]Microsoft.Xbox.Live.SDK.WinRT.UWP,2017.5.20170517.1 -------------------------------------------------------------------------------- /Nue/Nue.Core/TfmAtom.cs: -------------------------------------------------------------------------------- 1 | namespace Nue.Core 2 | { 3 | public class TfmAtom 4 | { 5 | public string Moniker { get; set; } 6 | public string Target { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Nue/Nue.Core/IPackageResolver.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Nue.Core 5 | { 6 | public interface IPackageResolver 7 | { 8 | // will merge the current package information mapping into pkgInfoMap 9 | bool CopyBinarySet(PackageAtom package, RunSettings runSettings, PackageInfomarionMapping pkgInfoMap, AssemblyMappingPackageInformation pkgInfoMapofdepAssembly, string outputPrefix = ""); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/custom.nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Nue/Nue.Core/Nue.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.6 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packagedefs/azuresdkfornet.csv: -------------------------------------------------------------------------------- 1 | uwp-aspnetcore-app-ref,[excludedDlls=Microsoft.Win32.Registry.dll|System.Diagnostics.EventLog*|System.IO.Pipelines*|System.Security*|System.Windows.Extensions.dll]Microsoft.AspNetCore.App.Ref,3.0.0 2 | uwp-toolkit,[altDep=netstandard1.4|netstandard1.6]Microsoft.Toolkit.Uwp,2.0.0 3 | uwp-toolkit-notif,Microsoft.Toolkit.Uwp.Notifications 4 | uwp-toolkit-ui,Microsoft.Toolkit.Uwp.UI,2.0.0 5 | uwp-toolkit-ui-animations,Microsoft.Toolkit.Uwp.UI.Animations,2.0.0 6 | uwp-toolkit-ui-controls,Microsoft.Toolkit.Uwp.UI.Controls,2.0.0 7 | uwp-toolkit-notif-js,Microsoft.Toolkit.Uwp.Notifications.JavaScript,2.0.0 8 | uwp-toolkit-devtools,Microsoft.Toolkit.Uwp.DeveloperTools,2.0.0 -------------------------------------------------------------------------------- /Nue/Nue.Core/RunSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Nue.Core 6 | { 7 | public class RunSettings 8 | { 9 | public const string NUGET_DEFAULT_FEED = "https://api.nuget.org/v3/index.json"; 10 | 11 | public readonly string TFM; 12 | 13 | public readonly string Feed; 14 | 15 | public readonly string NugetPath; 16 | 17 | public readonly string OutputPath; 18 | 19 | public RunSettings(string tfm, string feed, string nugetPath, string outputPath) 20 | { 21 | feed = feed?.Trim('\"'); 22 | Feed = string.IsNullOrEmpty(feed) ? NUGET_DEFAULT_FEED : feed; 23 | 24 | TFM = tfm; 25 | NugetPath = nugetPath; 26 | OutputPath = outputPath; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Nue/Nue/Core/DualOutput.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace Nue.Core 6 | { 7 | public static class DualOutput 8 | { 9 | private static TextWriter _current; 10 | 11 | private class OutputWriter : TextWriter 12 | { 13 | static object locker = new object(); 14 | public override Encoding Encoding 15 | { 16 | get 17 | { 18 | return _current.Encoding; 19 | } 20 | } 21 | 22 | public override void WriteLine(string value) 23 | { 24 | _current.WriteLine(value); 25 | 26 | lock (locker) 27 | { 28 | File.AppendAllText("nue-execution-log.txt", value); 29 | } 30 | } 31 | } 32 | 33 | public static void Initialize() 34 | { 35 | _current = Console.Out; 36 | Console.SetOut(new OutputWriter()); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Den Delimarsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Nue/Nue.Core/PackageInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Nue.Core 7 | { 8 | public class PackageInfomarion 9 | { 10 | public string Name { get; set; } 11 | 12 | public string Version { get; set; } 13 | 14 | public string Feed { get; set; } 15 | } 16 | 17 | // moniker1 18 | // |__ assembly1 => package1 19 | // |__ assembly2 => package2 20 | public class PackageInfomarionMapping : Dictionary> 21 | { 22 | public PackageInfomarionMapping ToFlatten(string newMoniker) 23 | { 24 | var result = new PackageInfomarionMapping(); 25 | result[newMoniker] = new Dictionary(); 26 | 27 | foreach (var moniker in this.Keys) 28 | { 29 | foreach (var mapping in this[moniker]) 30 | { 31 | result[newMoniker][mapping.Key] = mapping.Value; 32 | } 33 | } 34 | 35 | return result; 36 | } 37 | } 38 | 39 | public class AssemblyMappingPackageInformation : Dictionary> 40 | { 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Nue/Nue/Models/CommandLineOptions.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | 3 | namespace Nue.Models 4 | { 5 | internal class CommandLineOptions 6 | { 7 | [Option('p', "packages", Required = false, HelpText = "Path to CSV with packages.")] 8 | public string PackagePath { get; set; } 9 | 10 | [Option('o', "output", Required = false, HelpText = "Determines where to output the binaries and their dependencies.")] 11 | public string OutputPath { get; set; } 12 | 13 | [Option('f', "framework", Required = false, HelpText = "Determines the framework for which to get the binaries.", Default = "net471")] 14 | public string Framework { get; set; } 15 | 16 | [Option('n', "nugetpath", Required = false, HelpText = "Path to folder containing NuGet.exe.")] 17 | public string NuGetPath { get; set; } 18 | 19 | [Option('P', "password", Required = false, HelpText = "Password for the feed to be used.")] 20 | public string Password { get; set; } 21 | 22 | [Option('U', "username", Required = false, HelpText = "Username for the feed to be used.")] 23 | public string Username { get; set; } 24 | 25 | [Option('F', "feed", Required = false, HelpText = "Custom feed to use with to download NuGet packages.")] 26 | public string Feed { get; set; } 27 | 28 | [Option('m', "moniker", Required = false, HelpText = "Record all package information in a single moniker")] 29 | public string Moniker { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Nue/Nue/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Nue/Nue/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Nue")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Nue")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("68cc99c6-1dbe-4459-ba94-742e9ff7bc86")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Nue.StandardResolver")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Nue.StandardResolver")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c8046141-6e78-44b8-bad8-d83a19e8e77b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Nue/Nue/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using CommandLine; 5 | using Newtonsoft.Json; 6 | using Nue.Core; 7 | using Nue.Models; 8 | 9 | namespace Nue 10 | { 11 | internal class Program 12 | { 13 | private static void Main(string[] args) 14 | { 15 | Stopwatch stopwatch = new Stopwatch(); 16 | stopwatch.Start(); 17 | 18 | DualOutput.Initialize(); 19 | 20 | Console.WriteLine("[info] nue 2.0.0-9272018.1817"); 21 | 22 | Parser.Default.ParseArguments(args).WithParsed(options => 23 | { 24 | Console.WriteLine("[info] Declared NuGet path: " + options.NuGetPath); 25 | 26 | RunSettings runSettings = new RunSettings(options.Framework, options.Feed, options.NuGetPath, options.OutputPath); 27 | var completed = Extractor.DownloadPackages(options.PackagePath, runSettings, out var pkgInfoMap); 28 | 29 | // Write package information 30 | var moniker = Path.GetFileNameWithoutExtension(options.PackagePath); 31 | if (!string.IsNullOrEmpty(options.Moniker)) 32 | { 33 | pkgInfoMap = pkgInfoMap.ToFlatten(options.Moniker); 34 | moniker = options.Moniker; 35 | } 36 | var jsonString = JsonConvert.SerializeObject(pkgInfoMap); 37 | Directory.CreateDirectory(Path.Combine(options.OutputPath, "PackageInformation")); 38 | File.WriteAllText(Path.Combine(options.OutputPath, "PackageInformation", $"{moniker}.json"), jsonString); 39 | 40 | Console.WriteLine("[info] Completed successfully: " + completed); 41 | }); 42 | 43 | stopwatch.Stop(); 44 | Console.WriteLine($"[info] Completed extraction in {stopwatch.Elapsed.TotalMinutes} minutes"); 45 | Debug.WriteLine($"[info] Completed extraction in {stopwatch.Elapsed.TotalMinutes} minutes"); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Nue/Nue.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.3 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nue", "Nue\Nue.csproj", "{68CC99C6-1DBE-4459-BA94-742E9FF7BC86}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nue.Core", "Nue.Core\Nue.Core.csproj", "{6D7568AE-C944-4C70-8DA6-54F983689972}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nue.StandardResolver", "Nue.StandardResolver\Nue.StandardResolver.csproj", "{C8046141-6E78-44B8-BAD8-D83A19E8E77B}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {68CC99C6-1DBE-4459-BA94-742E9FF7BC86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {68CC99C6-1DBE-4459-BA94-742E9FF7BC86}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {68CC99C6-1DBE-4459-BA94-742E9FF7BC86}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {68CC99C6-1DBE-4459-BA94-742E9FF7BC86}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {6D7568AE-C944-4C70-8DA6-54F983689972}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {6D7568AE-C944-4C70-8DA6-54F983689972}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {6D7568AE-C944-4C70-8DA6-54F983689972}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {6D7568AE-C944-4C70-8DA6-54F983689972}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {C8046141-6E78-44B8-BAD8-D83A19E8E77B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {C8046141-6E78-44B8-BAD8-D83A19E8E77B}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {C8046141-6E78-44B8-BAD8-D83A19E8E77B}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {C8046141-6E78-44B8-BAD8-D83A19E8E77B}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {7DE47469-1C7B-4BA2-8AB6-36AC275727CB} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /Nue/Nue.Core/PackageAtom.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace Nue.Core 7 | { 8 | public class PackageAtom 9 | { 10 | [JsonProperty("moniker")] 11 | public string Moniker { get; set; } 12 | 13 | [JsonProperty("id")] 14 | public string Name { get; set; } 15 | 16 | [JsonProperty("versionOption")] 17 | public VersionOption VersionOption { get; set; } 18 | 19 | [JsonProperty("customVersion")] 20 | public string CustomVersion { get; set; } 21 | 22 | [JsonIgnore] 23 | public bool CustomVersionDefined { get => VersionOption == VersionOption.Custom && !string.IsNullOrEmpty(CustomVersion); } 24 | 25 | [JsonProperty("isPrerelease")] 26 | public bool IsPrerelease { get; set; } 27 | 28 | public bool IsPowerShellPackage { get; set; } 29 | 30 | public bool IsDotnetPlatform { get; set; } 31 | 32 | [JsonProperty("customProperties")] 33 | public PackageAdditionalProperties CustomProperties { get; set; } 34 | 35 | public Dictionary CustomPropertyBag { get; set; } 36 | 37 | public string GetFullName() 38 | { 39 | var versionStr = VersionOption == VersionOption.Custom ? CustomVersion : VersionOption.ToString(); 40 | if (IsPrerelease) 41 | { 42 | versionStr += " -Prerelease"; 43 | } 44 | return $"{Name} [Version {versionStr}]"; 45 | } 46 | } 47 | 48 | public class PackageAdditionalProperties 49 | { 50 | [JsonProperty("feed")] 51 | public string CustomFeed { get; set; } 52 | 53 | [JsonProperty("libFolder")] 54 | public string CustomLibraryFolder { get; set; } 55 | 56 | [JsonProperty("depFolder")] 57 | public string CustomDependencyFolder { get; set; } 58 | 59 | [JsonProperty("tfm")] 60 | public string TFM { get; set; } 61 | 62 | [JsonProperty("excludedDlls")] 63 | public List ExcludedDlls { get; set; } 64 | } 65 | 66 | public enum VersionOption 67 | { 68 | Latest, 69 | Custom 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Nue/Nue/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📦 nue - the NuGet Package Extractor 2 | 3 | ![Build status](https://apidrop.visualstudio.com/_apis/public/build/definitions/97663bb1-33b9-48bf-ab0d-6ab65814469c/68/badge) 4 | 5 | `nue` is a tool developed to extract NuGet packages into a folder structure that is compatible with the [docs.microsoft.com](https://docs.microsoft.com) .NET API documentation infrastructure. 6 | 7 | It accepts a `*.csv` file as a source, and then relies on `nuget.exe` to install individual packages and collect their dependencies. 8 | 9 | ![Folder Breakdown](nue.png) 10 | 11 | ## To run 12 | 13 | The core executable is `nue.exe`. It accepts the following parameters: 14 | 15 | * `-p` or `--packages` - Path to package listing CSV file. See the structure for the file below. 16 | * `-o` or `--output` - Output path. It's acceptable if the folder does not yet exist, as _nue_ will create one for you. 17 | * `-f` or `--frameworks` - Framework for which to extract the packages. Use the [TFMs](https://docs.microsoft.com/en-us/nuget/schema/target-frameworks) reference to target folders in the `lib` folder of the main package. 18 | * `-n` or `--nugetpath` - Path to `nuget.exe` when working in `le` (local extraction) mode. This can be downloaded on the [official NuGet page](https://www.nuget.org/downloads). 19 | 20 | ### Input CSV structure 21 | 22 | When working with a list of packages, generally you need to follow the structure: 23 | 24 | ```text 25 | {package_moniker_base},{package_ID},{version} 26 | {package_moniker_base},{package_ID} 27 | {package_moniker_base},{package_ID},{version} 28 | ``` 29 | 30 | The moniker will be assembled by combining the `{package_moniker_base}` and `{version}`, in this form: `{package_moniker}-{version}`, where `{version}` is available. When the `{version}` value is not specified, the `{package_moniker_base}` will become the moniker. 31 | 32 | ### Behavior 33 | 34 | * When in the CSV, a version is specified after a package ID, that specific version will be installed and subsequently - processed. 35 | * If no version is specified after the package ID, the latest available version for the package will be installed (_can be either stable or pre-release, depending on configuration_). 36 | 37 | ### Custom package configuration 38 | 39 | In some cases, you might need to create custom package onboarding scenarios. To handle those, we are enabling custom parameters, that can be included before the package ID, in square brackets, as such: 40 | 41 | ```text 42 | {package_moniker},[custom_parameter=value]{package_ID} 43 | ``` 44 | 45 | The following custom parameters are supported: 46 | 47 | | Parameter | Description | 48 | |:----------|:------------| 49 | | `customSource` | URL to a custom feed for the package. | 50 | | `tfm` | Overrides the global TFM for the specific package. | 51 | | `altDep` | Alternative dependency TFM - helpful when you have a specific TFM for the core library, but a different TFM for dependency libraries. | 52 | | `isPrerelease` | Required to install a pre-release package. | 53 | | `customDependencyFolder` | A custom folder from which we need to pull dependencies. Relative to the package root. | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/PackageDownloder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.IO.Compression; 4 | using System.Net; 5 | 6 | namespace Nue.StandardResolver 7 | { 8 | public class PackageDownloder 9 | { 10 | private string rootUrl = "http://nuget.org/api/v2/package/"; 11 | private string outPutPath; 12 | private string fileName; 13 | private string packageId; 14 | private string version; 15 | private bool overwrite; 16 | public string Url 17 | { 18 | get 19 | { 20 | var url = CombineUriToString(rootUrl, packageId); 21 | if (!string.IsNullOrEmpty(version)) 22 | { 23 | url = $"{url}/{version}"; 24 | } 25 | 26 | return url; 27 | } 28 | } 29 | 30 | public PackageDownloder(string outPutPath, string packageId, string version, bool overwrite = false) 31 | { 32 | this.outPutPath = outPutPath; 33 | this.packageId = packageId; 34 | this.overwrite = overwrite; 35 | this.version = version; 36 | } 37 | 38 | public void DownloadPackage() 39 | { 40 | if (string.IsNullOrEmpty(outPutPath)) 41 | { 42 | Console.WriteLine($"[error] outPutPath cannot be empty"); return; 43 | } 44 | 45 | if (string.IsNullOrEmpty(packageId)) 46 | { 47 | Console.WriteLine($"[error] packageId cannot be empty"); return; 48 | } 49 | 50 | if (!Directory.Exists(outPutPath)) 51 | { 52 | Directory.CreateDirectory(outPutPath); 53 | } 54 | 55 | var request = WebRequest.Create(Url) as HttpWebRequest; 56 | var response = request.GetResponse() as HttpWebResponse; 57 | fileName = response.Headers["Content-Disposition"]; 58 | if (string.IsNullOrEmpty(fileName)) 59 | { 60 | fileName = response.ResponseUri.Segments[response.ResponseUri.Segments.Length - 1]; 61 | } 62 | else 63 | { 64 | fileName = fileName.Remove(0, fileName.IndexOf("filename=") + 9); 65 | } 66 | 67 | using (var responseStream = response.GetResponseStream()) 68 | { 69 | long totalLength = response.ContentLength; 70 | using (var stream = new FileStream(Path.Combine(outPutPath, fileName), overwrite ? FileMode.Create : FileMode.CreateNew)) 71 | { 72 | byte[] bArr = new byte[1024]; 73 | int size; 74 | while ((size = responseStream.Read(bArr, 0, bArr.Length)) > 0) 75 | { 76 | stream.Write(bArr, 0, size); 77 | } 78 | } 79 | } 80 | } 81 | 82 | public void Unzip() 83 | { 84 | ModifyFileNameExtension(); 85 | var sourceFileName = Path.Combine(outPutPath, fileName); 86 | var zipPath = Path.Combine(outPutPath, $"{Path.GetFileNameWithoutExtension(sourceFileName)}"); 87 | if (!Directory.Exists(zipPath)) 88 | { 89 | Directory.CreateDirectory(zipPath); 90 | } 91 | 92 | ZipFile.ExtractToDirectory(fileName, zipPath); 93 | } 94 | 95 | private void ModifyFileNameExtension() 96 | { 97 | var sourceFileName = Path.Combine(outPutPath, fileName); 98 | var destFileName = Path.Combine(outPutPath, $"{Path.GetFileNameWithoutExtension(sourceFileName)}.zip"); 99 | File.Move(sourceFileName, destFileName); 100 | fileName = destFileName; 101 | } 102 | 103 | private string CombineUriToString(string baseUri, string relativeOrAbsoluteUri) 104 | { 105 | return new Uri(new Uri(baseUri), relativeOrAbsoluteUri).ToString(); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/Nue.StandardResolver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C8046141-6E78-44B8-BAD8-D83A19E8E77B} 8 | Library 9 | Properties 10 | Nue.StandardResolver 11 | Nue.StandardResolver 12 | v4.6.1 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 37 | 38 | 39 | 40 | 41 | True 42 | 43 | 44 | 45 | 46 | 47 | 48 | ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll 49 | True 50 | True 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {6d7568ae-c944-4c70-8da6-54f983689972} 70 | Nue.Core 71 | 72 | 73 | 74 | 75 | 76 | Always 77 | 78 | 79 | Designer 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /Nue/Nue/Nue.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {68CC99C6-1DBE-4459-BA94-742E9FF7BC86} 8 | Exe 9 | Properties 10 | Nue 11 | Nue 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | 18 | 19 | 20 | AnyCPU 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | AnyCPU 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\packages\CommandLineParser.2.2.1\lib\net45\CommandLine.dll 41 | 42 | 43 | 44 | ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 45 | 46 | 47 | 48 | 49 | 50 | ..\packages\System.Console.4.3.1\lib\net46\System.Console.dll 51 | True 52 | 53 | 54 | 55 | 56 | 57 | ..\packages\System.Reflection.TypeExtensions.4.4.0\lib\net461\System.Reflection.TypeExtensions.dll 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | {6d7568ae-c944-4c70-8da6-54f983689972} 82 | Nue.Core 83 | 84 | 85 | {c8046141-6e78-44b8-bad8-d83a19e8e77b} 86 | Nue.StandardResolver 87 | 88 | 89 | 90 | 91 | 92 | 93 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 94 | 95 | 96 | 97 | 104 | -------------------------------------------------------------------------------- /Nue/Nue/Core/Extractor.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualBasic.FileIO; 2 | using Nue.StandardResolver; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text.RegularExpressions; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace Nue.Core 12 | { 13 | public class Extractor 14 | { 15 | public static void PreparePropertyBag(IEnumerable packages) 16 | { 17 | foreach (var package in packages) 18 | { 19 | if (package.CustomPropertyBag ==null) 20 | { 21 | package.CustomPropertyBag = new Dictionary(); 22 | } 23 | if (package.CustomProperties == null) 24 | { 25 | package.CustomProperties = new PackageAdditionalProperties(); 26 | } 27 | 28 | // Inject the TFM into the resolver if none was specified for the package. 29 | if (package.CustomPropertyBag.TryGetValue("tfm", out string tfmVal)) 30 | { 31 | package.CustomProperties.TFM = tfmVal; 32 | } 33 | 34 | // Determines whether a package is a PowerShell package - there is some custom logic that we need 35 | // to apply to determine what the assemblies are there. 36 | if (package.CustomPropertyBag.TryGetValue("ps", out string psVal)) 37 | { 38 | package.IsPowerShellPackage = Convert.ToBoolean(psVal); 39 | } 40 | 41 | if (package.CustomPropertyBag.TryGetValue("isPrerelease", out string preReleaseVal)) 42 | { 43 | package.IsPrerelease = Convert.ToBoolean(preReleaseVal); 44 | } 45 | 46 | if (package.CustomPropertyBag.TryGetValue("libpath", out string libpathVal)) 47 | { 48 | package.CustomProperties.CustomLibraryFolder = libpathVal; 49 | } 50 | 51 | if (package.CustomPropertyBag.TryGetValue("customDependencyFolder", out string depPathVal)) 52 | { 53 | package.CustomProperties.CustomDependencyFolder = depPathVal; 54 | } 55 | 56 | if (package.CustomPropertyBag.TryGetValue("customSource", out string feedVal)) 57 | { 58 | package.CustomProperties.CustomFeed = feedVal; 59 | } 60 | 61 | if (package.CustomPropertyBag.TryGetValue("excludedDlls", out string excludedDllVal)) 62 | { 63 | if (!string.IsNullOrEmpty(excludedDllVal)) 64 | { 65 | var excludedDlls = excludedDllVal.Split('|'); 66 | package.CustomProperties.ExcludedDlls = excludedDlls.Select(e=>Helpers.WildCardToRegex(e)).ToList(); 67 | } 68 | } 69 | } 70 | } 71 | 72 | public static bool DownloadPackages(string packagePath, RunSettings runSettings, out PackageInfomarionMapping pkgInfoMap) 73 | { 74 | pkgInfoMap = new PackageInfomarionMapping(); 75 | 76 | if (string.IsNullOrWhiteSpace(packagePath) || string.IsNullOrWhiteSpace(runSettings.OutputPath)) return false; 77 | 78 | var packages = GetPackagesFromFile(packagePath); 79 | 80 | PreparePropertyBag(packages); 81 | 82 | var assemblyPkgInfoMap = new AssemblyMappingPackageInformation(); 83 | 84 | foreach (var package in packages) 85 | { 86 | // Package resolver that will be used to get the full path to binaries. 87 | IPackageResolver resolver = new Resolver(); 88 | 89 | var currentOutputPrefix = Guid.NewGuid().ToString().Substring(0,5); 90 | var isSuccess = resolver.CopyBinarySet(package, runSettings, pkgInfoMap, assemblyPkgInfoMap, currentOutputPrefix); 91 | 92 | try 93 | { 94 | Console.WriteLine($"[info] Deleting {Path.Combine(runSettings.OutputPath, "_pacman" + currentOutputPrefix)}"); 95 | Helpers.DeleteDirectory(Path.Combine(runSettings.OutputPath, "_pacman" + currentOutputPrefix)); 96 | } 97 | catch 98 | { 99 | Console.WriteLine("[error] Errored out the first time we tried to delete the folder. Retrying..."); 100 | 101 | Thread.Sleep(2000); 102 | Helpers.DeleteDirectory(Path.Combine(runSettings.OutputPath, "_pacman" + currentOutputPrefix)); 103 | } 104 | } 105 | 106 | return true; 107 | } 108 | 109 | private static List GetPackagesFromFile(string packagePath) 110 | { 111 | if (packagePath.EndsWith(".csv")) 112 | { 113 | return GetPackagesFromCSVFile(packagePath); 114 | } 115 | else if (packagePath.EndsWith(".json")) 116 | { 117 | return Newtonsoft.Json.JsonConvert.DeserializeObject>(File.ReadAllText(packagePath)); 118 | } 119 | Console.WriteLine($"[error] Cannot recognize {packagePath}."); 120 | 121 | return null; 122 | } 123 | 124 | private static List GetPackagesFromCSVFile(string packagePath) 125 | { 126 | var packages = new List(); 127 | 128 | using (var parser = new TextFieldParser(packagePath)) 129 | { 130 | parser.TextFieldType = FieldType.Delimited; 131 | parser.SetDelimiters(","); 132 | 133 | while (!parser.EndOfData) 134 | { 135 | var fields = parser.ReadFields(); 136 | 137 | if (fields == null) continue; 138 | 139 | // Given the conventions, let's find out how many versions are requested to be downloaded. 140 | var pAtom = new PackageAtom(); 141 | 142 | if (fields.Length == 2) 143 | { 144 | // There is no version specified. 145 | pAtom.Moniker = fields[0]; 146 | pAtom.Name = fields[1]; 147 | pAtom.VersionOption = VersionOption.Latest; 148 | } 149 | else if (fields.Length > 2) 150 | { 151 | // There is a version specified. 152 | pAtom.Moniker = fields[0] + "-" + fields[2]; 153 | pAtom.Name = fields[1]; 154 | pAtom.VersionOption = VersionOption.Custom; 155 | pAtom.CustomVersion = fields[2]; 156 | } 157 | else 158 | { 159 | Console.WriteLine("[error] Could not read in package information for " + fields.ToString()); 160 | break; 161 | } 162 | 163 | // Property bag will be formatted like: 164 | // [property1=value1;property2=value2]PackageId 165 | var propertyBagRegex = @"(\[.+\])"; 166 | Regex formalizedRegEx = new Regex(propertyBagRegex); 167 | var match = formalizedRegEx.Match(pAtom.Name); 168 | 169 | if (match.Success) 170 | { 171 | // There seems to be a property bag attached to the name. 172 | var rawPropertyBag = match.Value.Replace("[", "").Replace("]", "").Trim(); 173 | if (!string.IsNullOrWhiteSpace(rawPropertyBag)) 174 | { 175 | // Normalize the package name without the property bag. 176 | pAtom.Name = pAtom.Name.Replace(match.Value, ""); 177 | pAtom.CustomPropertyBag = new Dictionary(); 178 | 179 | // Avoiding the case of empty property bag, looks like in this case we are good. 180 | var properties = rawPropertyBag.Split(new char[] { ';' }); 181 | foreach (var property in properties) 182 | { 183 | var splitProperty = property.Split(new char[] { '=' }); 184 | pAtom.CustomPropertyBag.Add(splitProperty[0], splitProperty[1]); 185 | } 186 | } 187 | } 188 | 189 | packages.Add(pAtom); 190 | } 191 | } 192 | 193 | return packages; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Nue/Nue.Core/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace Nue.Core 9 | { 10 | public static class Helpers 11 | { 12 | // From: http://stackoverflow.com/a/329502 13 | public static void DeleteDirectory(string target_dir) 14 | { 15 | if (Directory.Exists(target_dir)) 16 | { 17 | string[] files = Directory.GetFiles(target_dir); 18 | string[] dirs = Directory.GetDirectories(target_dir); 19 | 20 | foreach (string file in files) 21 | { 22 | File.SetAttributes(file, FileAttributes.Normal); 23 | File.Delete(file); 24 | } 25 | 26 | foreach (string dir in dirs) 27 | { 28 | DeleteDirectory(dir); 29 | } 30 | 31 | Directory.Delete(target_dir, true); 32 | } 33 | } 34 | 35 | // Determines the best folder match for a libary based on the specified target moniker. 36 | public static string GetBestLibMatch(string tfm, string[] folderPaths) 37 | { 38 | var tfmRegex = new Regex(@"(?[a-zA-Z]*)(?[0-9\.0-9]*)"); 39 | var match = tfmRegex.Match(tfm); 40 | 41 | var tfmBase = match.Groups["Base"].Value; 42 | var tfmVersion = match.Groups["Version"].Value; 43 | string folder = string.Empty; 44 | 45 | // Look for a folder that matches exactly the TFM. 46 | var exactMatch = (from c in folderPaths 47 | where Path.GetFileName(c).Equals(tfm, StringComparison.CurrentCultureIgnoreCase) 48 | select c).FirstOrDefault(); 49 | 50 | // If we found one, we should just return it. 51 | if (exactMatch != null) 52 | return exactMatch; 53 | 54 | // As an example, if the TFM is net45, this should cover everything like: 55 | // net45, net451, net452 56 | var lenientMatch = new Regex($@"^(?(?{tfm})(?[0-9\.0-9]*))$", RegexOptions.IgnoreCase); 57 | folder = GetWinningFolder(folderPaths, lenientMatch); 58 | 59 | if (!string.IsNullOrWhiteSpace(folder)) return folder; 60 | // As an example, if the TFM is netcoreapp3.0 or net5.0, this should cover everything like: 61 | // netstandard2.0, netstandard1.0 62 | var tfmBaseOfNetCore = "netstandard"; 63 | var netCoreRegex = new Regex($@"^(?(?{tfmBaseOfNetCore})(?[0-9\.0-9]*))$", RegexOptions.IgnoreCase); 64 | folder = GetWinningFolder(folderPaths, netCoreRegex); 65 | 66 | if (!string.IsNullOrWhiteSpace(folder)) return folder; 67 | // Now we just match the base, e.g. for net we should get: 68 | // net45, net46, net461 69 | var baseMatch = new Regex($@"^(?(?{tfmBase}[a-z]*)(?[0-9\.0-9]*))$", RegexOptions.IgnoreCase); 70 | folder = GetWinningFolder(folderPaths, baseMatch); 71 | 72 | if (!string.IsNullOrWhiteSpace(folder)) return folder; 73 | // Now do an even more lenient match within 74 | var preciseTfmRegex = new Regex($@"(?(?{tfmBase})(?[0-9\.0-9]+))", RegexOptions.IgnoreCase); 75 | folder = GetWinningFolder(folderPaths, preciseTfmRegex); 76 | 77 | 78 | if (!string.IsNullOrWhiteSpace(folder)) return folder; 79 | // Given that we have found nothing, is there anything that matches the first 3 characters? 80 | var broadAssumptionRegex = new Regex($@"(?(?{tfmBase.Substring(0, 3)})(?[0-9\.0-9]+))", RegexOptions.IgnoreCase); 81 | folder = GetWinningFolder(folderPaths, broadAssumptionRegex); 82 | 83 | 84 | 85 | return folder; 86 | } 87 | 88 | public static Regex WildCardToRegex(string pattern) 89 | { 90 | return new Regex("^" + Regex.Escape(pattern).Replace("\\?", ".").Replace("\\*", ".*") + "$", RegexOptions.Compiled); 91 | } 92 | 93 | public static bool CopyLibraryContent(string source, string destination, PackageAtom package, out List binaries) 94 | { 95 | binaries = new List(); 96 | var docFiles = new List(); 97 | 98 | try 99 | { 100 | binaries = Directory.GetFiles(source, "*.*", SearchOption.TopDirectoryOnly) 101 | .Where(s => s.EndsWith(".dll") || s.EndsWith(".winmd")).ToList(); 102 | if (package.CustomProperties.ExcludedDlls != null && package.CustomProperties.ExcludedDlls.Count != 0) 103 | { 104 | binaries = binaries.Where(b => !package.CustomProperties.ExcludedDlls.Any(d => d.IsMatch(Path.GetFileName(b)))).ToList(); 105 | } 106 | } 107 | catch 108 | { 109 | Console.WriteLine($"[error] Could not get binaries for {package.Name} from {source}."); 110 | return false; 111 | } 112 | 113 | 114 | foreach (var binary in binaries) 115 | File.Copy(binary, Path.Combine(destination, Path.GetFileName(binary)), true); 116 | 117 | try 118 | { 119 | docFiles = Directory.GetFiles(source, "*.xml", SearchOption.TopDirectoryOnly).ToList(); 120 | 121 | if (package.CustomProperties.ExcludedDlls != null && package.CustomProperties.ExcludedDlls.Count != 0) 122 | { 123 | docFiles = docFiles.Where(b => !package.CustomProperties.ExcludedDlls.Any(d => d.IsMatch(Path.GetFileName(b)))).ToList(); 124 | } 125 | 126 | foreach (var docFile in docFiles) 127 | File.Copy(docFile, Path.Combine(destination, Path.GetFileName(docFile)), true); 128 | } 129 | catch 130 | { 131 | Console.WriteLine($"[warning] Could not get documentation files for {package.Name} from {source}."); 132 | } 133 | 134 | return true; 135 | } 136 | 137 | public static string BuildCommandString(PackageAtom package, string rootPath, string configPath, RunSettings runSettings) 138 | { 139 | var baseline = $@"install {package.Name} -OutputDirectory ""{rootPath.Trim('"')}"" -Verbosity Quiet -FallbackSource https://api.nuget.org/v3/index.json -ConfigFile ""{configPath.Trim('"')}"""; 140 | 141 | if (!string.IsNullOrWhiteSpace(package.CustomProperties.TFM)) 142 | { 143 | baseline += $" -Framework {package.CustomProperties.TFM}"; 144 | } 145 | else if (!string.IsNullOrEmpty(runSettings.TFM)) 146 | { 147 | baseline += $" -Framework {runSettings.TFM}"; 148 | } 149 | 150 | if (!string.IsNullOrWhiteSpace(package.CustomProperties.CustomFeed)) 151 | { 152 | baseline += $" -Source {package.CustomProperties.CustomFeed}"; 153 | } 154 | else if (!string.IsNullOrEmpty(runSettings.Feed)) 155 | { 156 | baseline += $" -Source {runSettings.Feed}"; 157 | } 158 | 159 | if (package.CustomVersionDefined) 160 | { 161 | baseline += $" -Version {package.CustomVersion}"; 162 | } 163 | if (package.IsPrerelease) 164 | { 165 | baseline += " -PreRelease"; 166 | } 167 | 168 | return baseline; 169 | } 170 | 171 | private static string GetWinningFolder(string[] folders, Regex regex) 172 | { 173 | var folderAssociations = new Dictionary(); 174 | foreach (var folder in folders) 175 | { 176 | var exactFolderName = Path.GetFileName(folder); 177 | var token = regex.Match(exactFolderName); 178 | if (!token.Success) continue; 179 | var folderVersion = token.Groups["version"].Value; 180 | 181 | if (!string.IsNullOrEmpty(folderVersion)) 182 | { 183 | folderAssociations.Add(folder, folderVersion); 184 | } 185 | else 186 | { 187 | folderAssociations.Add(folder, "0"); 188 | } 189 | } 190 | 191 | if (folderAssociations.Count <= 0) return string.Empty; 192 | var topItem = (from c in folderAssociations orderby c.Value descending select c).First(); 193 | return topItem.Key; 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Nue/Nue.StandardResolver/Resolver.cs: -------------------------------------------------------------------------------- 1 | using Nue.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Reflection; 8 | using System.Text; 9 | namespace Nue.StandardResolver 10 | { 11 | public class Resolver : IPackageResolver 12 | { 13 | public bool CopyBinarySet( 14 | PackageAtom package, 15 | RunSettings runSettings, 16 | PackageInfomarionMapping pkgInfoMap, 17 | AssemblyMappingPackageInformation assemblyPkgInfoMap, 18 | string outputPrefix = "") 19 | { 20 | var tfm = package.CustomProperties.TFM ?? runSettings.TFM; 21 | var rootPath = runSettings.OutputPath + "\\_pacman" + outputPrefix; 22 | 23 | Console.WriteLine($"[info] Attempting to install: {package.GetFullName()}..."); 24 | 25 | string command = $"{runSettings.NugetPath}\\nuget.exe"; 26 | 27 | ProcessStartInfo cmdsi = new ProcessStartInfo(command) 28 | { 29 | UseShellExecute = false, 30 | RedirectStandardOutput = true, 31 | RedirectStandardError = true, 32 | }; 33 | 34 | var configPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "custom.nuget.config"); 35 | 36 | string commandString = Helpers.BuildCommandString(package, rootPath, configPath, runSettings); 37 | cmdsi.Arguments = commandString; 38 | Console.WriteLine($"[info] {command} {commandString}"); 39 | StringBuilder sb = new StringBuilder(); 40 | Process cmd = Process.Start(cmdsi); 41 | 42 | cmd.OutputDataReceived += (object sender, DataReceivedEventArgs e) => 43 | { 44 | if (!string.IsNullOrWhiteSpace(e.Data)) 45 | { 46 | sb.AppendLine(e.Data); 47 | } 48 | }; 49 | cmd.ErrorDataReceived += (object sender, DataReceivedEventArgs e) => 50 | { 51 | if (!string.IsNullOrWhiteSpace(e.Data)) 52 | { 53 | sb.AppendLine(e.Data); 54 | } 55 | }; 56 | 57 | cmd.BeginOutputReadLine(); 58 | cmd.BeginErrorReadLine(); 59 | 60 | cmd.WaitForExit(); 61 | var isContinue = true; 62 | if (cmd.ExitCode != 0) 63 | { 64 | // The package ( https://www.nuget.org/packages/Microsoft.AspNetCore.App.Ref/3.0.0) is marked as DotnetPlatform and therefore cannot be installed directly by nuget.exe. One possible solution is to directly download the .nupkg file and extra dlls from it. 65 | // Task377921:https://ceapex.visualstudio.com/Engineering/_workitems/edit/377921 66 | var msg = sb.ToString(); 67 | if (msg.Contains("package type 'DotnetPlatform'")) 68 | { 69 | package.IsDotnetPlatform = true; 70 | Console.WriteLine("[info] The package is marked as DotnetPlatform and therefore cannot be installed directly by nuget.exe. Package attempted: " + package.Name); 71 | var packageEngine = new PackageDownloder(rootPath, package.Name, package.CustomVersion, true); 72 | packageEngine.DownloadPackage(); 73 | packageEngine.Unzip(); 74 | } 75 | else 76 | { 77 | Console.WriteLine("[error] There was an error in NuGet installation. Package attempted: " + package.Name); 78 | isContinue = false; 79 | } 80 | } 81 | 82 | if (!isContinue) 83 | { 84 | return false; 85 | } 86 | else 87 | { 88 | var packageFqn = package.Name; 89 | if (package.CustomVersionDefined) 90 | { 91 | packageFqn += "." + package.CustomVersion; 92 | } 93 | 94 | var pacManPackagePath = Path.Combine(rootPath, packageFqn); 95 | 96 | string pacManPackageLibPath = ""; 97 | 98 | pacManPackagePath = (from c in Directory.GetDirectories(rootPath) where c.StartsWith(pacManPackagePath, StringComparison.InvariantCultureIgnoreCase) select c).FirstOrDefault(); 99 | 100 | var packageVersion = pacManPackagePath.Replace(Path.Combine(rootPath, package.Name + "."), ""); 101 | if (package.IsDotnetPlatform) 102 | { 103 | packageVersion = pacManPackagePath.Replace(Path.Combine(rootPath, package.Name.ToLowerInvariant() + "."), ""); 104 | } 105 | 106 | // In some cases, the lookup might be happening inside a custom path. 107 | // For PowerShell, this should be done inside the root directory. 108 | if (package.IsPowerShellPackage) 109 | { 110 | pacManPackageLibPath = pacManPackagePath; 111 | } 112 | else if (!string.IsNullOrWhiteSpace(package.CustomProperties?.CustomLibraryFolder)) 113 | { 114 | pacManPackageLibPath = Path.Combine(pacManPackagePath, package.CustomProperties.CustomLibraryFolder); 115 | } 116 | else 117 | { 118 | pacManPackageLibPath = pacManPackagePath + "\\lib"; 119 | } 120 | 121 | var packageFolderId = string.IsNullOrEmpty(package.Moniker) ? package.Name : package.Moniker; 122 | var packageContainerPath = Path.Combine(runSettings.OutputPath, packageFolderId); 123 | var packageDependencyContainerPath = Path.Combine(runSettings.OutputPath, "dependencies", packageFolderId); 124 | 125 | // Among other things, we need to make sure that the package was not already extracted for 126 | // another team. 127 | if (Directory.Exists(pacManPackageLibPath) && !Directory.Exists(packageContainerPath)) 128 | { 129 | Directory.CreateDirectory(packageContainerPath); 130 | 131 | // If we are dealing with a different PowerShell package, we might need to operate slightly 132 | // differently givent that the structure is not at all reflective of what other NuGet packages encompass. 133 | if (package.IsPowerShellPackage) 134 | { 135 | Console.WriteLine($"[info] Treating {package.Name} as a PowerShell package."); 136 | 137 | var helpXmlFiles = from c in Directory.GetFiles(pacManPackageLibPath) 138 | where Path.GetFileName(c).ToLower().EndsWith("-help.xml") 139 | select c; 140 | 141 | var dllFiles = new List(); 142 | 143 | foreach (var helpXmlFile in helpXmlFiles) 144 | { 145 | var workingDll = Path.GetFileName(helpXmlFile).ToLower().Replace("-help.xml", ""); 146 | if (File.Exists(Path.Combine(pacManPackageLibPath, workingDll))) 147 | { 148 | dllFiles.Add(workingDll); 149 | } 150 | } 151 | 152 | if (dllFiles.Any()) 153 | { 154 | foreach (var dll in dllFiles) 155 | { 156 | File.Copy(Path.Combine(pacManPackageLibPath, dll), Path.Combine(packageContainerPath, dll), true); 157 | //File.Copy(Path.Combine(pacManPackageLibPath, dll + "-help.xml"), Path.Combine(packageContainerPath, Path.GetFileNameWithoutExtension(dll) + ".xml"), true); 158 | } 159 | 160 | var dependencies = (from c in Directory.GetFiles(pacManPackageLibPath) 161 | where !dllFiles.Contains(Path.GetFileName(c).ToLower()) && Path.GetFileName(c).EndsWith(".dll") 162 | select c).ToList(); 163 | if ((tfm.StartsWith("net46") || tfm.StartsWith("net47") || tfm.StartsWith("net48")) 164 | && Directory.Exists(Path.Combine(pacManPackageLibPath, "PreloadAssemblies"))) 165 | { 166 | dependencies.AddRange(Directory.GetFiles(Path.Combine(pacManPackageLibPath, "PreloadAssemblies"))); 167 | } 168 | if (tfm.StartsWith("netcoreapp") 169 | && Directory.Exists(Path.Combine(pacManPackageLibPath, "NetCoreAssemblies"))) 170 | { 171 | dependencies.AddRange(Directory.GetFiles(Path.Combine(pacManPackageLibPath, "NetCoreAssemblies"))); 172 | } 173 | if (dependencies.Count > 0) 174 | { 175 | Directory.CreateDirectory(packageDependencyContainerPath); 176 | 177 | foreach (var dependency in dependencies) 178 | { 179 | 180 | File.Copy(dependency, Path.Combine(packageDependencyContainerPath, Path.GetFileName(dependency)), true); 181 | } 182 | } 183 | } 184 | } 185 | else 186 | { 187 | var dependencyFolders = new List(); 188 | 189 | // Directory exists, so we should proceed to package extraction. 190 | var directories = Directory.GetDirectories(pacManPackageLibPath); 191 | var closestDirectory = Helpers.GetBestLibMatch(tfm, directories); 192 | 193 | try 194 | { 195 | dependencyFolders = (from c in Directory.GetDirectories(rootPath) 196 | where Path.GetFileName(c).ToLower() != packageFqn.ToLower() 197 | select c).ToList(); 198 | } 199 | catch 200 | { 201 | Console.WriteLine($"[warning] Could not create list of dependencies for {package.Name}"); 202 | } 203 | 204 | // It might be possible that the author specified an additional dependency folder. 205 | // If that is the case, we are just going to add it to the existing set of folders. 206 | if (!string.IsNullOrEmpty(package.CustomProperties.CustomDependencyFolder)) 207 | { 208 | dependencyFolders.Add(Path.Combine(pacManPackagePath, package.CustomProperties.CustomDependencyFolder)); 209 | } 210 | 211 | string informationalPackageString = $"[info] Currently available library sets for {package.Name}\n"; 212 | 213 | foreach (var folder in directories) 214 | { 215 | var tfmFolder = Path.GetFileName(folder); 216 | informationalPackageString += " |___" + tfmFolder + "\n"; 217 | } 218 | 219 | Console.WriteLine(informationalPackageString); 220 | 221 | if (dependencyFolders.Any()) 222 | { 223 | informationalPackageString = $"[info] Package dependencies for {package.Name}\n"; 224 | 225 | foreach (var dependency in dependencyFolders) 226 | { 227 | informationalPackageString += " |___" + Path.GetFileNameWithoutExtension(dependency) + "\n"; 228 | } 229 | 230 | Console.WriteLine(informationalPackageString); 231 | } 232 | 233 | var frameworkIsAvailable = !string.IsNullOrWhiteSpace(closestDirectory); 234 | 235 | bool capturedContent = false; 236 | List binaries = null; 237 | if (frameworkIsAvailable) 238 | { 239 | capturedContent = Helpers.CopyLibraryContent(closestDirectory, packageContainerPath, package, out binaries); 240 | } 241 | else 242 | { 243 | capturedContent = Helpers.CopyLibraryContent(pacManPackageLibPath, packageContainerPath, package, out binaries); 244 | } 245 | 246 | // record the assembly => package mapping 247 | var packageInfo = new PackageInfomarion() 248 | { 249 | Name = package.Name, 250 | Version = packageVersion, 251 | Feed = runSettings.Feed 252 | }; 253 | if (!pkgInfoMap.ContainsKey(packageFolderId)) 254 | { 255 | pkgInfoMap[packageFolderId] = new Dictionary(); 256 | } 257 | foreach (var binary in binaries) 258 | { 259 | AssemblyPackageInformationMap(binary, assemblyPkgInfoMap, packageInfo); 260 | var assemblyName = Path.GetFileNameWithoutExtension(binary); 261 | pkgInfoMap[packageFolderId][assemblyName] = packageInfo; 262 | } 263 | 264 | // Only process dependencies if we actually captured binary content. 265 | if (capturedContent) 266 | { 267 | if (package.CustomProperties.ExcludedDlls != null && package.CustomProperties.ExcludedDlls.Count != 0) 268 | { 269 | var excludedDllDirectory = pacManPackageLibPath; 270 | if (frameworkIsAvailable) 271 | { 272 | excludedDllDirectory = closestDirectory; 273 | } 274 | 275 | if (!Directory.Exists(packageDependencyContainerPath)) 276 | { 277 | Directory.CreateDirectory(packageDependencyContainerPath); 278 | } 279 | 280 | var dlls = Directory.EnumerateFiles(excludedDllDirectory, "*.*", SearchOption.TopDirectoryOnly) 281 | .Where(s => s.EndsWith(".dll")); 282 | 283 | foreach (var dll in dlls) 284 | { 285 | if (package.CustomProperties.ExcludedDlls.Any(d => d.IsMatch(Path.GetFileName(dll)))) 286 | { 287 | File.Copy(dll, 288 | Path.Combine(packageDependencyContainerPath, Path.GetFileName(dll)), 289 | true); 290 | } 291 | } 292 | } 293 | 294 | if (dependencyFolders.Any()) 295 | { 296 | foreach (var dependency in dependencyFolders) 297 | { 298 | var availableDependencyMonikers = new List(); 299 | 300 | var targetPath = Path.Combine(dependency, "lib"); 301 | if (Directory.Exists(targetPath) && Directory.GetFiles(targetPath, "*.*", SearchOption.AllDirectories) 302 | .Where(s => s.EndsWith(".dll") || s.EndsWith(".winmd")).Count() > 0) 303 | { 304 | List alternateDependencies = new List(); 305 | 306 | // In some cases, we might want to have alterhative dependency monikers. 307 | if (package.CustomPropertyBag.ContainsKey("altDep")) 308 | { 309 | alternateDependencies = new List(package.CustomPropertyBag["altDep"].Split('|')); 310 | } 311 | 312 | var dependencyLibFolders = Directory.GetDirectories(Path.Combine(dependency, "lib")); 313 | var closestDepLibFolder = Helpers.GetBestLibMatch(tfm, dependencyLibFolders); 314 | 315 | if (string.IsNullOrWhiteSpace(closestDepLibFolder)) 316 | { 317 | // We could not find a regular TFM dependency, let's try again for alternates. 318 | if (alternateDependencies.Count > 0) 319 | { 320 | foreach (var altDependency in alternateDependencies) 321 | { 322 | closestDepLibFolder = Helpers.GetBestLibMatch(altDependency, dependencyLibFolders); 323 | if (!string.IsNullOrWhiteSpace(closestDepLibFolder)) 324 | break; 325 | } 326 | } 327 | } 328 | 329 | var dFrameworkIsAvailable = !string.IsNullOrWhiteSpace(closestDepLibFolder); 330 | 331 | if (dFrameworkIsAvailable) 332 | { 333 | if (!Directory.Exists(packageDependencyContainerPath)) 334 | { 335 | Directory.CreateDirectory(packageDependencyContainerPath); 336 | } 337 | 338 | var dependencyBinaries = Directory.EnumerateFiles(closestDepLibFolder, "*.*", SearchOption.TopDirectoryOnly) 339 | .Where(s => s.EndsWith(".dll") || s.EndsWith(".winmd")); 340 | 341 | foreach (var binary in dependencyBinaries) 342 | File.Copy(binary, 343 | Path.Combine(packageDependencyContainerPath, Path.GetFileName(binary)), 344 | true); 345 | } 346 | } 347 | else 348 | { 349 | // The "lib" folder does not exist, so let's just look in the root. 350 | var dependencyBinaries = Directory.EnumerateFiles(dependency, "*.*", SearchOption.TopDirectoryOnly) 351 | .Where(s => s.EndsWith(".dll") || s.EndsWith(".winmd")); 352 | 353 | foreach (var binary in dependencyBinaries) 354 | File.Copy(binary, 355 | Path.Combine(packageDependencyContainerPath, Path.GetFileName(binary)), 356 | true); 357 | } 358 | } 359 | } 360 | else if(package.CustomProperties.ExcludedDlls == null || package.CustomProperties.ExcludedDlls.Count == 0) 361 | { 362 | Console.WriteLine($"[warning] No dependencies captured for {package.Name}"); 363 | } 364 | } 365 | else 366 | { 367 | Console.WriteLine($"[error] No binaries captured for {package.Name}"); 368 | return false; 369 | } 370 | } 371 | } 372 | return true; 373 | } 374 | } 375 | 376 | private void AssemblyPackageInformationMap(string binary, AssemblyMappingPackageInformation assemblyPkgInfoMap, PackageInfomarion packageInfo) 377 | { 378 | var assemblyName = Path.GetFileName(binary); 379 | if (!assemblyPkgInfoMap.ContainsKey(assemblyName)) 380 | { 381 | assemblyPkgInfoMap[assemblyName] = new List(); 382 | } 383 | 384 | var dependencyPackages = assemblyPkgInfoMap[assemblyName]; 385 | dependencyPackages.Add(packageInfo); 386 | 387 | if (dependencyPackages.Count > 1) 388 | { 389 | string informationalPackageStringOfDependency = $"[warning] {assemblyName} already exists in the following packages\n"; 390 | foreach (var item in dependencyPackages) 391 | { 392 | informationalPackageStringOfDependency += " |___" + item.Name + " " + item.Version + "\n"; 393 | } 394 | 395 | Console.WriteLine(informationalPackageStringOfDependency); 396 | } 397 | 398 | } 399 | } 400 | } 401 | --------------------------------------------------------------------------------