diff --git a/TestAssets/TestProjects/TestWebAppSimple/Program.cs b/TestAssets/TestProjects/TestWebAppSimple/Program.cs new file mode 100644 index 000000000..a15982830 --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/Program.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; + +namespace TestWebAppSimple +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} diff --git a/TestAssets/TestProjects/TestWebAppSimple/Startup.cs b/TestAssets/TestProjects/TestWebAppSimple/Startup.cs new file mode 100644 index 000000000..f00c04c75 --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/Startup.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace TestWebAppSimple +{ + public class Startup + { + // This method gets called by the runtime. Use this method to add services to the container. + // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) + { + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj b/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj new file mode 100644 index 000000000..f600e9d8e --- /dev/null +++ b/TestAssets/TestProjects/TestWebAppSimple/TestWebAppSimple.csproj @@ -0,0 +1,23 @@ + + + + + netcoreapp2.1 + + + + + + + + + + + + + + + diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 789a254bb..4af3e703c 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -1,7 +1,8 @@ - 2.1.0-preview2-30475 + 2.1.0-preview2-30475 + $(MicrosoftAspNetCoreAllPackageVersion) 2.1.0-rc1-26420-08 $(MicrosoftNETCoreAppPackageVersion) 15.7.0-preview-000169 @@ -17,7 +18,7 @@ 2.1.300-preview3-62819-01 $(MicrosoftNETSdkPackageVersion) $(MicrosoftAspNetCoreAppPackageVersion) - 2.1.300-rc1-20180418-1607241 + 2.1.300-rc1-20180420-1612100 $(MicrosoftNETSdkWebPackageVersion) $(MicrosoftNETSdkWebPackageVersion) 1.0.2-beta3-20180418-1607235 diff --git a/build/MSBuildExtensions.targets b/build/MSBuildExtensions.targets index a2a27d61a..67e864f1c 100644 --- a/build/MSBuildExtensions.targets +++ b/build/MSBuildExtensions.targets @@ -113,10 +113,14 @@ <_NETCoreAppPackageVersion>$(MicrosoftNETCoreAppPackageVersion) <_NETStandardLibraryPackageVersion>@(_NETStandardLibraryPackageVersions->Distinct()) <_NETCorePlatformsPackageVersion>@(_NETCorePlatformsPackageVersions->Distinct()) + <_AspNetCoreAllPackageVersion>$(MicrosoftAspNetCoreAllPackageVersion) + <_AspNetCoreAppPackageVersion>$(MicrosoftAspNetCoreAppPackageVersion) <_NETCoreAppTargetFrameworkVersion>$(_NETCoreAppPackageVersion.Split('.')[0]).$(_NETCoreAppPackageVersion.Split('.')[1]) <_NETStandardTargetFrameworkVersion>$(_NETStandardLibraryPackageVersion.Split('.')[0]).$(_NETStandardLibraryPackageVersion.Split('.')[1]) + <_AspNetCoreAllTargetFrameworkVersion>$(_NETCoreAppTargetFrameworkVersion) + <_AspNetCoreAppTargetFrameworkVersion>$(_AspNetCoreAllTargetFrameworkVersion) <_NETCoreSdkIsPreview Condition=" '$(DropSuffix)' == '' ">true @@ -140,9 +144,13 @@ Copyright (c) .NET Foundation. All rights reserved. $(_NETStandardTargetFrameworkVersion) $(_NETStandardLibraryPackageVersion) $(_NETCorePlatformsPackageVersion) + $(_AspNetCoreAllTargetFrameworkVersion) + $(_AspNetCoreAllPackageVersion) + $(_AspNetCoreAppTargetFrameworkVersion) + $(_AspNetCoreAppPackageVersion) $(SdkVersion) <_NETCoreSdkIsPreview>$(_NETCoreSdkIsPreview) - + 1.0.11 1.1.8 diff --git a/test/EndToEnd/GivenAspNetAppsResolveImplicitVersions.cs b/test/EndToEnd/GivenAspNetAppsResolveImplicitVersions.cs new file mode 100644 index 000000000..dc780f792 --- /dev/null +++ b/test/EndToEnd/GivenAspNetAppsResolveImplicitVersions.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using FluentAssertions; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using NuGet.ProjectModel; +using NuGet.Versioning; +using Xunit; + +namespace EndToEnd +{ + public class GivenAspNetAppsResolveImplicitVersions : TestBase + { + private const string AspNetTestProject = "TestWebAppSimple"; + + [Fact] + public void PortablePublishWithLatestTFMUsesBundledAspNetCoreAppVersion() + { + var _testInstance = TestAssets.Get(AspNetTestProject) + .CreateInstance(identifier: LatestSupportedAspNetCoreAppVersion) + .WithSourceFiles(); + + string projectDirectory = _testInstance.Root.FullName; + string projectPath = Path.Combine(projectDirectory, $"{AspNetTestProject}.csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + // Update TargetFramework to the right version of .NET Core + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value = "netcoreapp" + LatestSupportedAspNetCoreAppVersion; + + project.Save(projectPath); + + // Get the implicit version + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + var assetsFilePath = Path.Combine(projectDirectory, "obj", "project.assets.json"); + var assetsFile = new LockFileFormat().Read(assetsFilePath); + + var restoredVersion = GetAspNetCoreAppVersion(assetsFile, portable: true); + restoredVersion.Should().NotBeNull(); + + var bundledVersionPath = Path.Combine(projectDirectory, ".BundledAspNetCoreVersion"); + var bundledVersion = File.ReadAllText(bundledVersionPath).Trim(); + + restoredVersion.ToNormalizedString().Should().BeEquivalentTo(bundledVersion, + "The bundled aspnetcore versions set in Microsoft.NETCoreSdk.BundledVersions.props should be idenitical to the versions set in DependencyVersions.props." + + "Please update MSBuildExtensions.targets in this repo so these versions match."); + } + + [Fact] + public void StandalonePublishWithLatestTFMUsesBundledAspNetCoreAppVersion() + { + var _testInstance = TestAssets.Get(AspNetTestProject) + .CreateInstance(identifier: LatestSupportedAspNetCoreAppVersion) + .WithSourceFiles(); + + string projectDirectory = _testInstance.Root.FullName; + string projectPath = Path.Combine(projectDirectory, $"{AspNetTestProject}.csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + // Update TargetFramework to the right version of .NET Core + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value = "netcoreapp" + LatestSupportedAspNetCoreAppVersion; + + var rid = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier(); + + // Set RuntimeIdentifier to simulate standalone publish + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "RuntimeIdentifier", rid)); + + project.Save(projectPath); + + // Get the implicit version + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + var assetsFilePath = Path.Combine(projectDirectory, "obj", "project.assets.json"); + var assetsFile = new LockFileFormat().Read(assetsFilePath); + + var restoredVersion = GetAspNetCoreAppVersion(assetsFile); + restoredVersion.Should().NotBeNull(); + + var bundledVersionPath = Path.Combine(projectDirectory, ".BundledAspNetCoreVersion"); + var bundledVersion = File.ReadAllText(bundledVersionPath).Trim(); + + restoredVersion.ToNormalizedString().Should().BeEquivalentTo(bundledVersion, + "The bundled aspnetcore versions set in Microsoft.NETCoreSdk.BundledVersions.props should be idenitical to the versions set in DependencyVersions.props." + + "Please update MSBuildExtensions.targets in this repo so these versions match."); + } + + [Theory] + [MemberData(nameof(SupportedAspNetCoreAppVersions))] + public void ItRollsForwardToTheLatestVersion(string minorVersion) + { + var _testInstance = TestAssets.Get(AspNetTestProject) + .CreateInstance(identifier: minorVersion) + .WithSourceFiles(); + + string projectDirectory = _testInstance.Root.FullName; + + string projectPath = Path.Combine(projectDirectory, $"{AspNetTestProject}.csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + // Update TargetFramework to the right version of .NET Core + project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value = "netcoreapp" + minorVersion; + + var rid = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier(); + + // Set RuntimeIdentifier to opt in to roll-forward behavior + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "RuntimeIdentifier", rid)); + + project.Save(projectPath); + + // Get the version rolled forward to + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + string assetsFilePath = Path.Combine(projectDirectory, "obj", "project.assets.json"); + var assetsFile = new LockFileFormat().Read(assetsFilePath); + + var rolledForwardVersion = GetAspNetCoreAppVersion(assetsFile); + rolledForwardVersion.Should().NotBeNull(); + + if (rolledForwardVersion.IsPrerelease) + { + // If this version of .NET Core is still prerelease, then: + // - Floating the patch by adding ".*" to the major.minor version won't work, but + // - There aren't any patches to roll-forward to, so we skip testing this until the version + // leaves prerelease. + return; + } + + // Float the RuntimeFrameworkVersion to get the latest version of the runtime available from feeds + Directory.Delete(Path.Combine(projectDirectory, "obj"), true); + project.Root.Element(ns + "PropertyGroup") + .Add(new XElement(ns + "RuntimeFrameworkVersion", $"{minorVersion}.*")); + project.Save(projectPath); + + new RestoreCommand() + .WithWorkingDirectory(projectDirectory) + .Execute() + .Should().Pass(); + + var floatedAssetsFile = new LockFileFormat().Read(assetsFilePath); + + var floatedVersion = GetAspNetCoreAppVersion(floatedAssetsFile); + floatedVersion.Should().NotBeNull(); + + rolledForwardVersion.ToNormalizedString().Should().BeEquivalentTo(floatedVersion.ToNormalizedString(), + "the latest patch version properties in Microsoft.NETCoreSdk.BundledVersions.props need to be updated " + + "(see MSBuildExtensions.targets in this repo)"); + } + + [Fact] + public void WeCoverLatestAspNetCoreAppRollForward() + { + // Run "dotnet new web", get TargetFramework property, and make sure it's covered in SupportedAspNetCoreAppVersions + using (DisposableDirectory directory = Temp.CreateDirectory()) + { + string projectDirectory = directory.Path; + + new NewCommandShim() + .WithWorkingDirectory(projectDirectory) + .Execute("web --no-restore") + .Should().Pass(); + + string projectPath = Path.Combine(projectDirectory, Path.GetFileName(projectDirectory) + ".csproj"); + + var project = XDocument.Load(projectPath); + var ns = project.Root.Name.Namespace; + + string targetFramework = project.Root.Element(ns + "PropertyGroup") + .Element(ns + "TargetFramework") + .Value; + + SupportedAspNetCoreAppVersions.Select(v => $"netcoreapp{v[0]}") + .Should().Contain(targetFramework, $"the {nameof(SupportedAspNetCoreAppVersions)} property should include the default version " + + "of Microsoft.AspNetCore.App used by the templates created by \"dotnet new web\""); + + } + } + + private NuGetVersion GetAspNetCoreAppVersion(LockFile lockFile, bool portable = false) + { + return lockFile?.Targets?.SingleOrDefault(t => portable || t.RuntimeIdentifier != null) + ?.Libraries?.SingleOrDefault(l => + string.Compare(l.Name, "Microsoft.AspNetCore.App", StringComparison.CurrentCultureIgnoreCase) == 0) + ?.Version; + } + + public static string LatestSupportedAspNetCoreAppVersion = "2.1"; + + public static IEnumerable SupportedAspNetCoreAppVersions + { + get + { + yield return new object[] { LatestSupportedAspNetCoreAppVersion }; + } + } + } +}