From d3442f35904e8ae5c60fe03c6818f206bc32a6db Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Tue, 11 Sep 2018 16:20:07 -0700 Subject: [PATCH] Create a single Microsoft.Extensions.ApiDescription.Client package - #8428 - add signing-related and PackageVerifier configuration for new package - remove packaging configuration from dotnet-getdocument project - adjust `dotnet-getdocument` invocation to its new location - remove use of nonexistent (ignored) `dotnet-getdocument --no-build` option Remove `--uri` feature from `dotnet-getdocument` - reduce dependencies from Microsoft.AspNetCore.TestHost to Microsoft.AspNetCore.Hosting.Abstractions - assume web site depends on that - merge `DownloadFileCore` into `DownloadFile` - remove other unecessary code e.g. `WrappedException` was never `throw`n Correct issues in `DownloadFile` - e.g. dispose of `responseStream`, use `await` more, support FIPS-compliant machines nits: - clean up `Project` and the metadata it fetches - remove unnecessary `.props` and `.targets` files --- NuGetPackageVerifier.json | 19 ++- build/dependencies.props | 4 +- .../Commands/GetDocumentCommand.cs | 11 +- .../Commands/GetDocumentCommandContext.cs | 2 - .../Commands/GetDocumentCommandWorker.cs | 112 ++------------- src/GetDocumentInsider/DownloadFileCore.cs | 121 ---------------- .../GetDocumentInsider.csproj | 6 +- src/GetDocumentInsider/ILogWrapper.cs | 53 ------- src/GetDocumentInsider/Json.cs | 18 --- src/GetDocumentInsider/LogWrapper.cs | 31 ---- src/GetDocumentInsider/Program.cs | 5 +- .../Properties/Resources.Designer.cs | 126 ---------------- src/GetDocumentInsider/Resources.resx | 29 +--- src/GetDocumentInsider/WrappedException.cs | 24 ---- .../DownloadFile.cs | 134 ++++++++++++++++-- .../LogWrapper.cs | 38 ----- ...ft.Extensions.ApiDescription.Client.csproj | 47 ++++-- ...ft.Extensions.ApiDescription.Client.nuspec | 16 ++- .../ServiceProjectReferenceMetadata.targets | 32 ----- ...oft.Extensions.ApiDescription.Client.props | 5 - ...t.Extensions.ApiDescription.Client.targets | 5 +- .../Commands/InvokeCommand.cs | 6 - src/dotnet-getdocument/Project.cs | 81 +++++------ .../Properties/Resources.Designer.cs | 4 +- src/dotnet-getdocument/Resources.resx | 2 +- .../ServiceProjectReferenceMetadata.props | 17 --- .../ServiceProjectReferenceMetadata.targets | 26 ++-- .../dotnet-getdocument.csproj | 68 +-------- .../dotnet-getdocument.nuspec | 28 ---- 29 files changed, 261 insertions(+), 809 deletions(-) delete mode 100644 src/GetDocumentInsider/DownloadFileCore.cs delete mode 100644 src/GetDocumentInsider/ILogWrapper.cs delete mode 100644 src/GetDocumentInsider/Json.cs delete mode 100644 src/GetDocumentInsider/LogWrapper.cs delete mode 100644 src/GetDocumentInsider/WrappedException.cs delete mode 100644 src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs delete mode 100644 src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets delete mode 100644 src/dotnet-getdocument/ServiceProjectReferenceMetadata.props delete mode 100644 src/dotnet-getdocument/dotnet-getdocument.nuspec diff --git a/NuGetPackageVerifier.json b/NuGetPackageVerifier.json index b153ab1515..079b7cef51 100644 --- a/NuGetPackageVerifier.json +++ b/NuGetPackageVerifier.json @@ -1,7 +1,16 @@ { - "Default": { - "rules": [ - "DefaultCompositeRule" - ] + "Default": { + "rules": [ + "DefaultCompositeRule" + ], + "packages": { + "Microsoft.Extensions.ApiDescription.Client": { + "Exclusions": { + "BUILD_ITEMS_FRAMEWORK": { + "*": "Package includes tool with different target frameworks." + } + } + } } -} \ No newline at end of file + } +} diff --git a/build/dependencies.props b/build/dependencies.props index 304c16413f..eed739b78e 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -7,7 +7,7 @@ is not otherwise referenced. They avoid unnecessary changes to the Universe build graph or to product dependencies. Do not use these properties elsewhere. --> - + 0.9.9 0.10.13 2.1.1 @@ -32,6 +32,7 @@ 2.2.0-preview3-35359 2.2.0-preview3-35359 2.2.0-preview3-35359 + 2.0.0 2.2.0-preview3-35359 2.2.0-preview3-35359 2.2.0-preview3-35359 @@ -54,7 +55,6 @@ 2.2.0-preview3-35359 2.2.0-preview3-35359 2.2.0-preview3-35359 - 2.0.0 2.2.0-preview3-35359 2.2.0-preview3-35359 5.2.6 diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommand.cs b/src/GetDocumentInsider/Commands/GetDocumentCommand.cs index 583031b324..cd45255f09 100644 --- a/src/GetDocumentInsider/Commands/GetDocumentCommand.cs +++ b/src/GetDocumentInsider/Commands/GetDocumentCommand.cs @@ -17,13 +17,11 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands internal const string FallbackDocumentName = "v1"; internal const string FallbackMethod = "Generate"; internal const string FallbackService = "Microsoft.Extensions.ApiDescription.IDocumentProvider"; - private const string WorkerType = "Microsoft.Extensions.ApiDescription.Client.Commands.GetDocumentCommandWorker"; private CommandOption _documentName; private CommandOption _method; private CommandOption _output; private CommandOption _service; - private CommandOption _uri; public override void Configure(CommandLineApplication command) { @@ -35,7 +33,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands _method = command.Option("--method ", Resources.FormatMethodDescription(FallbackMethod)); _output = command.Option("--output ", Resources.OutputDescription); _service = command.Option("--service ", Resources.FormatServiceDescription(FallbackService)); - _uri = command.Option("--uri ", Resources.UriDescription); } protected override void Validate() @@ -131,12 +128,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands #error target frameworks need to be updated. #endif - // Now safe to reference TestHost type. + // Now safe to reference the application's code. try { - var workerType = thisAssembly.GetType(WorkerType, throwOnError: true); - var methodInfo = workerType.GetMethod("Process", BindingFlags.Public | BindingFlags.Static); - var assemblyPath = AssemblyPath.Value(); var context = new GetDocumentCommandContext { @@ -147,10 +141,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands Method = _method.Value(), Output = _output.Value(), Service = _service.Value(), - Uri = _uri.Value(), }; - return (int)methodInfo.Invoke(obj: null, parameters: new[] { context }); + return GetDocumentCommandWorker.Process(context); } catch (Exception ex) { diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs b/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs index a109977c20..c4fc0b6e45 100644 --- a/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs +++ b/src/GetDocumentInsider/Commands/GetDocumentCommandContext.cs @@ -21,7 +21,5 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands public string Output { get; set; } public string Service { get; set; } - - public string Uri { get; set; } } } diff --git a/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs b/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs index ac90640171..b0434e453c 100644 --- a/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs +++ b/src/GetDocumentInsider/Commands/GetDocumentCommandWorker.cs @@ -2,15 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.IO; using System.Reflection; -using System.Threading; -using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.ApiDescription.Client.Commands { @@ -34,25 +29,10 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands } var success = TryProcess(context, services); - if (!success && string.IsNullOrEmpty(context.Uri)) + if (!success) { - return 4; - } - - var builder = GetBuilder(entryPointType, context.AssemblyPath, context.AssemblyName); - if (builder == null) - { - return 5; - } - - // Mute the HttpsRedirectionMiddleware warning about HTTPS configuration. - builder.ConfigureLogging(loggingBuilder => loggingBuilder.AddFilter( - "Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware", - LogLevel.Error)); - - using (var server = new TestServer(builder)) - { - ProcessAsync(context, server).Wait(); + // As part of the aspnet/Mvc#8425 fix, return 4 here. + return 0; } return 0; @@ -95,15 +75,9 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands if (!success) { + // As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists. var message = Resources.FormatMethodInvocationFailed(methodName, serviceName, documentName); - if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output)) - { - Reporter.WriteError(message); - } - else - { - Reporter.WriteWarning(message); - } + Reporter.WriteWarning(message); } return success; @@ -111,35 +85,14 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands catch (Exception ex) { var message = FormatException(ex); - if (string.IsNullOrEmpty(context.Uri) && !File.Exists(context.Output)) - { - Reporter.WriteError(message); - } - else - { - Reporter.WriteWarning(message); - } + + // As part of the aspnet/Mvc#8425 fix, make this an error unless the file already exists. + Reporter.WriteWarning(message); return false; } } - public static async Task ProcessAsync(GetDocumentCommandContext context, TestServer server) - { - - Debug.Assert(!string.IsNullOrEmpty(context.Uri)); - Reporter.WriteInformation(Resources.FormatUsingUri(context.Uri)); - - var httpClient = server.CreateClient(); - await DownloadFileCore.DownloadAsync( - context.Uri, - context.Output, - httpClient, - new LogWrapper(), - CancellationToken.None, - timeoutSeconds: 60); - } - // TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available. private static IServiceProvider GetServices(Type entryPointType, string assemblyPath, string assemblyName) { @@ -203,54 +156,7 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands } } - // Startup - return new WebHostBuilder().UseStartup(assemblyName).Build().Services; - } - - // TODO: Use Microsoft.AspNetCore.Hosting.WebHostBuilderFactory.Sources once we have dev feed available. - private static IWebHostBuilder GetBuilder(Type entryPointType, string assemblyPath, string assemblyName) - { - var methodInfo = entryPointType.GetMethod("BuildWebHost"); - if (methodInfo != null) - { - // BuildWebHost cannot be used. Fall through, most likely to Startup fallback. - Reporter.WriteWarning( - "BuildWebHost method cannot be used. Falling back to minimal Startup configuration."); - } - - methodInfo = entryPointType.GetMethod("CreateWebHostBuilder"); - if (methodInfo != null) - { - // CreateWebHostBuilder - var parameters = methodInfo.GetParameters(); - if (!methodInfo.IsStatic || - parameters.Length != 1 || - typeof(string[]) != parameters[0].ParameterType || - typeof(IWebHostBuilder) != methodInfo.ReturnType) - { - Reporter.WriteError( - "CreateWebHostBuilder method found in {assemblyPath} does not have expected signature."); - - return null; - } - - try - { - var args = new[] { Array.Empty() }; - var builder = (IWebHostBuilder)methodInfo.Invoke(obj: null, parameters: args); - - return builder; - } - catch (Exception ex) - { - Reporter.WriteError($"CreateWebHostBuilder method threw: {FormatException(ex)}"); - - return null; - } - } - - // Startup - return new WebHostBuilder().UseStartup(assemblyName); + return null; } private static string FormatException(Exception exception) diff --git a/src/GetDocumentInsider/DownloadFileCore.cs b/src/GetDocumentInsider/DownloadFileCore.cs deleted file mode 100644 index 7bae9e0227..0000000000 --- a/src/GetDocumentInsider/DownloadFileCore.cs +++ /dev/null @@ -1,121 +0,0 @@ -// 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.IO; -using System.Net.Http; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - internal static class DownloadFileCore - { - public static async Task DownloadAsync( - string uri, - string destinationPath, - HttpClient httpClient, - ILogWrapper log, - CancellationToken cancellationToken, - int timeoutSeconds) - { - // Timeout if the response has not begun within 1 minute - httpClient.Timeout = TimeSpan.FromMinutes(1); - - var destinationExists = File.Exists(destinationPath); - var reachedCopy = false; - try - { - using (var response = await httpClient.GetAsync(uri, cancellationToken)) - { - response.EnsureSuccessStatusCode(); - cancellationToken.ThrowIfCancellationRequested(); - - using (var responseStreamTask = response.Content.ReadAsStreamAsync()) - { - var finished = await Task.WhenAny( - responseStreamTask, - Task.Delay(TimeSpan.FromSeconds(timeoutSeconds))); - - if (!ReferenceEquals(responseStreamTask, finished)) - { - throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds."); - } - - var responseStream = await responseStreamTask; - if (destinationExists) - { - // Check hashes before using the downloaded information. - var downloadHash = GetHash(responseStream); - responseStream.Position = 0L; - - byte[] destinationHash; - using (var destinationStream = File.OpenRead(destinationPath)) - { - destinationHash = GetHash(destinationStream); - } - - var sameHashes = downloadHash.LongLength == destinationHash.LongLength; - for (var i = 0L; sameHashes && i < downloadHash.LongLength; i++) - { - sameHashes = downloadHash[i] == destinationHash[i]; - } - - if (sameHashes) - { - log.LogInformational($"Not overwriting existing and matching file '{destinationPath}'."); - return; - } - } - else - { - // May need to create directory to hold the file. - var destinationDirectory = Path.GetDirectoryName(destinationPath); - if (!(string.IsNullOrEmpty(destinationDirectory) || Directory.Exists(destinationDirectory))) - { - Directory.CreateDirectory(destinationDirectory); - } - } - - // Create or overwrite the destination file. - reachedCopy = true; - using (var outStream = File.Create(destinationPath)) - { - responseStream.CopyTo(outStream); - } - } - } - } - catch (HttpRequestException ex) when (destinationExists) - { - if (ex.InnerException is SocketException socketException) - { - log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'."); - } - else - { - log.LogWarning($"Unable to download {uri}: {ex.Message}"); - } - } - catch (Exception ex) - { - log.LogError($"Downloading '{uri}' failed."); - log.LogError(ex, showStackTrace: true); - if (reachedCopy) - { - File.Delete(destinationPath); - } - } - } - - private static byte[] GetHash(Stream stream) - { - using (var algorithm = SHA256.Create()) - { - return algorithm.ComputeHash(stream); - } - } - } -} diff --git a/src/GetDocumentInsider/GetDocumentInsider.csproj b/src/GetDocumentInsider/GetDocumentInsider.csproj index 93849f9cc4..b2c4fed5c6 100644 --- a/src/GetDocumentInsider/GetDocumentInsider.csproj +++ b/src/GetDocumentInsider/GetDocumentInsider.csproj @@ -13,6 +13,10 @@ - + + + + + diff --git a/src/GetDocumentInsider/ILogWrapper.cs b/src/GetDocumentInsider/ILogWrapper.cs deleted file mode 100644 index 261e6df868..0000000000 --- a/src/GetDocumentInsider/ILogWrapper.cs +++ /dev/null @@ -1,53 +0,0 @@ -// 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; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - internal interface ILogWrapper - { - /// - /// Logs specified informational . Implementations should be thread safe. - /// - /// The message to log. - /// Optional arguments for formatting the string. - /// - /// Thrown when is . - /// - void LogInformational(string message, params object[] messageArgs); - - /// - /// Logs a warning with the specified . Implementations should be thread safe. - /// - /// The message to log. - /// Optional arguments for formatting the string. - /// - /// Thrown when is . - /// - void LogWarning(string message, params object[] messageArgs); - - /// - /// Logs an error with the specified . Implementations should be thread safe. - /// - /// The message to log. - /// Optional arguments for formatting the string. - /// - /// Thrown when is . - /// - void LogError(string message, params object[] messageArgs); - - /// - /// Logs an error with the message and (optionally) the stack trace of the given . - /// Implementations should be thread safe. - /// - /// The to log. - /// - /// If , append stack trace to 's message. - /// - /// - /// Thrown when is . - /// - void LogError(Exception exception, bool showStackTrace); - } -} diff --git a/src/GetDocumentInsider/Json.cs b/src/GetDocumentInsider/Json.cs deleted file mode 100644 index 53885c5825..0000000000 --- a/src/GetDocumentInsider/Json.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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 Microsoft.DotNet.Cli.CommandLine; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - internal static class Json - { - public static CommandOption ConfigureOption(CommandLineApplication command) - => command.Option("--json", Resources.JsonDescription); - - public static string Literal(string text) - => text != null - ? "\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"" - : "null"; - } -} diff --git a/src/GetDocumentInsider/LogWrapper.cs b/src/GetDocumentInsider/LogWrapper.cs deleted file mode 100644 index e7c4b7b8c7..0000000000 --- a/src/GetDocumentInsider/LogWrapper.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - public class LogWrapper : ILogWrapper - { - public void LogError(string message, params object[] messageArgs) - { - Reporter.WriteError(string.Format(message, messageArgs)); - } - - public void LogError(Exception exception, bool showStackTrace) - { - var message = showStackTrace ? exception.ToString() : exception.Message; - Reporter.WriteError(message); - } - - public void LogInformational(string message, params object[] messageArgs) - { - Reporter.WriteInformation(string.Format(message, messageArgs)); - } - - public void LogWarning(string message, params object[] messageArgs) - { - Reporter.WriteWarning(string.Format(message, messageArgs)); - } - } -} diff --git a/src/GetDocumentInsider/Program.cs b/src/GetDocumentInsider/Program.cs index 2f2f43a4c0..af1bdc298a 100644 --- a/src/GetDocumentInsider/Program.cs +++ b/src/GetDocumentInsider/Program.cs @@ -30,10 +30,7 @@ namespace Microsoft.Extensions.ApiDescription.Client } catch (Exception ex) { - if (ex is CommandException - || ex is CommandParsingException - || (ex is WrappedException wrappedException - && wrappedException.Type == "Microsoft.Extensions.ApiDescription.Client.Design.OperationException")) + if (ex is CommandException || ex is CommandParsingException) { Reporter.WriteVerbose(ex.ToString()); } diff --git a/src/GetDocumentInsider/Properties/Resources.Designer.cs b/src/GetDocumentInsider/Properties/Resources.Designer.cs index 460ca76235..c9576ef8c0 100644 --- a/src/GetDocumentInsider/Properties/Resources.Designer.cs +++ b/src/GetDocumentInsider/Properties/Resources.Designer.cs @@ -24,20 +24,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatAssemblyDescription() => GetString("AssemblyDescription"); - /// - /// Show JSON output. - /// - internal static string JsonDescription - { - get => GetString("JsonDescription"); - } - - /// - /// Show JSON output. - /// - internal static string FormatJsonDescription() - => GetString("JsonDescription"); - /// /// Missing required option '--{0}'. /// @@ -94,48 +80,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatPrefixDescription() => GetString("PrefixDescription"); - /// - /// Using application base '{0}'. - /// - internal static string UsingApplicationBase - { - get => GetString("UsingApplicationBase"); - } - - /// - /// Using application base '{0}'. - /// - internal static string FormatUsingApplicationBase(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("UsingApplicationBase"), p0); - - /// - /// Using assembly '{0}'. - /// - internal static string UsingAssembly - { - get => GetString("UsingAssembly"); - } - - /// - /// Using assembly '{0}'. - /// - internal static string FormatUsingAssembly(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("UsingAssembly"), p0); - - /// - /// Using configuration file '{0}'. - /// - internal static string UsingConfigurationFile - { - get => GetString("UsingConfigurationFile"); - } - - /// - /// Using configuration file '{0}'. - /// - internal static string FormatUsingConfigurationFile(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("UsingConfigurationFile"), p0); - /// /// Show verbose output. /// @@ -150,34 +94,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatVerboseDescription() => GetString("VerboseDescription"); - /// - /// Writing '{0}'... - /// - internal static string WritingFile - { - get => GetString("WritingFile"); - } - - /// - /// Writing '{0}'... - /// - internal static string FormatWritingFile(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0); - - /// - /// Using working directory '{0}'. - /// - internal static string UsingWorkingDirectory - { - get => GetString("UsingWorkingDirectory"); - } - - /// - /// Using working directory '{0}'. - /// - internal static string FormatUsingWorkingDirectory(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("UsingWorkingDirectory"), p0); - /// /// Location from which inside man was copied (in the .NET Framework case) or loaded. /// @@ -192,20 +108,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatToolsDirectoryDescription() => GetString("ToolsDirectoryDescription"); - /// - /// The URI to download the document from. - /// - internal static string UriDescription - { - get => GetString("UriDescription"); - } - - /// - /// The URI to download the document from. - /// - internal static string FormatUriDescription() - => GetString("UriDescription"); - /// /// The name of the method to invoke on the '--service' instance. Default value '{0}'. /// @@ -234,20 +136,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatServiceDescription(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("ServiceDescription"), p0); - /// - /// Missing required option '--{0}' or '--{1}'. - /// - internal static string MissingOptions - { - get => GetString("MissingOptions"); - } - - /// - /// Missing required option '--{0}' or '--{1}'. - /// - internal static string FormatMissingOptions(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("MissingOptions"), p0, p1); - /// /// The name of the document to pass to the '--method' method. Default value '{0}'. /// @@ -304,20 +192,6 @@ namespace Microsoft.Extensions.ApiDescription.Client internal static string FormatUsingService(object p0) => string.Format(CultureInfo.CurrentCulture, GetString("UsingService"), p0); - /// - /// Using URI '{0}'. - /// - internal static string UsingUri - { - get => GetString("UsingUri"); - } - - /// - /// Using URI '{0}'. - /// - internal static string FormatUsingUri(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("UsingUri"), p0); - /// /// Method '{0}' of service '{1}' failed to generate document '{2}'. /// diff --git a/src/GetDocumentInsider/Resources.resx b/src/GetDocumentInsider/Resources.resx index f42b6144e6..fffabb44f3 100644 --- a/src/GetDocumentInsider/Resources.resx +++ b/src/GetDocumentInsider/Resources.resx @@ -120,9 +120,6 @@ The assembly to use. - - Show JSON output. - Missing required option '--{0}'. @@ -135,39 +132,18 @@ Prefix console output with logging level. - - Using application base '{0}'. - - - Using assembly '{0}'. - - - Using configuration file '{0}'. - Show verbose output. - - Writing '{0}'... - - - Using working directory '{0}'. - Location from which inside man was copied (in the .NET Framework case) or loaded. - - The URI to download the document from. - The name of the method to invoke on the '--service' instance. Default value '{0}'. The qualified name of the service type to retrieve from dependency injection. Default value '{0}'. - - Missing required option '--{0}' or '--{1}'. - The name of the document to pass to the '--method' method. Default value '{0}'. @@ -180,13 +156,10 @@ Using service '{0}'. - - Using URI '{0}'. - Method '{0}' of service '{1}' failed to generate document '{2}'. Assembly '{0}' does not contain an entry point. - + \ No newline at end of file diff --git a/src/GetDocumentInsider/WrappedException.cs b/src/GetDocumentInsider/WrappedException.cs deleted file mode 100644 index ec90fb6061..0000000000 --- a/src/GetDocumentInsider/WrappedException.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - internal class WrappedException : Exception - { - private readonly string _stackTrace; - - public WrappedException(string type, string message, string stackTrace) - : base(message) - { - Type = type; - _stackTrace = stackTrace; - } - - public string Type { get; } - - public override string ToString() - => _stackTrace; - } -} diff --git a/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs b/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs index 1e79fd9722..14c5545b2b 100644 --- a/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs +++ b/src/Microsoft.Extensions.ApiDescription.Client/DownloadFile.cs @@ -4,6 +4,9 @@ using System; using System.IO; using System.Net.Http; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Framework; @@ -94,19 +97,130 @@ namespace Microsoft.Extensions.ApiDescription.Client return; } - log.LogMessage($"Downloading '{uri}' to '{destinationPath}'."); + log.LogMessage(MessageImportance.High, $"Downloading '{uri}' to '{destinationPath}'."); - using (var httpClient = new HttpClient + using (var httpClient = new HttpClient()) { - }) + await DownloadAsync(uri, destinationPath, httpClient, cancellationToken, log, timeoutSeconds); + } + } + + public static async Task DownloadAsync( + string uri, + string destinationPath, + HttpClient httpClient, + CancellationToken cancellationToken, + TaskLoggingHelper log, + int timeoutSeconds) + { + // Timeout if the response has not begun within 1 minute + httpClient.Timeout = TimeSpan.FromMinutes(1); + + var destinationExists = File.Exists(destinationPath); + var reachedCopy = false; + try { - await DownloadFileCore.DownloadAsync( - uri, - destinationPath, - httpClient, - new LogWrapper(log), - cancellationToken, - timeoutSeconds); + using (var response = await httpClient.GetAsync(uri, cancellationToken)) + { + response.EnsureSuccessStatusCode(); + cancellationToken.ThrowIfCancellationRequested(); + + using (var responseStreamTask = response.Content.ReadAsStreamAsync()) + { + var finished = await Task.WhenAny( + responseStreamTask, + Task.Delay(TimeSpan.FromSeconds(timeoutSeconds))); + + if (!ReferenceEquals(responseStreamTask, finished)) + { + throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds."); + } + + using (var responseStream = await responseStreamTask) + { + if (destinationExists) + { + // Check hashes before using the downloaded information. + var downloadHash = GetHash(responseStream); + responseStream.Position = 0L; + + byte[] destinationHash; + using (var destinationStream = File.OpenRead(destinationPath)) + { + destinationHash = GetHash(destinationStream); + } + + var sameHashes = downloadHash.Length == destinationHash.Length; + for (var i = 0; sameHashes && i < downloadHash.Length; i++) + { + sameHashes = downloadHash[i] == destinationHash[i]; + } + + if (sameHashes) + { + log.LogMessage($"Not overwriting existing and matching file '{destinationPath}'."); + return; + } + } + else + { + // May need to create directory to hold the file. + var destinationDirectory = Path.GetDirectoryName(destinationPath); + if (!string.IsNullOrEmpty(destinationDirectory)) + { + Directory.CreateDirectory(destinationDirectory); + } + } + + // Create or overwrite the destination file. + reachedCopy = true; + using (var outStream = File.Create(destinationPath)) + { + await responseStream.CopyToAsync(outStream); + } + } + } + } + } + catch (HttpRequestException ex) when (destinationExists) + { + if (ex.InnerException is SocketException socketException) + { + log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'."); + } + else + { + log.LogWarning($"Unable to download {uri}: {ex.Message}"); + } + } + catch (Exception ex) + { + log.LogError($"Downloading '{uri}' failed."); + log.LogErrorFromException(ex, showStackTrace: true); + if (reachedCopy) + { + File.Delete(destinationPath); + } + } + } + + private static byte[] GetHash(Stream stream) + { + SHA256 algorithm; + try + { + algorithm = SHA256.Create(); + } + catch (TargetInvocationException) + { + // SHA256.Create is documented to throw this exception on FIPS-compliant machines. See + // https://msdn.microsoft.com/en-us/library/z08hz7ad Fall back to a FIPS-compliant SHA256 algorithm. + algorithm = new SHA256CryptoServiceProvider(); + } + + using (algorithm) + { + return algorithm.ComputeHash(stream); } } } diff --git a/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs b/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs deleted file mode 100644 index f058620621..0000000000 --- a/src/Microsoft.Extensions.ApiDescription.Client/LogWrapper.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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 Microsoft.Build.Utilities; - -namespace Microsoft.Extensions.ApiDescription.Client -{ - internal class LogWrapper : ILogWrapper - { - private readonly TaskLoggingHelper _log; - - public LogWrapper(TaskLoggingHelper log) - { - _log = log; - } - - public void LogError(string message, params object[] messageArgs) - { - _log.LogError(message, messageArgs); - } - - public void LogError(Exception exception, bool showStackTrace) - { - _log.LogErrorFromException(exception, showStackTrace); - } - - public void LogInformational(string message, params object[] messageArgs) - { - _log.LogMessage(message, messageArgs); - } - - public void LogWarning(string message, params object[] messageArgs) - { - _log.LogWarning(message, messageArgs); - } - } -} diff --git a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj index daec0b3b26..946f3d1d16 100644 --- a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj +++ b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.csproj @@ -8,21 +8,52 @@ MSBuild tasks and targets for code generation false - false + false + false + false $(MSBuildProjectName).nuspec - Build Tasks;msbuild;DownloadFile;GetFilenameFromUri;code generation + Build Tasks;MSBuild;Swagger;Open API;code generation; Web API client netstandard2.0;net461 - - - + + - - - + + + $(AssemblySigningCertName) + tasks/$(TargetFramework)/$(TargetFileName) + $(AssemblySigningStrongName) + + + + + + + $(AssemblySigningCertName) + tools/dotnet-getdocument.dll + $(AssemblySigningStrongName) + + + $(AssemblySigningCertName) + tools/net461/GetDocument.Insider.exe + $(AssemblySigningStrongName) + + + $(AssemblySigningCertName) + tools/net461-x86/GetDocument.Insider.exe + $(AssemblySigningStrongName) + + + $(AssemblySigningCertName) + tools/netcoreapp2.0/GetDocument.Insider.exe + $(AssemblySigningStrongName) + diff --git a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec index 5b840bd379..e35a37d51c 100644 --- a/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec +++ b/src/Microsoft.Extensions.ApiDescription.Client/Microsoft.Extensions.ApiDescription.Client.nuspec @@ -12,17 +12,19 @@ $owners$ $projectUrl$ - false + true $tags$ $version$ - - - - - - + + + + + + + + diff --git a/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets b/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets deleted file mode 100644 index 5ed88a3f6c..0000000000 --- a/src/Microsoft.Extensions.ApiDescription.Client/ServiceProjectReferenceMetadata.targets +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props index d01b8044e8..0e1260da7c 100644 --- a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props +++ b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.props @@ -93,11 +93,6 @@ Default is set in server project, falling back to "Microsoft.Extensions.ApiDescription.IDocumentProvider". --> - - diff --git a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets index e7c60f9017..051922a579 100644 --- a/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets +++ b/src/Microsoft.Extensions.ApiDescription.Client/build/Microsoft.Extensions.ApiDescription.Client.targets @@ -179,7 +179,7 @@ - dotnet getdocument --no-build --project %(FullPath) --output %(DocumentPath) + dotnet $(MSBuildThisFileDirectory)/../tools/dotnet-getdocument.dll --project %(FullPath) --output %(DocumentPath) $(DefaultDocumentGeneratorDefaultOptions) %(Command) --service %(Service) - - %(Command) --uri %(Uri) - %(Command) --configuration %(ProjectConfiguration) %(DefaultDocumentGeneratorOptions) diff --git a/src/dotnet-getdocument/Commands/InvokeCommand.cs b/src/dotnet-getdocument/Commands/InvokeCommand.cs index b376b3b9d2..b1426c057d 100644 --- a/src/dotnet-getdocument/Commands/InvokeCommand.cs +++ b/src/dotnet-getdocument/Commands/InvokeCommand.cs @@ -159,12 +159,6 @@ namespace Microsoft.Extensions.ApiDescription.Client.Commands args.Add(project.DefaultService); } - if (!(args.Contains("--uri") || string.IsNullOrEmpty(project.DefaultUri))) - { - args.Add("--uri"); - args.Add(project.DefaultUri); - } - if (_output.HasValue()) { args.Add("--output"); diff --git a/src/dotnet-getdocument/Project.cs b/src/dotnet-getdocument/Project.cs index ce3325b7e8..f9fc0da93e 100644 --- a/src/dotnet-getdocument/Project.cs +++ b/src/dotnet-getdocument/Project.cs @@ -1,6 +1,7 @@ // 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.Diagnostics; using System.IO; @@ -11,7 +12,8 @@ namespace Microsoft.Extensions.ApiDescription.Client { internal class Project { - private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client.ServiceProjectReferenceMetadata"; + private const string ResourceFilename = "ServiceProjectReferenceMetadata.targets"; + private const string MSBuildResourceName = "Microsoft.Extensions.ApiDescription.Client." + ResourceFilename; private Project() { @@ -33,8 +35,6 @@ namespace Microsoft.Extensions.ApiDescription.Client public string DefaultService { get; private set; } - public string DefaultUri { get; private set; } - public string DepsPath { get; private set; } public string Directory { get; private set; } @@ -53,41 +53,36 @@ namespace Microsoft.Extensions.ApiDescription.Client public string RuntimeFrameworkVersion { get; private set; } + public string RuntimeIdentifier { get; private set; } + public string TargetFramework { get; private set; } public string TargetFrameworkMoniker { get; private set; } public static Project FromFile( - string file, + string projectFile, string buildExtensionsDirectory, string framework = null, string configuration = null, string runtime = null) { - Debug.Assert(!string.IsNullOrEmpty(file), "file is null or empty."); + if (string.IsNullOrEmpty(projectFile)) + { + throw new ArgumentNullException(nameof(projectFile)); + } if (string.IsNullOrEmpty(buildExtensionsDirectory)) { - buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(file), "obj"); + buildExtensionsDirectory = Path.Combine(Path.GetDirectoryName(projectFile), "obj"); } IODirectory.CreateDirectory(buildExtensionsDirectory); var assembly = typeof(Project).Assembly; - var propsPath = Path.Combine( + var targetsPath = Path.Combine( buildExtensionsDirectory, - Path.GetFileName(file) + ".ServiceProjectReferenceMetadata.props"); - using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.props")) - { - using (var output = File.OpenWrite(propsPath)) - { - Reporter.WriteVerbose(Resources.FormatWritingFile(propsPath)); - input.CopyTo(output); - } - } - - var targetsPath = Path.ChangeExtension(propsPath, ".targets"); - using (var input = assembly.GetManifestResourceStream($"{MSBuildResourceName}.targets")) + $"{Path.GetFileName(projectFile)}.{ResourceFilename}"); + using (var input = assembly.GetManifestResourceStream(MSBuildResourceName)) { using (var output = File.OpenWrite(targetsPath)) { @@ -101,32 +96,29 @@ namespace Microsoft.Extensions.ApiDescription.Client var metadataPath = Path.GetTempFileName(); try { - var propertyArg = "/property:ServiceProjectReferenceMetadataPath=" + metadataPath; - if (!string.IsNullOrEmpty(framework)) - { - propertyArg += ";TargetFramework=" + framework; - } - if (!string.IsNullOrEmpty(configuration)) - { - propertyArg += ";Configuration=" + configuration; - } - if (!string.IsNullOrEmpty(runtime)) - { - propertyArg += ";RuntimeIdentifier=" + runtime; - } - var args = new List { "msbuild", "/target:WriteServiceProjectReferenceMetadata", - propertyArg, "/verbosity:quiet", - "/nologo" + "/nologo", + $"/property:ServiceProjectReferenceMetadataPath={metadataPath}", + projectFile, }; - if (!string.IsNullOrEmpty(file)) + if (!string.IsNullOrEmpty(framework)) { - args.Add(file); + args.Add($"/property:TargetFramework={framework}"); + } + + if (!string.IsNullOrEmpty(configuration)) + { + args.Add($"/property:Configuration={configuration}"); + } + + if (!string.IsNullOrEmpty(runtime)) + { + args.Add($"/property:RuntimeIdentifier={runtime}"); } var exitCode = Exe.Run("dotnet", args); @@ -140,21 +132,20 @@ namespace Microsoft.Extensions.ApiDescription.Client } finally { - File.Delete(propsPath); File.Delete(metadataPath); File.Delete(targetsPath); } var project = new Project { + DefaultDocumentName = metadata[nameof(DefaultDocumentName)], + DefaultMethod = metadata[nameof(DefaultMethod)], + DefaultService = metadata[nameof(DefaultService)], + AssemblyName = metadata[nameof(AssemblyName)], AssemblyPath = metadata[nameof(AssemblyPath)], AssetsPath = metadata[nameof(AssetsPath)], Configuration = metadata[nameof(Configuration)], - DefaultDocumentName = metadata[nameof(DefaultDocumentName)], - DefaultMethod = metadata[nameof(DefaultMethod)], - DefaultService = metadata[nameof(DefaultService)], - DefaultUri = metadata[nameof(DefaultUri)], DepsPath = metadata[nameof(DepsPath)], Directory = metadata[nameof(Directory)], ExtensionsPath = metadata[nameof(ExtensionsPath)], @@ -164,6 +155,7 @@ namespace Microsoft.Extensions.ApiDescription.Client PlatformTarget = metadata[nameof(PlatformTarget)] ?? metadata[nameof(Platform)], RuntimeConfigPath = metadata[nameof(RuntimeConfigPath)], RuntimeFrameworkVersion = metadata[nameof(RuntimeFrameworkVersion)], + RuntimeIdentifier = metadata[nameof(RuntimeIdentifier)], TargetFramework = metadata[nameof(TargetFramework)], TargetFrameworkMoniker = metadata[nameof(TargetFrameworkMoniker)], }; @@ -193,6 +185,11 @@ namespace Microsoft.Extensions.ApiDescription.Client project.AssemblyPath = Path.GetFullPath(Path.Combine(project.Directory, project.AssemblyPath)); } + if (!Path.IsPathRooted(project.ExtensionsPath)) + { + project.ExtensionsPath = Path.GetFullPath(Path.Combine(project.Directory, project.ExtensionsPath)); + } + if (!Path.IsPathRooted(project.OutputPath)) { project.OutputPath = Path.GetFullPath(Path.Combine(project.Directory, project.OutputPath)); diff --git a/src/dotnet-getdocument/Properties/Resources.Designer.cs b/src/dotnet-getdocument/Properties/Resources.Designer.cs index eb38ce67a7..8ab2cece4f 100644 --- a/src/dotnet-getdocument/Properties/Resources.Designer.cs +++ b/src/dotnet-getdocument/Properties/Resources.Designer.cs @@ -277,7 +277,7 @@ namespace Microsoft.Extensions.ApiDescription.Client => string.Format(CultureInfo.CurrentCulture, GetString("WritingFile"), p0); /// - /// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option. + /// Project output not found. Project must be up-to-date when using this tool. /// internal static string MustBuild { @@ -285,7 +285,7 @@ namespace Microsoft.Extensions.ApiDescription.Client } /// - /// Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option. + /// Project output not found. Project must be up-to-date when using this tool. /// internal static string FormatMustBuild() => GetString("MustBuild"); diff --git a/src/dotnet-getdocument/Resources.resx b/src/dotnet-getdocument/Resources.resx index 7db5873e7d..9829182dc6 100644 --- a/src/dotnet-getdocument/Resources.resx +++ b/src/dotnet-getdocument/Resources.resx @@ -175,7 +175,7 @@ Writing '{0}'... - Project output not found and --no-build was specified. Project must be up-to-date when using the --no-build option. + Project output not found. Project must be up-to-date when using this tool. The file to write the result to. diff --git a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props deleted file mode 100644 index 30f045f3c0..0000000000 --- a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.props +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - $(WriteServiceProjectReferenceMetadataDependsOn) - - - - - $(WriteServiceProjectReferenceMetadataDependsOn) - - - $(WriteServiceProjectReferenceMetadataDependsOn) - - - diff --git a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets index e8840ea37b..a9a3115b94 100644 --- a/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets +++ b/src/dotnet-getdocument/ServiceProjectReferenceMetadata.targets @@ -1,20 +1,14 @@  - - - + - - - + + @@ -29,21 +23,21 @@ + - - + + - + diff --git a/src/dotnet-getdocument/dotnet-getdocument.csproj b/src/dotnet-getdocument/dotnet-getdocument.csproj index 53967535a7..370514484b 100644 --- a/src/dotnet-getdocument/dotnet-getdocument.csproj +++ b/src/dotnet-getdocument/dotnet-getdocument.csproj @@ -1,89 +1,25 @@ - - $(GenerateNuspecDependsOn);PopulateNuspec - dotnet-getdocument GetDocument Command-line Tool outside man false - true - false false - $(MSBuildProjectName).nuspec Exe - true - GetDocument;command line;command-line;tool Microsoft.Extensions.ApiDescription.Client netcoreapp2.1 - - - + - - - - + - - - - - - - - - - - - - - - <_Temporary Remove="@(Temporary)" /> - <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461" /> - <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=net461;Platform=x86" /> - <_Temporary Include="../GetDocumentInsider/GetDocumentInsider.csproj" Properties="TargetFramework=netcoreapp2.0" /> - - - - - - - <_Temporary Remove="@(_Temporary)" /> - - - - - authors=$(Authors); - copyright=$(Copyright); - description=$(Description); - iconUrl=$(PackageIconUrl); - id=$(PackageId); - InsiderNet461Output=..\GetDocumentInsider\bin\$(Configuration)\net461\publish\*; - InsiderNet461X86Output=..\GetDocumentInsider\bin\x86\$(Configuration)\net461\publish\*; - InsiderNetCoreOutput=..\GetDocumentInsider\bin\$(Configuration)\netcoreapp2.0\publish\*; - licenseUrl=$(PackageLicenseUrl); - Output=$(PublishDir)**\*; - OutputShims=$(IntermediateOutputPath)shims\**\*; - packageType=$(PackageType); - projectUrl=$(PackageProjectUrl); - repositoryCommit=$(RepositoryCommit); - repositoryUrl=$(RepositoryUrl); - SettingsFile=$(_ToolsSettingsFilePath); - tags=$(PackageTags.Replace(';', ' ')); - targetFramework=$(TargetFramework); - version=$(PackageVersion); - - - diff --git a/src/dotnet-getdocument/dotnet-getdocument.nuspec b/src/dotnet-getdocument/dotnet-getdocument.nuspec deleted file mode 100644 index 60c51f7869..0000000000 --- a/src/dotnet-getdocument/dotnet-getdocument.nuspec +++ /dev/null @@ -1,28 +0,0 @@ - - - - $id$ - $version$ - $authors$ - true - $licenseUrl$ - $projectUrl$ - $iconUrl$ - $description$ - $copyright$ - $tags$ - - - - - - - - - - - - - - -