зеркало из https://github.com/dotnet/razor.git
Merge branch 'release/3.1'
This commit is contained in:
Коммит
0ef68536ee
|
@ -15,6 +15,14 @@ variables:
|
|||
value: .NETCORE
|
||||
- name: _DotNetValidationArtifactsCategory
|
||||
value: .NETCOREVALIDATION
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- group: DotNet-MSRC-Storage
|
||||
- name: _InternalRuntimeDownloadArgs
|
||||
value: /p:DotNetRuntimeSourceFeed=https://dotnetclimsrc.blob.core.windows.net/dotnet
|
||||
/p:DotNetRuntimeSourceFeedKey=$(dotnetclimsrc-read-sas-token-base64)
|
||||
- ${{ if eq(variables['System.TeamProject'], 'public') }}:
|
||||
- name: _InternalRuntimeDownloadArgs
|
||||
value: ''
|
||||
|
||||
# used for post-build phases, internal builds only
|
||||
- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
|
||||
|
@ -65,7 +73,15 @@ stages:
|
|||
inputs:
|
||||
command: custom
|
||||
arguments: 'locals all -clear'
|
||||
- powershell: ./restore.cmd -ci; ./eng/scripts/CodeCheck.ps1 -ci
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Setup Private Feeds Credentials
|
||||
inputs:
|
||||
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
|
||||
arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
|
||||
env:
|
||||
Token: $(dn-bot-dnceng-artifact-feeds-rw)
|
||||
- powershell: ./restore.cmd -ci $(_InternalRuntimeDownloadArgs); ./eng/scripts/CodeCheck.ps1 -ci
|
||||
displayName: Run eng/scripts/CodeCheck.ps1
|
||||
|
||||
- job: Source_Build
|
||||
|
@ -92,6 +108,14 @@ stages:
|
|||
chmod +x $HOME/bin/jq
|
||||
echo "##vso[task.prependpath]$HOME/bin"
|
||||
displayName: Install jq
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- task: Bash@3
|
||||
displayName: Setup Private Feeds Credentials
|
||||
inputs:
|
||||
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
|
||||
arguments: $(Build.SourcesDirectory)/NuGet.config $Token
|
||||
env:
|
||||
Token: $(dn-bot-dnceng-artifact-feeds-rw)
|
||||
- script: ./eng/scripts/ci-source-build.sh --ci --configuration $(_BuildConfig) /p:BuildNodeJs=false
|
||||
displayName: Run ci-source-build.sh
|
||||
|
||||
|
@ -188,11 +212,20 @@ stages:
|
|||
displayName: Install ProcDump
|
||||
- powershell: ./eng/scripts/StartDumpCollectionForHangingBuilds.ps1 $(ProcDumpPath)procdump.exe artifacts/log/$(_BuildConfig) (Get-Date).AddMinutes(25) dotnet, msbuild
|
||||
displayName: Start background dump collection
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- task: PowerShell@2
|
||||
displayName: Setup Private Feeds Credentials
|
||||
inputs:
|
||||
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
|
||||
arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
|
||||
env:
|
||||
Token: $(dn-bot-dnceng-artifact-feeds-rw)
|
||||
- script: eng\common\cibuild.cmd
|
||||
-configuration $(_BuildConfig)
|
||||
-prepareMachine
|
||||
$(_BuildArgs)
|
||||
$(_PublishArgs)
|
||||
$(_InternalRuntimeDownloadArgs)
|
||||
name: Build
|
||||
displayName: Build
|
||||
condition: succeeded()
|
||||
|
@ -249,9 +282,18 @@ stages:
|
|||
displayName: Install Node 10.x
|
||||
inputs:
|
||||
versionSpec: 10.x
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- task: Bash@3
|
||||
displayName: Setup Private Feeds Credentials
|
||||
inputs:
|
||||
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
|
||||
arguments: $(Build.SourcesDirectory)/NuGet.config $Token
|
||||
env:
|
||||
Token: $(dn-bot-dnceng-artifact-feeds-rw)
|
||||
- script: eng/common/cibuild.sh
|
||||
--configuration $(_BuildConfig)
|
||||
--prepareMachine
|
||||
$(_InternalRuntimeDownloadArgs)
|
||||
name: Build
|
||||
displayName: Build
|
||||
condition: succeeded()
|
||||
|
@ -282,9 +324,18 @@ stages:
|
|||
displayName: Install Node 10.x
|
||||
inputs:
|
||||
versionSpec: 10.x
|
||||
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
|
||||
- task: Bash@3
|
||||
displayName: Setup Private Feeds Credentials
|
||||
inputs:
|
||||
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
|
||||
arguments: $(Build.SourcesDirectory)/NuGet.config $Token
|
||||
env:
|
||||
Token: $(dn-bot-dnceng-artifact-feeds-rw)
|
||||
- script: eng/common/cibuild.sh
|
||||
--configuration $(_BuildConfig)
|
||||
--prepareMachine
|
||||
$(_InternalRuntimeDownloadArgs)
|
||||
name: Build
|
||||
displayName: Build
|
||||
condition: succeeded()
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
private const string ContentRoot = "ContentRoot";
|
||||
private const string BasePath = "BasePath";
|
||||
private const string SourceId = "SourceId";
|
||||
|
||||
[Required]
|
||||
public string TargetManifestPath { get; set; }
|
||||
|
@ -76,13 +77,19 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
// so it needs to always be '/'.
|
||||
var normalizedBasePath = basePath.Replace("\\", "/");
|
||||
|
||||
// contentRoot can have forward and trailing slashes and sometimes consecutive directory
|
||||
// separators. To be more flexible we will normalize the content root so that it contains a
|
||||
// single trailing separator.
|
||||
var normalizedContentRoot = $"{contentRoot.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)}{Path.DirectorySeparatorChar}";
|
||||
|
||||
// At this point we already know that there are no elements with different base paths and same content roots
|
||||
// or viceversa. Here we simply skip additional items that have the same base path and same content root.
|
||||
if (!nodes.Exists(e => e.Attribute(BasePath).Value.Equals(normalizedBasePath, StringComparison.OrdinalIgnoreCase)))
|
||||
if (!nodes.Exists(e => e.Attribute("BasePath").Value.Equals(normalizedBasePath, StringComparison.OrdinalIgnoreCase) &&
|
||||
e.Attribute("Path").Value.Equals(normalizedContentRoot, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
nodes.Add(new XElement("ContentRoot",
|
||||
new XAttribute("BasePath", normalizedBasePath),
|
||||
new XAttribute("Path", contentRoot)));
|
||||
new XAttribute("Path", normalizedContentRoot)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +109,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
var contentRootDefinition = ContentRootDefinitions[i];
|
||||
if (!EnsureRequiredMetadata(contentRootDefinition, BasePath) ||
|
||||
!EnsureRequiredMetadata(contentRootDefinition, ContentRoot))
|
||||
!EnsureRequiredMetadata(contentRootDefinition, ContentRoot) ||
|
||||
!EnsureRequiredMetadata(contentRootDefinition, SourceId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -126,23 +134,35 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
var contentRootDefinition = ContentRootDefinitions[i];
|
||||
var basePath = contentRootDefinition.GetMetadata(BasePath);
|
||||
var contentRoot = contentRootDefinition.GetMetadata(ContentRoot);
|
||||
var sourceId = contentRootDefinition.GetMetadata(SourceId);
|
||||
|
||||
if (basePaths.TryGetValue(basePath, out var existingBasePath))
|
||||
{
|
||||
var existingBasePathContentRoot = existingBasePath.GetMetadata(ContentRoot);
|
||||
if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase))
|
||||
var existingSourceId = existingBasePath.GetMetadata(SourceId);
|
||||
if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase) &&
|
||||
// We want to check this case to allow for client-side blazor projects to have multiple different content
|
||||
// root sources exposed under the same base path while still requiring unique base paths/content roots across
|
||||
// project/package boundaries.
|
||||
!string.Equals(sourceId, existingSourceId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Case:
|
||||
// Item1: /_content/Library, /package/aspnetContent1
|
||||
// Item2: /_content/Library, /package/aspnetContent2
|
||||
// Item2: /_content/Library, project:/project/aspnetContent2
|
||||
// Item1: /_content/Library, package:/package/aspnetContent1
|
||||
Log.LogError($"Duplicate base paths '{basePath}' for content root paths '{contentRoot}' and '{existingBasePathContentRoot}'. " +
|
||||
$"('{contentRootDefinition.ItemSpec}', '{existingBasePath.ItemSpec}')");
|
||||
return false;
|
||||
}
|
||||
|
||||
// It was a duplicate, so we skip it.
|
||||
// Case:
|
||||
// Item1: /_content/Library, /package/aspnetContent
|
||||
// Item2: /_content/Library, /package/aspnetContent
|
||||
// Item1: /_content/Library, project:/project/aspnetContent
|
||||
// Item2: /_content/Library, project:/project/aspnetContent
|
||||
|
||||
// It was a separate content root exposed from the same project/package, so we skip it.
|
||||
// Case:
|
||||
// Item1: /_content/Library, project:/project/aspnetContent/bin/debug/netstandard2.1/dist
|
||||
// Item2: /_content/Library, project:/project/wwwroot
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
// 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 Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
public class ValidateStaticWebAssetsUniquePaths : Task
|
||||
{
|
||||
private const string BasePath = "BasePath";
|
||||
private const string RelativePath = "RelativePath";
|
||||
private const string TargetPath = "TargetPath";
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] StaticWebAssets { get; set; }
|
||||
|
||||
[Required]
|
||||
public ITaskItem[] WebRootFiles { get; set; }
|
||||
|
||||
public override bool Execute()
|
||||
{
|
||||
var assetsByWebRootPaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
|
||||
for (var i = 0; i < StaticWebAssets.Length; i++)
|
||||
{
|
||||
var contentRootDefinition = StaticWebAssets[i];
|
||||
if (!EnsureRequiredMetadata(contentRootDefinition, BasePath) ||
|
||||
!EnsureRequiredMetadata(contentRootDefinition, RelativePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var webRootPath = GetWebRootPath(
|
||||
contentRootDefinition.GetMetadata(BasePath),
|
||||
contentRootDefinition.GetMetadata(RelativePath));
|
||||
|
||||
if (assetsByWebRootPaths.TryGetValue(webRootPath, out var existingWebRootPath))
|
||||
{
|
||||
if (!string.Equals(contentRootDefinition.ItemSpec, existingWebRootPath.ItemSpec, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Log.LogError($"Conflicting assets with the same path '{webRootPath}' for content root paths '{contentRootDefinition.ItemSpec}' and '{existingWebRootPath.ItemSpec}'.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assetsByWebRootPaths.Add(webRootPath, contentRootDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < WebRootFiles.Length; i++)
|
||||
{
|
||||
var webRootFile = WebRootFiles[i];
|
||||
var relativePath = webRootFile.GetMetadata(TargetPath);
|
||||
var webRootFileWebRootPath = GetWebRootPath("/", relativePath);
|
||||
if (assetsByWebRootPaths.TryGetValue(webRootFileWebRootPath, out var existingAsset))
|
||||
{
|
||||
Log.LogError($"The static web asset '{existingAsset.ItemSpec}' has a conflicting web root path '{webRootFileWebRootPath}' with the project file '{webRootFile.ItemSpec}'.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Normalizes /base/relative \base\relative\ base\relative and so on to /base/relative
|
||||
private string GetWebRootPath(string basePath, string relativePath) => $"/{Path.Combine(basePath, relativePath.TrimStart('.').TrimStart('/')).Replace("\\", "/").Trim('/')}";
|
||||
|
||||
private bool EnsureRequiredMetadata(ITaskItem item, string metadataName)
|
||||
{
|
||||
var value = item.GetMetadata(metadataName);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
Log.LogError($"Missing required metadata '{metadataName}' for '{item.ItemSpec}'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,11 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
|
||||
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
|
||||
|
||||
<UsingTask
|
||||
TaskName="Microsoft.AspNetCore.Razor.Tasks.ValidateStaticWebAssetsUniquePaths"
|
||||
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
|
||||
Condition="'$(RazorSdkBuildTasksAssembly)' != ''" />
|
||||
|
||||
<UsingTask
|
||||
TaskName="Microsoft.AspNetCore.Razor.Tasks.GenerateStaticWebAsssetsPropsFile"
|
||||
AssemblyFile="$(RazorSdkBuildTasksAssembly)"
|
||||
|
@ -54,23 +59,19 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
$(GetCurrentProjectStaticWebAssetsDependsOn)
|
||||
</GetCurrentProjectStaticWebAssetsDependsOn>
|
||||
|
||||
<AssignTargetPathsDependsOn>
|
||||
<GetCopyToOutputDirectoryItemsDependsOn>
|
||||
$(GetCopyToOutputDirectoryItemsDependsOn);
|
||||
GenerateStaticWebAssetsManifest;
|
||||
$(AssignTargetPathsDependsOn)
|
||||
</AssignTargetPathsDependsOn>
|
||||
</GetCopyToOutputDirectoryItemsDependsOn>
|
||||
|
||||
<ResolveStaticWebAssetsInputsDependsOn>
|
||||
ResolveCurrentProjectStaticWebAssetsInputs;
|
||||
$(ResolveStaticWebAssetsInputsDependsOn)
|
||||
</ResolveStaticWebAssetsInputsDependsOn>
|
||||
|
||||
<ResolveStaticWebAssetsInputsDependsOn Condition="$(NoBuild) != 'true'">
|
||||
ResolveReferencedProjectsStaticWebAssets;
|
||||
$(ResolveStaticWebAssetsInputsDependsOn)
|
||||
</ResolveStaticWebAssetsInputsDependsOn>
|
||||
|
||||
<ResolveReferencedProjectsStaticWebAssetsDependsOn>
|
||||
ResolveReferences;
|
||||
PrepareProjectReferences;
|
||||
$(ResolveReferencedProjectsStaticWebAssetsDependsOn)
|
||||
</ResolveReferencedProjectsStaticWebAssetsDependsOn>
|
||||
|
||||
|
@ -145,11 +146,12 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
Condition="'%(SourceType)' != ''">
|
||||
<BasePath>%(StaticWebAsset.BasePath)</BasePath>
|
||||
<ContentRoot>%(StaticWebAsset.ContentRoot)</ContentRoot>
|
||||
<SourceId>%(StaticWebAsset.SourceId)</SourceId>
|
||||
</_ExternalStaticWebAsset>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- We need a transform here to make sure we hash the metadata -->
|
||||
<Hash ItemsToHash="@(_ExternalStaticWebAsset->'%(Identity)%(BasePath)%(ContentRoot)')">
|
||||
<Hash ItemsToHash="@(_ExternalStaticWebAsset->'%(Identity)%(SourceId)%(BasePath)%(ContentRoot)')">
|
||||
<Output TaskParameter="HashResult" PropertyName="_StaticWebAssetsCacheHash" />
|
||||
</Hash>
|
||||
|
||||
|
@ -181,20 +183,29 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
Outputs="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
|
||||
DependsOnTargets="$(GenerateStaticWebAssetsManifestDependsOn)">
|
||||
|
||||
<ItemGroup>
|
||||
<_WebRootFiles Include="@(ContentWithTargetPath)" Condition="$([System.String]::Copy('%(TargetPath)').Replace('\','/').StartsWith('wwwroot/'))" />
|
||||
<_ReferencedStaticWebAssets Include="@(StaticWebAsset)" Condition="'%(SourceType)' != ''" />
|
||||
</ItemGroup>
|
||||
|
||||
<ValidateStaticWebAssetsUniquePaths
|
||||
StaticWebAssets="@(_ReferencedStaticWebAssets)"
|
||||
WebRootFiles="@(_WebRootFiles)" />
|
||||
|
||||
<GenerateStaticWebAssetsManifest
|
||||
ContentRootDefinitions="@(_ExternalStaticWebAsset)"
|
||||
TargetManifestPath="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
|
||||
|
||||
<!-- This is the list of inputs that will be used for generating the manifest used during development. -->
|
||||
<ItemGroup>
|
||||
<Content
|
||||
<ContentWithTargetPath
|
||||
Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
|
||||
Condition="'@(_ExternalStaticWebAsset->Count())' != '0'"
|
||||
Link="$(TargetName).StaticWebAssets.xml">
|
||||
Condition="'@(_ExternalStaticWebAsset->Count())' != '0'">
|
||||
|
||||
<TargetPath>$(TargetName).StaticWebAssets.xml</TargetPath>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -273,7 +284,7 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
|
||||
<_ThisProjectStaticWebAsset
|
||||
Include="@(Content)"
|
||||
Condition="$([System.String]::Copy('%(Identity)').StartsWith('wwwroot'))">
|
||||
Condition="$([System.String]::Copy('%(Identity)').Replace('\','/').StartsWith('wwwroot/'))">
|
||||
|
||||
<!-- Remove the wwwroot\ prefix -->
|
||||
<RelativePath>$([System.String]::Copy('%(Identity)').Substring(8))</RelativePath>
|
||||
|
@ -506,14 +517,15 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
|
||||
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
|
||||
<RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))'))</RelativePath>
|
||||
|
||||
</_ExternalPublishStaticWebAsset>
|
||||
|
||||
<!-- Remove any existing external static web asset that might have been added as part of the
|
||||
regular publish pipeline. -->
|
||||
<ResolvedFileToPublish Remove="@(_ExternalPublishStaticWebAsset)" />
|
||||
|
||||
<ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)" />
|
||||
<ResolvedFileToPublish Include="@(_ExternalPublishStaticWebAsset)">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</ResolvedFileToPublish>
|
||||
</ItemGroup>
|
||||
|
||||
</Target>
|
||||
|
|
|
@ -89,12 +89,14 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine("nuget","MyLibrary")
|
||||
["ContentRoot"] = Path.Combine("nuget", "MyLibrary"),
|
||||
["SourceId"] = "MyLibrary"
|
||||
}),
|
||||
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary")
|
||||
["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary"),
|
||||
["SourceId"] = "MyOtherLibrary"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
@ -111,6 +113,58 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowsMultipleContentRootsWithSameBasePath_ForTheSameSourceId()
|
||||
{
|
||||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", $"Blazor.Client{Path.DirectorySeparatorChar}")}"" />
|
||||
<ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", "bin", "debug", $"netstandard2.1{Path.DirectorySeparatorChar}")}"" />
|
||||
</StaticWebAssets>";
|
||||
|
||||
var buildEngine = new Mock<IBuildEngine>();
|
||||
|
||||
var task = new GenerateStaticWebAssetsManifest
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
ContentRootDefinitions = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "Blazor.Client",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget","Blazor.Client"),
|
||||
["SourceId"] = "Blazor.Client"
|
||||
}),
|
||||
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "Blazor.Client",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "bin","debug","netstandard2.1"),
|
||||
["SourceId"] = "Blazor.Client"
|
||||
})
|
||||
},
|
||||
TargetManifestPath = file
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
var document = File.ReadAllText(file);
|
||||
Assert.Equal(expectedDocument, document);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(file))
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsError_ForDuplicateContentRoots()
|
||||
{
|
||||
|
@ -128,11 +182,13 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["SourceId"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "MyLibrary")
|
||||
}),
|
||||
CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyOtherLibrary",
|
||||
["SourceId"] = "MyOtherLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "MyLibrary")
|
||||
})
|
||||
}
|
||||
|
@ -191,7 +247,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
|
||||
<ContentRoot BasePath=""MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", $"razorContent{Path.DirectorySeparatorChar}")}"" />
|
||||
</StaticWebAssets>";
|
||||
|
||||
try
|
||||
|
@ -206,7 +262,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent"),
|
||||
["SourceId"] = "MyLibrary"
|
||||
}),
|
||||
},
|
||||
TargetManifestPath = file
|
||||
|
@ -235,7 +292,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
// Arrange
|
||||
var file = Path.GetTempFileName();
|
||||
var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
|
||||
<ContentRoot BasePath=""Base/MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", "razorContent")}"" />
|
||||
<ContentRoot BasePath=""Base/MyLibrary"" Path=""{Path.Combine(".", "nuget", "MyLibrary", $"razorContent{Path.DirectorySeparatorChar}")}"" />
|
||||
</StaticWebAssets>";
|
||||
|
||||
try
|
||||
|
@ -251,12 +308,14 @@ namespace Microsoft.AspNetCore.Razor.Tasks
|
|||
{
|
||||
// Base path needs to be normalized to '/' as it goes in the url
|
||||
["BasePath"] = "Base\\MyLibrary",
|
||||
["SourceId"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLibrary", "razorContent")
|
||||
}),
|
||||
// Comparisons are case insensitive
|
||||
CreateItem(Path.Combine("wwwroot, site.css"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "Base\\MyLIBRARY",
|
||||
["SourceId"] = "MyLibrary",
|
||||
["ContentRoot"] = Path.Combine(".", "nuget", "MyLIBRARY", "razorContent")
|
||||
}),
|
||||
},
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
||||
|
@ -221,5 +224,49 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.BuildOutputContainsLine(result, "Content: appsettings.json CopyToOutputDirectory= CopyToPublishDirectory=PreserveNewest ExcludeFromSingleFile=true");
|
||||
Assert.BuildOutputContainsLine(result, "Content: appsettings.Development.json CopyToOutputDirectory= CopyToPublishDirectory=PreserveNewest ExcludeFromSingleFile=true");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task IntrospectRazorTasksDllPath()
|
||||
{
|
||||
// Regression test for https://github.com/aspnet/AspNetCore/issues/17308
|
||||
var solutionRoot = GetType().Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
|
||||
.First(a => a.Key == "Testing.RepoRoot")
|
||||
.Value;
|
||||
|
||||
var tfm =
|
||||
#if NETCOREAPP3_1
|
||||
"netcoreapp3.1";
|
||||
#else
|
||||
#error Target framework needs to be updated.
|
||||
#endif
|
||||
|
||||
var expected = Path.Combine(solutionRoot, "artifacts", "bin", "Microsoft.NET.Sdk.Razor", Configuration, "sdk-output", "tasks", tfm, "Microsoft.NET.Sdk.Razor.Tasks.dll");
|
||||
|
||||
// Verifies the fix for https://github.com/aspnet/AspNetCore/issues/17308
|
||||
var result = await DotnetMSBuild("_IntrospectRazorTasks");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
Assert.BuildOutputContainsLine(result, $"RazorTasksPath: {expected}");
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
public async Task IntrospectRazorTasksDllPath_DesktopMsBuild()
|
||||
{
|
||||
var solutionRoot = GetType().Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
|
||||
.First(a => a.Key == "Testing.RepoRoot")
|
||||
.Value;
|
||||
|
||||
var tfm = "net46";
|
||||
var expected = Path.Combine(solutionRoot, "artifacts", "bin", "Microsoft.NET.Sdk.Razor", Configuration, "sdk-output", "tasks", tfm, "Microsoft.NET.Sdk.Razor.Tasks.dll");
|
||||
|
||||
// Verifies the fix for https://github.com/aspnet/AspNetCore/issues/17308
|
||||
var result = await DotnetMSBuild("_IntrospectRazorTasks", msBuildProcessKind: MSBuildProcessKind.Desktop);
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
Assert.BuildOutputContainsLine(result, $"RazorTasksPath: {expected}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
|
||||
protected string PublishOutputPath => Path.Combine(OutputPath, "publish");
|
||||
|
||||
protected string GetRidSpecificPublishOutputPath(string rid) => Path.Combine(OutputPath, rid, "publish");
|
||||
|
||||
// Used by the test framework to set the project that we're working with
|
||||
internal static ProjectDirectory Project
|
||||
{
|
||||
|
|
|
@ -83,6 +83,31 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileDoesNotExist(result, PublishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
|
||||
public async Task Publish_CopiesStaticWebAssetsToDestinationFolder_PublishSingleFile()
|
||||
{
|
||||
var runtimeIdentifier = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win-x64" : (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx-x64" : "linux-x64");
|
||||
var result = await DotnetMSBuild("Publish", $"/restore /p:PublishSingleFile=true /p:RuntimeIdentifier={runtimeIdentifier}");
|
||||
|
||||
Assert.BuildPassed(result);
|
||||
var publishOutputPath = GetRidSpecificPublishOutputPath(runtimeIdentifier);
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
|
||||
Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
|
||||
|
||||
// Validate that static web assets don't get published as content too on their regular path
|
||||
Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
|
||||
Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.v4.js"));
|
||||
|
||||
// Validate that the manifest never gets copied
|
||||
Assert.FileDoesNotExist(result, publishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
|
||||
public async Task Publish_WithBuildReferencesDisabled_CopiesStaticWebAssetsToDestinationFolder()
|
||||
|
@ -104,6 +129,26 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
|
|||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("AppWithPackageAndP2PReference", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
|
||||
public async Task Publish_NoBuild_CopiesStaticWebAssetsToDestinationFolder()
|
||||
{
|
||||
var build = await DotnetMSBuild("Build", "/restore");
|
||||
|
||||
Assert.BuildPassed(build);
|
||||
|
||||
var publish = await DotnetMSBuild("Publish", "/p:NoBuild=true");
|
||||
|
||||
Assert.BuildPassed(publish);
|
||||
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
|
||||
Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[InitializeTestProject("SimpleMvc")]
|
||||
|
|
|
@ -53,6 +53,11 @@
|
|||
<_Parameter2>$(ProcDumpPath)</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
|
||||
<_Parameter1>Testing.RepoRoot</_Parameter1>
|
||||
<_Parameter2>$(RepoRoot)</_Parameter2>
|
||||
</AssemblyAttribute>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<!-- The test projects rely on these binaries being available -->
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// 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.IO;
|
||||
using Microsoft.Build.Framework;
|
||||
using Microsoft.Build.Utilities;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Tasks
|
||||
{
|
||||
public class ValidateStaticWebAssetsUniquePathsTest
|
||||
{
|
||||
[Fact]
|
||||
public void ReturnsError_WhenStaticWebAssetsWebRootPathMatchesExistingContentItemPath()
|
||||
{
|
||||
// Arrange
|
||||
var errorMessages = new List<string>();
|
||||
var buildEngine = new Mock<IBuildEngine>();
|
||||
buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
|
||||
.Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
|
||||
|
||||
var task = new ValidateStaticWebAssetsUniquePaths
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
StaticWebAssets = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "/",
|
||||
["RelativePath"] = "/sample.js",
|
||||
})
|
||||
},
|
||||
WebRootFiles = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine(".", "App", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["TargetPath"] = "/SAMPLE.js",
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal($"The static web asset '{Path.Combine(".", "Library", "wwwroot", "sample.js")}' has a conflicting web root path '/SAMPLE.js' with the project file '{Path.Combine(".", "App", "wwwroot", "sample.js")}'.", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsError_WhenMultipleStaticWebAssetsHaveTheSameWebRootPath()
|
||||
{
|
||||
// Arrange
|
||||
var errorMessages = new List<string>();
|
||||
var buildEngine = new Mock<IBuildEngine>();
|
||||
buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
|
||||
.Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
|
||||
|
||||
var task = new ValidateStaticWebAssetsUniquePaths
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
StaticWebAssets = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "/",
|
||||
["RelativePath"] = "/sample.js",
|
||||
}),
|
||||
CreateItem(Path.Combine(".", "Library", "bin", "dist", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "/",
|
||||
["RelativePath"] = "/sample.js",
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
var message = Assert.Single(errorMessages);
|
||||
Assert.Equal($"Conflicting assets with the same path '/sample.js' for content root paths '{Path.Combine(".", "Library", "bin", "dist", "sample.js")}' and '{Path.Combine(".", "Library", "wwwroot", "sample.js")}'.", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsSuccess_WhenStaticWebAssetsDontConflictWithApplicationContentItems()
|
||||
{
|
||||
// Arrange
|
||||
var errorMessages = new List<string>();
|
||||
var buildEngine = new Mock<IBuildEngine>();
|
||||
|
||||
var task = new ValidateStaticWebAssetsUniquePaths
|
||||
{
|
||||
BuildEngine = buildEngine.Object,
|
||||
StaticWebAssets = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "/_library",
|
||||
["RelativePath"] = "/sample.js",
|
||||
}),
|
||||
CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["BasePath"] = "/_library",
|
||||
["RelativePath"] = "/sample.js",
|
||||
})
|
||||
},
|
||||
WebRootFiles = new TaskItem[]
|
||||
{
|
||||
CreateItem(Path.Combine(".", "App", "wwwroot", "sample.js"), new Dictionary<string,string>
|
||||
{
|
||||
["TargetPath"] = "/SAMPLE.js",
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = task.Execute();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
private static TaskItem CreateItem(
|
||||
string spec,
|
||||
IDictionary<string, string> metadata)
|
||||
{
|
||||
var result = new TaskItem(spec);
|
||||
foreach (var (key, value) in metadata)
|
||||
{
|
||||
result.SetMetadata(key, value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,4 +46,11 @@
|
|||
<Target Name="_IntrospectContentItems">
|
||||
<Message Text="Content: %(Content.Identity) CopyToOutputDirectory=%(Content.CopyToOutputDirectory) CopyToPublishDirectory=%(Content.CopyToPublishDirectory) ExcludeFromSingleFile=%(Content.ExcludeFromSingleFile)" Importance="High" />
|
||||
</Target>
|
||||
|
||||
<Target Name="_IntrospectRazorTasks">
|
||||
<PropertyGroup>
|
||||
<_SdkTaskPath>$([System.IO.Path]::GetFullPath('$(RazorSdkBuildTasksAssembly)'))</_SdkTaskPath>
|
||||
</PropertyGroup>
|
||||
<Message Text="RazorTasksPath: $(_SdkTaskPath)" Importance="High" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
Загрузка…
Ссылка в новой задаче