Add TestApplication to allow publish output caching (#1511)
This commit is contained in:
Родитель
446b64c199
Коммит
658906bf7c
|
@ -4,34 +4,34 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Label="Package Versions">
|
<PropertyGroup Label="Package Versions">
|
||||||
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17102</InternalAspNetCoreSdkPackageVersion>
|
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17102</InternalAspNetCoreSdkPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
<MicrosoftAspNetCoreHttpAbstractionsPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreHttpAbstractionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
<MicrosoftAspNetCoreHttpFeaturesPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreHttpFeaturesPackageVersion>
|
||||||
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreHttpPackageVersion>
|
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreHttpPackageVersion>
|
||||||
<MicrosoftAspNetCoreOwinPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreOwinPackageVersion>
|
<MicrosoftAspNetCoreOwinPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreOwinPackageVersion>
|
||||||
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34823</MicrosoftAspNetCoreTestingPackageVersion>
|
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34825</MicrosoftAspNetCoreTestingPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
|
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
|
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||||
<MicrosoftExtensionsConfigurationPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsConfigurationPackageVersion>
|
<MicrosoftExtensionsConfigurationPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsConfigurationPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||||
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
|
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
|
||||||
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsLoggingConsolePackageVersion>
|
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||||
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsLoggingPackageVersion>
|
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsLoggingPackageVersion>
|
||||||
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsLoggingTestingPackageVersion>
|
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsLoggingTestingPackageVersion>
|
||||||
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsOptionsPackageVersion>
|
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsOptionsPackageVersion>
|
||||||
<MicrosoftExtensionsProcessSourcesPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsProcessSourcesPackageVersion>
|
<MicrosoftExtensionsProcessSourcesPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsProcessSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
|
<MicrosoftExtensionsRazorViewsSourcesPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsRazorViewsSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsStackTraceSourcesPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsStackTraceSourcesPackageVersion>
|
<MicrosoftExtensionsStackTraceSourcesPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsStackTraceSourcesPackageVersion>
|
||||||
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.2.0-preview1-34823</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>2.2.0-preview1-34825</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
|
||||||
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
|
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
|
||||||
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
|
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
|
||||||
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview1-26618-02</MicrosoftNETCoreApp22PackageVersion>
|
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview1-26618-02</MicrosoftNETCoreApp22PackageVersion>
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
|
{
|
||||||
|
public class ApplicationPublisher
|
||||||
|
{
|
||||||
|
public string ApplicationPath { get; }
|
||||||
|
|
||||||
|
public ApplicationPublisher(string applicationPath)
|
||||||
|
{
|
||||||
|
ApplicationPath = applicationPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly string DotnetCommandName = "dotnet";
|
||||||
|
|
||||||
|
public virtual Task<PublishedApplication> Publish(DeploymentParameters deploymentParameters, ILogger logger)
|
||||||
|
{
|
||||||
|
var publishDirectory = CreateTempDirectory();
|
||||||
|
using (logger.BeginScope("dotnet-publish"))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(deploymentParameters.TargetFramework))
|
||||||
|
{
|
||||||
|
throw new Exception($"A target framework must be specified in the deployment parameters for applications that require publishing before deployment");
|
||||||
|
}
|
||||||
|
|
||||||
|
var parameters = $"publish "
|
||||||
|
+ $" --output \"{publishDirectory.FullName}\""
|
||||||
|
+ $" --framework {deploymentParameters.TargetFramework}"
|
||||||
|
+ $" --configuration {deploymentParameters.Configuration}"
|
||||||
|
+ (deploymentParameters.RestoreOnPublish
|
||||||
|
? string.Empty
|
||||||
|
: " --no-restore -p:VerifyMatchingImplicitPackageVersion=false");
|
||||||
|
// Set VerifyMatchingImplicitPackageVersion to disable errors when Microsoft.NETCore.App's version is overridden externally
|
||||||
|
// This verification doesn't matter if we are skipping restore during tests.
|
||||||
|
|
||||||
|
if (deploymentParameters.ApplicationType == ApplicationType.Standalone)
|
||||||
|
{
|
||||||
|
parameters += $" --runtime {GetRuntimeIdentifier(deploymentParameters)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters += $" {deploymentParameters.AdditionalPublishParameters}";
|
||||||
|
|
||||||
|
var startInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = DotnetCommandName,
|
||||||
|
Arguments = parameters,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
WorkingDirectory = deploymentParameters.ApplicationPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
ProcessHelpers.AddEnvironmentVariablesToProcess(startInfo, deploymentParameters.PublishEnvironmentVariables, logger);
|
||||||
|
|
||||||
|
var hostProcess = new Process() { StartInfo = startInfo };
|
||||||
|
|
||||||
|
logger.LogInformation($"Executing command {DotnetCommandName} {parameters}");
|
||||||
|
|
||||||
|
hostProcess.StartAndCaptureOutAndErrToLogger("dotnet-publish", logger);
|
||||||
|
|
||||||
|
// A timeout is passed to Process.WaitForExit() for two reasons:
|
||||||
|
//
|
||||||
|
// 1. When process output is read asynchronously, WaitForExit() without a timeout blocks until child processes
|
||||||
|
// are killed, which can cause hangs due to MSBuild NodeReuse child processes started by dotnet.exe.
|
||||||
|
// With a timeout, WaitForExit() returns when the parent process is killed and ignores child processes.
|
||||||
|
// https://stackoverflow.com/a/37983587/102052
|
||||||
|
//
|
||||||
|
// 2. If "dotnet publish" does hang indefinitely for some reason, tests should fail fast with an error message.
|
||||||
|
const int timeoutMinutes = 5;
|
||||||
|
if (hostProcess.WaitForExit(milliseconds: timeoutMinutes * 60 * 1000))
|
||||||
|
{
|
||||||
|
if (hostProcess.ExitCode != 0)
|
||||||
|
{
|
||||||
|
var message = $"{DotnetCommandName} publish exited with exit code : {hostProcess.ExitCode}";
|
||||||
|
logger.LogError(message);
|
||||||
|
throw new Exception(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var message = $"{DotnetCommandName} publish failed to exit after {timeoutMinutes} minutes";
|
||||||
|
logger.LogError(message);
|
||||||
|
throw new Exception(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogInformation($"{DotnetCommandName} publish finished with exit code : {hostProcess.ExitCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(new PublishedApplication(publishDirectory.FullName, logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRuntimeIdentifier(DeploymentParameters deploymentParameters)
|
||||||
|
{
|
||||||
|
var architecture = deploymentParameters.RuntimeArchitecture;
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
return "win7-" + architecture;
|
||||||
|
}
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
return "linux-" + architecture;
|
||||||
|
}
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
{
|
||||||
|
return "osx-" + architecture;
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException("Unrecognized operation system platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static DirectoryInfo CreateTempDirectory()
|
||||||
|
{
|
||||||
|
var tempPath = Path.GetTempPath() + Guid.NewGuid().ToString("N");
|
||||||
|
var target = new DirectoryInfo(tempPath);
|
||||||
|
target.Create();
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
|
{
|
||||||
|
public class CachingApplicationPublisher: ApplicationPublisher, IDisposable
|
||||||
|
{
|
||||||
|
private readonly Dictionary<DotnetPublishParameters, PublishedApplication> _publishCache = new Dictionary<DotnetPublishParameters, PublishedApplication>();
|
||||||
|
|
||||||
|
public CachingApplicationPublisher(string applicationPath) : base(applicationPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<PublishedApplication> Publish(DeploymentParameters deploymentParameters, ILogger logger)
|
||||||
|
{
|
||||||
|
if (ApplicationPath != deploymentParameters.ApplicationPath)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("ApplicationPath mismatch");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploymentParameters.PublishEnvironmentVariables.Any())
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("DeploymentParameters.PublishEnvironmentVariables not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(deploymentParameters.PublishedApplicationRootPath))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("DeploymentParameters.PublishedApplicationRootPath not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploymentParameters.RestoreOnPublish)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("DeploymentParameters.RestoreOnPublish not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dotnetPublishParameters = new DotnetPublishParameters
|
||||||
|
{
|
||||||
|
TargetFramework = deploymentParameters.TargetFramework,
|
||||||
|
Configuration = deploymentParameters.Configuration,
|
||||||
|
ApplicationType = deploymentParameters.ApplicationType,
|
||||||
|
RuntimeArchitecture = deploymentParameters.RuntimeArchitecture
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!_publishCache.TryGetValue(dotnetPublishParameters, out var publishedApplication))
|
||||||
|
{
|
||||||
|
publishedApplication = await base.Publish(deploymentParameters, logger);
|
||||||
|
_publishCache.Add(dotnetPublishParameters, publishedApplication);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PublishedApplication(CopyPublishedOutput(publishedApplication, logger), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CopyPublishedOutput(PublishedApplication application, ILogger logger)
|
||||||
|
{
|
||||||
|
var target = CreateTempDirectory();
|
||||||
|
|
||||||
|
var source = new DirectoryInfo(application.Path);
|
||||||
|
CopyFiles(source, target, logger);
|
||||||
|
return target.FullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CopyFiles(DirectoryInfo source, DirectoryInfo target, ILogger logger)
|
||||||
|
{
|
||||||
|
foreach (DirectoryInfo directoryInfo in source.GetDirectories())
|
||||||
|
{
|
||||||
|
CopyFiles(directoryInfo, target.CreateSubdirectory(directoryInfo.Name), logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogDebug($"Processing {target.FullName}");
|
||||||
|
foreach (FileInfo fileInfo in source.GetFiles())
|
||||||
|
{
|
||||||
|
logger.LogDebug($" Copying {fileInfo.Name}");
|
||||||
|
var destFileName = Path.Combine(target.FullName, fileInfo.Name);
|
||||||
|
fileInfo.CopyTo(destFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var publishedApp in _publishCache.Values)
|
||||||
|
{
|
||||||
|
publishedApp.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DotnetPublishParameters
|
||||||
|
{
|
||||||
|
public string TargetFramework { get; set; }
|
||||||
|
public string Configuration { get; set; }
|
||||||
|
public ApplicationType ApplicationType { get; set; }
|
||||||
|
public RuntimeArchitecture RuntimeArchitecture { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,6 +99,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApplicationPublisher ApplicationPublisher { get; set; }
|
||||||
|
|
||||||
public ServerType ServerType { get; set; }
|
public ServerType ServerType { get; set; }
|
||||||
|
|
||||||
public RuntimeFlavor RuntimeFlavor { get; set; }
|
public RuntimeFlavor RuntimeFlavor { get; set; }
|
||||||
|
|
|
@ -20,12 +20,10 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
{
|
{
|
||||||
public static readonly string DotnetCommandName = "dotnet";
|
public static readonly string DotnetCommandName = "dotnet";
|
||||||
|
|
||||||
// This is the argument that separates the dotnet arguments for the args being passed to the
|
|
||||||
// app being run when running dotnet run
|
|
||||||
public static readonly string DotnetArgumentSeparator = "--";
|
|
||||||
|
|
||||||
private readonly Stopwatch _stopwatch = new Stopwatch();
|
private readonly Stopwatch _stopwatch = new Stopwatch();
|
||||||
|
|
||||||
|
private PublishedApplication _publishedApplication;
|
||||||
|
|
||||||
public ApplicationDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
|
public ApplicationDeployer(DeploymentParameters deploymentParameters, ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
DeploymentParameters = deploymentParameters;
|
DeploymentParameters = deploymentParameters;
|
||||||
|
@ -82,78 +80,9 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
|
|
||||||
protected void DotnetPublish(string publishRoot = null)
|
protected void DotnetPublish(string publishRoot = null)
|
||||||
{
|
{
|
||||||
using (Logger.BeginScope("dotnet-publish"))
|
var publisher = DeploymentParameters.ApplicationPublisher ?? new ApplicationPublisher(DeploymentParameters.ApplicationPath);
|
||||||
{
|
_publishedApplication = publisher.Publish(DeploymentParameters, Logger).GetAwaiter().GetResult();
|
||||||
if (string.IsNullOrEmpty(DeploymentParameters.TargetFramework))
|
DeploymentParameters.PublishedApplicationRootPath = _publishedApplication.Path;
|
||||||
{
|
|
||||||
throw new Exception($"A target framework must be specified in the deployment parameters for applications that require publishing before deployment");
|
|
||||||
}
|
|
||||||
|
|
||||||
DeploymentParameters.PublishedApplicationRootPath = publishRoot ?? Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
|
|
||||||
|
|
||||||
var parameters = $"publish "
|
|
||||||
+ $" --output \"{DeploymentParameters.PublishedApplicationRootPath}\""
|
|
||||||
+ $" --framework {DeploymentParameters.TargetFramework}"
|
|
||||||
+ $" --configuration {DeploymentParameters.Configuration}"
|
|
||||||
+ (DeploymentParameters.RestoreOnPublish
|
|
||||||
? string.Empty
|
|
||||||
: " --no-restore -p:VerifyMatchingImplicitPackageVersion=false");
|
|
||||||
// Set VerifyMatchingImplicitPackageVersion to disable errors when Microsoft.NETCore.App's version is overridden externally
|
|
||||||
// This verification doesn't matter if we are skipping restore during tests.
|
|
||||||
|
|
||||||
if (DeploymentParameters.ApplicationType == ApplicationType.Standalone)
|
|
||||||
{
|
|
||||||
parameters += $" --runtime {GetRuntimeIdentifier()}";
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters += $" {DeploymentParameters.AdditionalPublishParameters}";
|
|
||||||
|
|
||||||
var startInfo = new ProcessStartInfo
|
|
||||||
{
|
|
||||||
FileName = DotnetCommandName,
|
|
||||||
Arguments = parameters,
|
|
||||||
UseShellExecute = false,
|
|
||||||
CreateNoWindow = true,
|
|
||||||
RedirectStandardError = true,
|
|
||||||
RedirectStandardOutput = true,
|
|
||||||
WorkingDirectory = DeploymentParameters.ApplicationPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.PublishEnvironmentVariables);
|
|
||||||
|
|
||||||
var hostProcess = new Process() { StartInfo = startInfo };
|
|
||||||
|
|
||||||
Logger.LogInformation($"Executing command {DotnetCommandName} {parameters}");
|
|
||||||
|
|
||||||
hostProcess.StartAndCaptureOutAndErrToLogger("dotnet-publish", Logger);
|
|
||||||
|
|
||||||
// A timeout is passed to Process.WaitForExit() for two reasons:
|
|
||||||
//
|
|
||||||
// 1. When process output is read asynchronously, WaitForExit() without a timeout blocks until child processes
|
|
||||||
// are killed, which can cause hangs due to MSBuild NodeReuse child processes started by dotnet.exe.
|
|
||||||
// With a timeout, WaitForExit() returns when the parent process is killed and ignores child processes.
|
|
||||||
// https://stackoverflow.com/a/37983587/102052
|
|
||||||
//
|
|
||||||
// 2. If "dotnet publish" does hang indefinitely for some reason, tests should fail fast with an error message.
|
|
||||||
const int timeoutMinutes = 5;
|
|
||||||
if (hostProcess.WaitForExit(milliseconds: timeoutMinutes * 60 * 1000))
|
|
||||||
{
|
|
||||||
if (hostProcess.ExitCode != 0)
|
|
||||||
{
|
|
||||||
var message = $"{DotnetCommandName} publish exited with exit code : {hostProcess.ExitCode}";
|
|
||||||
Logger.LogError(message);
|
|
||||||
throw new Exception(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var message = $"{DotnetCommandName} publish failed to exit after {timeoutMinutes} minutes";
|
|
||||||
Logger.LogError(message);
|
|
||||||
throw new Exception(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogInformation($"{DotnetCommandName} publish finished with exit code : {hostProcess.ExitCode}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void CleanPublishedOutput()
|
protected void CleanPublishedOutput()
|
||||||
|
@ -168,11 +97,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
RetryHelper.RetryOperation(
|
_publishedApplication.Dispose();
|
||||||
() => Directory.Delete(DeploymentParameters.PublishedApplicationRootPath, true),
|
|
||||||
e => Logger.LogWarning($"Failed to delete directory : {e.Message}"),
|
|
||||||
retryCount: 3,
|
|
||||||
retryDelayMilliseconds: 100);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -219,26 +144,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
protected void AddEnvironmentVariablesToProcess(ProcessStartInfo startInfo, IDictionary<string, string> environmentVariables)
|
protected void AddEnvironmentVariablesToProcess(ProcessStartInfo startInfo, IDictionary<string, string> environmentVariables)
|
||||||
{
|
{
|
||||||
var environment = startInfo.Environment;
|
var environment = startInfo.Environment;
|
||||||
SetEnvironmentVariable(environment, "ASPNETCORE_ENVIRONMENT", DeploymentParameters.EnvironmentName);
|
ProcessHelpers.SetEnvironmentVariable(environment, "ASPNETCORE_ENVIRONMENT", DeploymentParameters.EnvironmentName, Logger);
|
||||||
|
ProcessHelpers.AddEnvironmentVariablesToProcess(startInfo, environmentVariables, Logger);
|
||||||
foreach (var environmentVariable in environmentVariables)
|
|
||||||
{
|
|
||||||
SetEnvironmentVariable(environment, environmentVariable.Key, environmentVariable.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void SetEnvironmentVariable(IDictionary<string, string> environment, string name, string value)
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
Logger.LogInformation("Removing environment variable {name}", name);
|
|
||||||
environment.Remove(name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.LogInformation("SET {name}={value}", name, value);
|
|
||||||
environment[name] = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void InvokeUserApplicationCleanup()
|
protected void InvokeUserApplicationCleanup()
|
||||||
|
@ -286,26 +193,5 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Dispose();
|
public abstract void Dispose();
|
||||||
|
|
||||||
private string GetRuntimeIdentifier()
|
|
||||||
{
|
|
||||||
var architecture = DeploymentParameters.RuntimeArchitecture;
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
return "win7-" + architecture;
|
|
||||||
}
|
|
||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
||||||
{
|
|
||||||
return "linux-" + architecture;
|
|
||||||
}
|
|
||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
||||||
{
|
|
||||||
return "osx-" + architecture;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Unrecognized operation system platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
|
{
|
||||||
|
internal class ProcessHelpers
|
||||||
|
{
|
||||||
|
public static void AddEnvironmentVariablesToProcess(ProcessStartInfo startInfo, IDictionary<string, string> environmentVariables, ILogger logger)
|
||||||
|
{
|
||||||
|
var environment = startInfo.Environment;
|
||||||
|
|
||||||
|
foreach (var environmentVariable in environmentVariables)
|
||||||
|
{
|
||||||
|
SetEnvironmentVariable(environment, environmentVariable.Key, environmentVariable.Value, logger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetEnvironmentVariable(IDictionary<string, string> environment, string name, string value, ILogger logger)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Removing environment variable {name}", name);
|
||||||
|
environment.Remove(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.LogInformation("SET {name}={value}", name, value);
|
||||||
|
environment[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// 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 Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
|
{
|
||||||
|
public class PublishedApplication: IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public string Path { get; }
|
||||||
|
|
||||||
|
public PublishedApplication(string path, ILogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
RetryHelper.RetryOperation(
|
||||||
|
() => Directory.Delete(Path, true),
|
||||||
|
e => _logger.LogWarning($"Failed to delete directory : {e.Message}"),
|
||||||
|
retryCount: 3,
|
||||||
|
retryDelayMilliseconds: 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче