Automatically inject ASP.NET version from KoreBuild lineup into template nupkg files

This commit is contained in:
Steve Sanderson 2017-08-30 19:10:07 +01:00
parent 1255418146
commit d6fdeb7e19
14 changed files with 155 additions and 211 deletions

View File

@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Web.Projec
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TemplatePackages", "TemplatePackages", "{0AD6E692-E423-408C-B523-DAFB19412E4B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DependencyUpdater", "tools\DependencyUpdater\DependencyUpdater.csproj", "{15806338-550D-4418-99AE-42FDAE03808A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -35,6 +37,10 @@ Global
{62D00388-8824-4661-8CC8-8D8436FF97E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62D00388-8824-4661-8CC8-8D8436FF97E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62D00388-8824-4661-8CC8-8D8436FF97E6}.Release|Any CPU.Build.0 = Release|Any CPU
{15806338-550D-4418-99AE-42FDAE03808A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15806338-550D-4418-99AE-42FDAE03808A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{15806338-550D-4418-99AE-42FDAE03808A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15806338-550D-4418-99AE-42FDAE03808A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -10,8 +10,9 @@
<NoWarn>2008;8021</NoWarn>
<PackageVersion Condition="'$(VersionSuffix)' == ''">$(VersionPrefix)</PackageVersion>
<PackageVersion Condition="'$(VersionSuffix)' != ''">$(VersionPrefix)-$(VersionSuffix)</PackageVersion>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)..\artifacts\</PackageOutputPath>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(MSBuildThisFileDirectory)..\artifacts\tmp\</PackageOutputPath>
<NuspecProperties>version=$(PackageVersion)</NuspecProperties>
<AspNetCoreVersionFromLineup>@(PackageReference->WithMetadataValue('Identity', 'Microsoft.AspNetCore.All')->Metadata('Version'))</AspNetCoreVersionFromLineup>
<!--
These are not regular projects. They are just wrappers for the .nuspec files in each
@ -26,8 +27,15 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="Microsoft.NETCore.App" />
<ProjectReference Include="$(MSBuildThisFileDirectory)../tools/DependencyUpdater/DependencyUpdater.csproj" />
<None Include="Content/**" />
<!--
Having this package reference causes KoreBuild to pick the applicable
ASP.NET Core version from the lineup, making it possible to declare
the AspNetCoreVersionFromLineup variable above.
-->
<PackageReference Include="Microsoft.AspNetCore.All" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
<Project>
<Import Project="..\Directory.Build.targets" />
<!--
Once we've written each template .nupkg file, we modify the ASP.NET references
in its .csproj content files, injecting whatever version number exists in
the curent build context.
-->
<UsingTask
TaskName="DependencyUpdater.UpdateAspNetReference"
AssemblyFile="$(MSBuildProjectDirectory)\$(OutputPath)DependencyUpdater.dll" />
<Target Name="RunTask" AfterTargets="Pack">
<UpdateAspNetReference
NupkgFile="$(PackageOutputPath)$(MSBuildProjectName).$(PackageVersion).nupkg"
AspNetVersion="$(AspNetCoreVersionFromLineup)"
OutDir="$(PackageOutputPath)..\" />
</Target>
</Project>

View File

@ -10,10 +10,10 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
</Project>

View File

@ -11,7 +11,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
</Project>

View File

@ -10,19 +10,19 @@
<None Update="app.db" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualAuth)' == 'True'" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualAuth)' == 'True'" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.0.0" Condition="'$(IndividualAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="2.0.0" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.0.0" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="$(TemplateAspNetCoreVersion)" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="$(TemplateAspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />

View File

@ -15,19 +15,19 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualAuth)' == 'True'" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualAuth)' == 'True'" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.0.0" Condition="'$(IndividualAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="2.0.0" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="2.0.0" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.0.0" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualAuth)' == 'True' OR '$(OrganizationalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="$(TemplateAspNetCoreVersion)" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="$(TemplateAspNetCoreVersion)" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="$(TemplateAspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" PrivateAssets="All" Condition="'$(IndividualLocalAuth)' == 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.0" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' != 'True'" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.0.0" Condition=" '$(IndividualLocalAuth)' == 'True' AND '$(UseLocalDB)' == 'True'" />

View File

@ -14,13 +14,13 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.ViewCompilation" Version="$(TemplateAspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
</Project>

View File

@ -13,12 +13,12 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.0.0" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(TemplateAspNetCoreVersion)" Condition="'$(OrganizationalAuth)' == 'True' OR '$(IndividualB2CAuth)' == 'True'" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
<ItemGroup>

View File

@ -12,11 +12,11 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' == ''">
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.All" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkOverride)' != ''">
<PackageReference Include="Microsoft.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="$(TemplateAspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(TemplateAspNetCoreVersion)" />
</ItemGroup>
</Project>

View File

@ -1,6 +1,11 @@
<Project Sdk="Microsoft.Net.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<OutputType>Exe</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" />
<PackageReference Include="Microsoft.Build.Utilities.Core" />
</ItemGroup>
</Project>

View File

@ -1,66 +0,0 @@
using System.Text;
namespace DependencyUpdater
{
public class EncodingUtil
{
/// <remarks>http://www.unicode.org/faq/utf_bom.html</remarks>
public static Encoding Detect(byte[] buffer, int currentBufferLength, out byte[] bom)
{
if (currentBufferLength == 0)
{
//File is zero length - pick something
bom = new byte[0];
return Encoding.UTF8;
}
if (currentBufferLength >= 4)
{
if (buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0xFE && buffer[3] == 0xFF)
{
//Big endian UTF-32
bom = new byte[] { 0x00, 0x00, 0xFE, 0xFF };
return Encoding.GetEncoding(12001);
}
if (buffer[0] == 0xFF && buffer[1] == 0xFE && buffer[2] == 0x00 && buffer[3] == 0x00)
{
//Little endian UTF-32
bom = new byte[] { 0xFF, 0xFE, 0x00, 0x00 };
return Encoding.UTF32;
}
}
if (currentBufferLength >= 3)
{
if (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF)
{
//UTF-8
bom = new byte[] { 0xEF, 0xBB, 0xBF };
return Encoding.UTF8;
}
}
if (currentBufferLength >= 2)
{
if (buffer[0] == 0xFE && buffer[1] == 0xFF)
{
//Big endian UTF-16
bom = new byte[] { 0xFE, 0xFF };
return Encoding.BigEndianUnicode;
}
if (buffer[0] == 0xFF && buffer[1] == 0xFE)
{
//Little endian UTF-16
bom = new byte[] { 0xFF, 0xFE };
return Encoding.Unicode;
}
}
//Fallback to UTF-8
bom = new byte[0];
return Encoding.UTF8;
}
}
}

View File

@ -1,108 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace DependencyUpdater
{
class Program
{
static int Main(string[] args)
{
if(args.Length < 2)
{
Console.WriteLine(@"Usage:
dotnet DependencyUpdater.dll [path to templates:string] [infer no-timestamp packages from semaphore files] [[paths to package sources]]");
return -1;
}
bool NoTimestamp = bool.Parse(args[1]);
string root = args[0];
List<string> sources = new List<string>();
foreach(string arg in args.Skip(2))
{
if (Directory.Exists(arg))
{
sources.Add(arg);
}
else if (File.Exists(arg))
{
string sem = File.ReadAllText(arg);
foreach (string line in sem.Split('\n'))
{
string trimmed = line.Trim();
string share = trimmed.Substring(trimmed.LastIndexOf(':') + 1).Trim();
string source = Path.Combine(share, @"Signed\Packages") + (NoTimestamp ? "-NoTimeStamp" : "");
if (Directory.Exists(source))
{
sources.Add(source);
}
}
}
else
{
Console.Error.WriteLine($"Unknown source: \"{arg}\"");
}
}
Regex versionPattern = new Regex(@"\d+\.\d+\.\d+([-\+].*)?");
List<Tuple<string, Regex, string>> replacements = new List<Tuple<string, Regex, string>>();
foreach (string source in sources)
{
foreach (string file in Directory.EnumerateFiles(source, "*.nupkg", SearchOption.AllDirectories))
{
string packageIdAndVersion = Path.GetFileNameWithoutExtension(file);
Match m = versionPattern.Match(packageIdAndVersion);
string packageId = packageIdAndVersion.Substring(0, m.Index).TrimEnd('.');
string version = packageIdAndVersion.Substring(m.Index);
int versionFirstDot = version.IndexOf('.');
int versionSecondDot = version.IndexOf('.', versionFirstDot + 1);
string majorMinor = version.Substring(0, versionSecondDot);
string majorMinorEscaped = Regex.Escape(majorMinor);
Regex rx = new Regex($"(?<=\"{packageId}\"\\s+Version\\s*=\\s*\"){majorMinorEscaped}[^\"]*(?=\")");
replacements.Add(Tuple.Create(packageId, rx, version));
}
}
foreach(string project in Directory.EnumerateFiles(root, "*.*proj", SearchOption.AllDirectories))
{
if(Path.GetExtension(project).ToUpperInvariant() == ".PROJ" || Path.GetFileName(project).ToUpperInvariant() == "BUILD.CSPROJ")
{
continue;
}
string source = File.ReadAllText(project);
string result = source;
foreach(Tuple<string, Regex, string> replacement in replacements)
{
string orig = result;
result = replacement.Item2.Replace(result, replacement.Item3);
if(orig != result)
{
Console.WriteLine($"{project.Substring(root.Length).TrimStart('\\', '/')}: {replacement.Item1} -> {replacement.Item3}");
}
}
byte[] data = File.ReadAllBytes(project);
Encoding encoding = EncodingUtil.Detect(data, data.Length, out byte[] bom);
if(bom.Length == 0 && Encoding.UTF8.EncodingName == encoding.EncodingName)
{
encoding = new UTF8Encoding(false);
}
File.WriteAllText(project, result, encoding);
}
return 0;
}
}
}

View File

@ -0,0 +1,78 @@
using Microsoft.Build.Utilities;
using System;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace DependencyUpdater
{
public class UpdateAspNetReference : Task
{
private static string[] ProjectFileExtensions = new[]
{
".csproj",
".fsproj"
};
public string NupkgFile { get; set; }
public string AspNetVersion { get; set; }
public string OutDir { get; set; }
public override bool Execute()
{
if (string.IsNullOrEmpty(AspNetVersion))
{
throw new ArgumentException($"No value specified for {nameof(AspNetVersion)}.");
}
// We can't modify the .nupkg in place because the build system still
// has a lock on the file. We can read it, but not write it. So copy
// to the output location and then modify the copy.
var outFile = Path.Combine(OutDir, Path.GetFileName(NupkgFile));
File.Copy(NupkgFile, outFile, true);
using (var zipFile = ZipFile.Open(outFile, ZipArchiveMode.Update))
{
foreach (var projectFile in zipFile.Entries.Where(IsProjectFile))
{
PerformVariableSubstitution(projectFile);
}
}
return true;
}
private static bool IsProjectFile(ZipArchiveEntry entry)
{
return ProjectFileExtensions.Any(
extension => Path.GetExtension(entry.Name).Equals(extension, StringComparison.OrdinalIgnoreCase));
}
private void PerformVariableSubstitution(ZipArchiveEntry entry)
{
using (var fileStream = entry.Open())
{
string contents;
using (var reader = new StreamReader(fileStream))
using (var writer = new StreamWriter(fileStream))
{
contents = reader.ReadToEnd();
fileStream.Seek(0, SeekOrigin.Begin);
fileStream.SetLength(0);
writer.Write(SubstituteVariables(contents));
}
}
}
private string SubstituteVariables(string projectFileContents)
{
// Currently we only need a way of updating ASP.NET package
// reference versions, so that's all this does. In the future,
// we could generalise this into a system for injecting
// versions for all packages based on the KoreBuild lineup.
return projectFileContents.Replace(
"$(TemplateAspNetCoreVersion)",
AspNetVersion);
}
}
}