Merge branch 'release' into dev

This commit is contained in:
moozzyk 2016-04-15 14:28:21 -07:00
commit 31473694f3
5 changed files with 127 additions and 34 deletions

View File

@ -20,13 +20,15 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
app.HelpOption("-h|--help"); app.HelpOption("-h|--help");
var publishFolderOption = app.Option("--publish-folder|-p", "The path to the publish output folder", CommandOptionType.SingleValue); var publishFolderOption = app.Option("--publish-folder|-p", "The path to the publish output folder", CommandOptionType.SingleValue);
var frameworkOption = app.Option("-f|--framework <FRAMEWORK>", "Target framework of application being published", CommandOptionType.SingleValue);
var projectPath = app.Argument("<PROJECT>", "The path to the project (project folder or project.json) being published. If empty the current directory is used."); var projectPath = app.Argument("<PROJECT>", "The path to the project (project folder or project.json) being published. If empty the current directory is used.");
app.OnExecute(() => app.OnExecute(() =>
{ {
var publishFolder = publishFolderOption.Value(); var publishFolder = publishFolderOption.Value();
var framework = frameworkOption.Value();
if (publishFolder == null) if (publishFolder == null || framework == null)
{ {
app.ShowHelp(); app.ShowHelp();
return 2; return 2;
@ -34,7 +36,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
Reporter.Output.WriteLine($"Configuring the following project for use with IIS: '{publishFolder}'"); Reporter.Output.WriteLine($"Configuring the following project for use with IIS: '{publishFolder}'");
var exitCode = new PublishIISCommand(publishFolder, projectPath.Value).Run(); var exitCode = new PublishIISCommand(publishFolder, framework, projectPath.Value).Run();
Reporter.Output.WriteLine("Configuring project completed successfully"); Reporter.Output.WriteLine("Configuring project completed successfully");

View File

@ -3,10 +3,12 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Xml; using System.Xml;
using System.Xml.Linq; using System.Xml.Linq;
using Microsoft.DotNet.ProjectModel; using Microsoft.DotNet.ProjectModel;
using Microsoft.Extensions.Cli.Utils; using Microsoft.Extensions.Cli.Utils;
using NuGet.Frameworks;
namespace Microsoft.AspNetCore.Server.IISIntegration.Tools namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
{ {
@ -14,11 +16,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
{ {
private readonly string _publishFolder; private readonly string _publishFolder;
private readonly string _projectPath; private readonly string _projectPath;
private readonly string _framework;
public PublishIISCommand(string publishFolder, string projectPath) public PublishIISCommand(string publishFolder, string framework, string projectPath)
{ {
_publishFolder = publishFolder; _publishFolder = publishFolder;
_projectPath = projectPath; _projectPath = projectPath;
_framework = framework;
} }
public int Run() public int Run()
@ -42,8 +46,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
Reporter.Output.WriteLine($"No web.config found. Creating '{webConfigPath}'"); Reporter.Output.WriteLine($"No web.config found. Creating '{webConfigPath}'");
} }
var applicationName = GetApplicationName(applicationBasePath) + ".exe"; var projectContext = GetProjectContext(applicationBasePath, _framework);
var transformedConfig = WebConfigTransform.Transform(webConfigXml, applicationName, ConfigureForAzure()); var isPortable = !projectContext.TargetFramework.IsDesktop() && projectContext.IsPortable;
var applicationName = projectContext.ProjectFile.Name + (isPortable ? ".dll" : ".exe");
var transformedConfig = WebConfigTransform.Transform(webConfigXml, applicationName, ConfigureForAzure(), isPortable);
using (var f = new FileStream(webConfigPath, FileMode.Create)) using (var f = new FileStream(webConfigPath, FileMode.Create))
{ {
@ -67,9 +73,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
return Directory.GetCurrentDirectory(); return Directory.GetCurrentDirectory();
} }
private string GetApplicationName(string applicationBasePath) private static ProjectContext GetProjectContext(string applicationBasePath, string framework)
{ {
return ProjectReader.GetProject(Path.Combine(applicationBasePath, "project.json")).Name; var project = ProjectReader.GetProject(Path.Combine(applicationBasePath, "project.json"));
return new ProjectContextBuilder()
.WithProject(project)
.WithTargetFramework(framework)
.Build();
} }
private static bool ConfigureForAzure() private static bool ConfigureForAzure()

View File

@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
{ {
public static class WebConfigTransform public static class WebConfigTransform
{ {
public static XDocument Transform(XDocument webConfig, string appName, bool configureForAzure) public static XDocument Transform(XDocument webConfig, string appName, bool configureForAzure, bool isPortable)
{ {
const string HandlersElementName = "handlers"; const string HandlersElementName = "handlers";
const string aspNetCoreElementName = "aspNetCore"; const string aspNetCoreElementName = "aspNetCore";
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
var webServerSection = GetOrCreateChild(webConfig.Root, "system.webServer"); var webServerSection = GetOrCreateChild(webConfig.Root, "system.webServer");
TransformHandlers(GetOrCreateChild(webServerSection, HandlersElementName)); TransformHandlers(GetOrCreateChild(webServerSection, HandlersElementName));
TransformAspNetCore(GetOrCreateChild(webServerSection, aspNetCoreElementName), appName, configureForAzure); TransformAspNetCore(GetOrCreateChild(webServerSection, aspNetCoreElementName), appName, configureForAzure, isPortable);
// make sure that the aspNetCore element is after handlers element // make sure that the aspNetCore element is after handlers element
var aspNetCoreElement = webServerSection.Element(HandlersElementName) var aspNetCoreElement = webServerSection.Element(HandlersElementName)
@ -55,14 +55,32 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools
SetAttributeValueIfEmpty(aspNetCoreElement, "resourceType", "Unspecified"); SetAttributeValueIfEmpty(aspNetCoreElement, "resourceType", "Unspecified");
} }
private static void TransformAspNetCore(XElement aspNetCoreElement, string appName, bool configureForAzure) private static void TransformAspNetCore(XElement aspNetCoreElement, string appName, bool configureForAzure, bool isPortable)
{ {
// Forward slashes currently work neither in AspNetCoreModule nor in dotnet so they need to be // Forward slashes currently work neither in AspNetCoreModule nor in dotnet so they need to be
// replaced with backwards slashes when the application is published on a non-Windows machine // replaced with backwards slashes when the application is published on a non-Windows machine
var appPath = Path.Combine(configureForAzure ? @"%home%\site" : ".", appName).Replace("/", "\\"); var appPath = Path.Combine(configureForAzure ? @"%home%\site" : ".", appName).Replace("/", "\\");
var logPath = Path.Combine(configureForAzure ? @"\\?\%home%\LogFiles" : @".\logs", "stdout").Replace("/", "\\"); var logPath = Path.Combine(configureForAzure ? @"\\?\%home%\LogFiles" : @".\logs", "stdout").Replace("/", "\\");
aspNetCoreElement.SetAttributeValue("processPath", appPath); if (!isPortable)
{
aspNetCoreElement.SetAttributeValue("processPath", appPath);
}
else
{
aspNetCoreElement.SetAttributeValue("processPath", "dotnet");
// In Xml the order of attributes does not matter but it is nice to have
// the `arguments` attribute next to the `processPath` attribute
aspNetCoreElement.Attribute("arguments")?.Remove();
var attributes = aspNetCoreElement.Attributes().ToList();
var processPathIndex = attributes.FindIndex(a => a.Name.LocalName == "processPath");
attributes.Insert(processPathIndex + 1, new XAttribute("arguments", appPath));
aspNetCoreElement.Attributes().Remove();
aspNetCoreElement.Add(attributes);
}
SetAttributeValueIfEmpty(aspNetCoreElement, "stdoutLogEnabled", "false"); SetAttributeValueIfEmpty(aspNetCoreElement, "stdoutLogEnabled", "false");
SetAttributeValueIfEmpty(aspNetCoreElement, "stdoutLogFile", logPath); SetAttributeValueIfEmpty(aspNetCoreElement, "stdoutLogFile", logPath);
SetAttributeValueIfEmpty(aspNetCoreElement, "startupTimeLimit", "3600"); SetAttributeValueIfEmpty(aspNetCoreElement, "startupTimeLimit", "3600");

View File

@ -15,17 +15,48 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
public string ProjectPath; public string ProjectPath;
} }
[Fact] [Theory]
public void PublishIIS_uses_default_values_if_options_not_specified() [InlineData("netcoreapp1.0")]
[InlineData("netstandard1.5")]
public void PublishIIS_uses_default_values_if_options_not_specified(string targetFramework)
{ {
var folders = CreateTestDir("{}"); var folders = CreateTestDir($@"{{ ""frameworks"": {{ ""{targetFramework}"": {{ }} }} }}");
new PublishIISCommand(folders.PublishOutput, folders.ProjectPath).Run(); new PublishIISCommand(folders.PublishOutput, targetFramework, folders.ProjectPath).Run();
var processPath = (string)GetPublishedWebConfig(folders.PublishOutput) var processPath = (string)GetPublishedWebConfig(folders.PublishOutput)
.Descendants("aspNetCore").Attributes("processPath").Single(); .Descendants("aspNetCore").Attributes("processPath").Single();
Assert.Equal($@".\projectDir.exe", processPath); Assert.Equal(@".\projectDir.exe", processPath);
Directory.Delete(folders.TestRoot, recursive: true);
}
[Fact]
public void PublishIIS_can_publish_for_portable_app()
{
var folders = CreateTestDir(
@"
{
""frameworks"": {
""netcoreapp1.0"": {
""dependencies"": {
""Microsoft.NETCore.App"": {
""version"": ""1.0.0-*"",
""type"": ""platform""
}
}
}
}
}");
new PublishIISCommand(folders.PublishOutput, "netcoreapp1.0", folders.ProjectPath).Run();
var aspNetCoreElement = GetPublishedWebConfig(folders.PublishOutput)
.Descendants("aspNetCore").Single();
Assert.Equal(@"dotnet", (string)aspNetCoreElement.Attribute("processPath"));
Assert.Equal(@".\projectDir.dll", (string)aspNetCoreElement.Attribute("arguments"));
Directory.Delete(folders.TestRoot, recursive: true); Directory.Delete(folders.TestRoot, recursive: true);
} }
@ -35,9 +66,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
[InlineData("awesome.App")] [InlineData("awesome.App")]
public void PublishIIS_reads_application_name_from_project_json_if_exists(string projectName) public void PublishIIS_reads_application_name_from_project_json_if_exists(string projectName)
{ {
var folders = CreateTestDir($@"{{ ""name"": ""{projectName}"" }}"); var folders = CreateTestDir($@"{{ ""name"": ""{projectName}"", ""frameworks"": {{ ""netcoreapp1.0"": {{}} }} }}");
new PublishIISCommand(folders.PublishOutput, folders.ProjectPath).Run(); new PublishIISCommand(folders.PublishOutput, "netcoreapp1.0", folders.ProjectPath).Run();
var processPath = (string)GetPublishedWebConfig(folders.PublishOutput) var processPath = (string)GetPublishedWebConfig(folders.PublishOutput)
.Descendants("aspNetCore").Attributes("processPath").Single(); .Descendants("aspNetCore").Attributes("processPath").Single();
@ -52,9 +83,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
[InlineData("project.Dir")] [InlineData("project.Dir")]
public void PublishIIS_accepts_path_to_project_json_as_project_path(string projectDir) public void PublishIIS_accepts_path_to_project_json_as_project_path(string projectDir)
{ {
var folders = CreateTestDir("{}", projectDir); var folders = CreateTestDir(@"{ ""frameworks"": { ""netcoreapp1.0"": { } } }", projectDir);
new PublishIISCommand(folders.PublishOutput, Path.Combine(folders.ProjectPath, "project.json")).Run(); new PublishIISCommand(folders.PublishOutput, "netcoreapp1.0",
Path.Combine(folders.ProjectPath, "project.json")).Run();
var processPath = (string)GetPublishedWebConfig(folders.PublishOutput) var processPath = (string)GetPublishedWebConfig(folders.PublishOutput)
.Descendants("aspNetCore").Attributes("processPath").Single(); .Descendants("aspNetCore").Attributes("processPath").Single();
@ -67,7 +99,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
[Fact] [Fact]
public void PublishIIS_modifies_existing_web_config() public void PublishIIS_modifies_existing_web_config()
{ {
var folders = CreateTestDir("{}"); var folders = CreateTestDir(@"{ ""frameworks"": { ""netcoreapp1.0"": { } } }");
File.WriteAllText(Path.Combine(folders.PublishOutput, "web.config"), File.WriteAllText(Path.Combine(folders.PublishOutput, "web.config"),
@"<configuration> @"<configuration>
@ -79,7 +111,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
</system.webServer> </system.webServer>
</configuration>"); </configuration>");
new PublishIISCommand(folders.PublishOutput, Path.Combine(folders.ProjectPath, "project.json")).Run(); new PublishIISCommand(folders.PublishOutput, "netcoreapp1.0",
Path.Combine(folders.ProjectPath, "project.json")).Run();
var aspNetCoreElement = GetPublishedWebConfig(folders.PublishOutput) var aspNetCoreElement = GetPublishedWebConfig(folders.PublishOutput)
.Descendants("aspNetCore").Single(); .Descendants("aspNetCore").Single();

View File

@ -20,14 +20,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
public void WebConfigTransform_creates_new_config_if_one_does_not_exist() public void WebConfigTransform_creates_new_config_if_one_does_not_exist()
{ {
Assert.True(XNode.DeepEquals(WebConfigTemplate, Assert.True(XNode.DeepEquals(WebConfigTemplate,
WebConfigTransform.Transform(null, "test.exe", configureForAzure: false))); WebConfigTransform.Transform(null, "test.exe", configureForAzure: false, isPortable: false)));
} }
[Fact] [Fact]
public void WebConfigTransform_creates_new_config_if_one_has_unexpected_format() public void WebConfigTransform_creates_new_config_if_one_has_unexpected_format()
{ {
Assert.True(XNode.DeepEquals(WebConfigTemplate, Assert.True(XNode.DeepEquals(WebConfigTemplate,
WebConfigTransform.Transform(XDocument.Parse("<unexpected />"), "test.exe", configureForAzure: false))); WebConfigTransform.Transform(XDocument.Parse("<unexpected />"), "test.exe", configureForAzure: false, isPortable: false)));
} }
[Theory] [Theory]
@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
} }
Assert.True(XNode.DeepEquals(WebConfigTemplate, Assert.True(XNode.DeepEquals(WebConfigTemplate,
WebConfigTransform.Transform(input, "test.exe", configureForAzure: false))); WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false)));
} }
[Theory] [Theory]
@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
var input = WebConfigTemplate; var input = WebConfigTemplate;
input.Descendants(elementName).Single().SetAttributeValue(attributeName, attributeValue); input.Descendants(elementName).Single().SetAttributeValue(attributeName, attributeValue);
var output = WebConfigTransform.Transform(input, "test.exe", configureForAzure: false); var output = WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false);
Assert.Equal(attributeValue, (string)output.Descendants(elementName).Single().Attribute(attributeName)); Assert.Equal(attributeValue, (string)output.Descendants(elementName).Single().Attribute(attributeName));
} }
@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
public void WebConfigTransform_overwrites_processPath() public void WebConfigTransform_overwrites_processPath()
{ {
var newProcessPath = var newProcessPath =
(string)WebConfigTransform.Transform(WebConfigTemplate, "app.exe", configureForAzure: false) (string)WebConfigTransform.Transform(WebConfigTemplate, "app.exe", configureForAzure: false, isPortable: false)
.Descendants("aspNetCore").Single().Attribute("processPath"); .Descendants("aspNetCore").Single().Attribute("processPath");
Assert.Equal(@".\app.exe", newProcessPath); Assert.Equal(@".\app.exe", newProcessPath);
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
input.Descendants("add").Single().SetAttributeValue("name", "aspnetcore"); input.Descendants("add").Single().SetAttributeValue("name", "aspnetcore");
Assert.True(XNode.DeepEquals(WebConfigTemplate, Assert.True(XNode.DeepEquals(WebConfigTemplate,
WebConfigTransform.Transform(input, "test.exe", configureForAzure: false))); WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false)));
} }
[Fact] [Fact]
@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
input.Descendants("aspNetCore").Single().Add(envVarElement); input.Descendants("aspNetCore").Single().Add(envVarElement);
Assert.True(XNode.DeepEquals(envVarElement, Assert.True(XNode.DeepEquals(envVarElement,
WebConfigTransform.Transform(input, "app.exe", configureForAzure: false) WebConfigTransform.Transform(input, "app.exe", configureForAzure: false, isPortable: false)
.Descendants("environmentVariable").SingleOrDefault(e => (string)e.Attribute("name") == "ENVVAR"))); .Descendants("environmentVariable").SingleOrDefault(e => (string)e.Attribute("name") == "ENVVAR")));
} }
@ -110,7 +110,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
Assert.Equal( Assert.Equal(
"false", "false",
(string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false) (string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false)
.Descendants().Attributes("stdoutLogEnabled").Single()); .Descendants().Attributes("stdoutLogEnabled").Single());
} }
@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
Assert.Equal( Assert.Equal(
@".\logs\stdout", @".\logs\stdout",
(string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false) (string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false)
.Descendants().Attributes("stdoutLogFile").Single()); .Descendants().Attributes("stdoutLogFile").Single());
} }
@ -153,7 +153,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
Assert.Equal( Assert.Equal(
"mylog.txt", "mylog.txt",
(string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false) (string)WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false)
.Descendants().Attributes("stdoutLogFile").Single()); .Descendants().Attributes("stdoutLogFile").Single());
} }
@ -163,7 +163,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
var input = WebConfigTemplate; var input = WebConfigTemplate;
input.Descendants("aspNetCore").Attributes().Remove(); input.Descendants("aspNetCore").Attributes().Remove();
var aspNetCoreElement = WebConfigTransform.Transform(input, "test.exe", configureForAzure: true) var aspNetCoreElement = WebConfigTransform.Transform(input, "test.exe", configureForAzure: true, isPortable: false)
.Descendants("aspNetCore").Single(); .Descendants("aspNetCore").Single();
aspNetCoreElement.Elements().Remove(); aspNetCoreElement.Elements().Remove();
@ -173,6 +173,35 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
aspNetCoreElement)); aspNetCoreElement));
} }
[Fact]
public void WebConfigTransform_configures_portable_apps_correctly()
{
var aspNetCoreElement =
WebConfigTransform.Transform(WebConfigTemplate, "test.exe", configureForAzure: false, isPortable: true)
.Descendants("aspNetCore").Single();
Assert.True(XNode.DeepEquals(
XDocument.Parse(@"<aspNetCore processPath=""dotnet"" arguments="".\test.exe"" stdoutLogEnabled=""false""
stdoutLogFile="".\logs\stdout"" startupTimeLimit=""3600""/>").Root,
aspNetCoreElement));
}
[Fact]
public void WebConfigTransform_overwrites_existing_arguments_attribute_for_portable_apps()
{
var input = WebConfigTemplate;
input.Descendants("aspNetCore").Single().SetAttributeValue("arguments", "42");
var aspNetCoreElement =
WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: true)
.Descendants("aspNetCore").Single();
Assert.True(XNode.DeepEquals(
XDocument.Parse(@"<aspNetCore processPath=""dotnet"" arguments="".\test.exe"" stdoutLogEnabled=""false""
stdoutLogFile="".\logs\stdout"" startupTimeLimit=""3600""/>").Root,
aspNetCoreElement));
}
private bool VerifyMissingElementCreated(params string[] elementNames) private bool VerifyMissingElementCreated(params string[] elementNames)
{ {
var input = WebConfigTemplate; var input = WebConfigTemplate;
@ -182,7 +211,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.Tools.Tests
} }
return XNode.DeepEquals(WebConfigTemplate, return XNode.DeepEquals(WebConfigTemplate,
WebConfigTransform.Transform(input, "test.exe", configureForAzure: false)); WebConfigTransform.Transform(input, "test.exe", configureForAzure: false, isPortable: false));
} }
} }
} }