Merge branch 'release/3.0'

This commit is contained in:
Justin Kotalik 2019-08-08 14:10:43 -07:00
commit 0193983327
205 changed files with 6912 additions and 1714 deletions

View File

@ -29,7 +29,9 @@ variables:
- ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}:
- name: _BuildArgs
value: ''
jobs:
# Code check
- template: jobs/default-build.yml
parameters:
jobName: Code_check
@ -38,6 +40,10 @@ jobs:
steps:
- powershell: ./eng/scripts/CodeCheck.ps1 -ci
displayName: Run eng/scripts/CodeCheck.ps1
artifacts:
- name: Code_Check_Logs
path: artifacts/log/
publishOnError: true
# Build Windows (x64/x86)
- template: jobs/default-build.yml
@ -171,6 +177,7 @@ jobs:
-bl:artifacts/log/build.macos.binlog
$(_BuildArgs)
installNodeJs: false
installJdk: false
artifacts:
- name: MacOS_x64_Packages
path: artifacts/packages/
@ -191,7 +198,6 @@ jobs:
jobName: Linux_x64_build
jobDisplayName: "Build: Linux x64"
agentOs: Linux
installNodeJs: false
steps:
- script: ./build.sh
--ci
@ -211,6 +217,7 @@ jobs:
--arch x64 \
--build-installers \
--no-build-deps \
--no-build-nodejs \
-p:OnlyPackPlatformSpecificPackages=true \
-p:BuildRuntimeArchive=false \
-p:LinuxInstallerType=deb \
@ -224,12 +231,15 @@ jobs:
--arch x64 \
--build-installers \
--no-build-deps \
--no-build-nodejs \
-p:OnlyPackPlatformSpecificPackages=true \
-p:BuildRuntimeArchive=false \
-p:LinuxInstallerType=rpm \
-bl:artifacts/log/build.rpm.binlog \
$(_BuildArgs)
displayName: Build RPM installers
installNodeJs: false
installJdk: false
artifacts:
- name: Linux_x64_Packages
path: artifacts/packages/
@ -260,6 +270,7 @@ jobs:
-bl:artifacts/log/build.linux-arm.binlog
$(_BuildArgs)
installNodeJs: false
installJdk: false
artifacts:
- name: Linux_arm_Packages
path: artifacts/packages/
@ -290,6 +301,7 @@ jobs:
-bl:artifacts/log/build.arm64.binlog
$(_BuildArgs)
installNodeJs: false
installJdk: false
artifacts:
- name: Linux_arm64_Packages
path: artifacts/packages/
@ -323,6 +335,7 @@ jobs:
-bl:artifacts/log/build.musl.binlog
$(_BuildArgs)
installNodeJs: false
installJdk: false
artifacts:
- name: Linux_musl_x64_Packages
path: artifacts/packages/
@ -337,7 +350,7 @@ jobs:
parameters:
inputName: Linux_musl_x64
# Build Linux Musl arm64
# Build Linux Musl ARM64
- template: jobs/default-build.yml
parameters:
jobName: Linux_musl_arm64_build
@ -356,6 +369,7 @@ jobs:
-bl:artifacts/log/build.musl.binlog
$(_BuildArgs)
installNodeJs: false
installJdk: false
artifacts:
- name: Linux_musl_arm64_Packages
path: artifacts/packages/
@ -499,7 +513,7 @@ jobs:
version: 3.0.x
installationPath: $(DotNetCoreSdkDir)
includePreviewVersions: true
- script: ./eng/scripts/ci-source-build.sh --ci --configuration Release /p:BuildManaged=true
- script: ./eng/scripts/ci-source-build.sh --ci --configuration Release /p:BuildManaged=true /p:BuildNodeJs=false
displayName: Run ci-source-build.sh
- task: PublishBuildArtifacts@1
displayName: Upload logs

View File

@ -1,13 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Keep this file in sync with src/ProjectTemplates/test/Infrastructure/NuGet.config.in. -->
<configuration>
<packageSources>
<clear />
<add key="dotnet-core" value="https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="aspnet-blazor" value="https://dotnetfeed.blob.core.windows.net/aspnet-blazor/index.json" />
<add key="aspnet-extensions" value="https://dotnetfeed.blob.core.windows.net/aspnet-extensions/index.json" />
<add key="aspnet-entityframeworkcore" value="https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json" />
<add key="aspnet-aspnetcore-tooling" value="https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json" />
<add key="grpc-nuget-dev" value="https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev" />
<add key="roslyn" value="https://dotnet.myget.org/F/roslyn/api/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="aspnetcore-dev" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json" />
<add key="aspnetcore-tools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
<add key="roslyn-tools" value="https://dotnet.myget.org/F/roslyn-tools/api/v3/index.json" />
</packageSources>
</configuration>

View File

@ -183,7 +183,7 @@ elseif ($Projects) {
}
# When adding new sub-group build flags, add them to this check.
elseif((-not $BuildNative) -and (-not $BuildManaged) -and (-not $BuildNodeJS) -and (-not $BuildInstallers) -and (-not $BuildJava)) {
Write-Warning "No default group of projects was specified, so building the 'managed' subsets of projects. Run ``build.cmd -help`` for more details."
Write-Warning "No default group of projects was specified, so building the 'managed' and its dependent subsets of projects. Run ``build.cmd -help`` for more details."
# This goal of this is to pick a sensible default for `build.cmd` with zero arguments.
# Now that we support subfolder invokations of build.cmd, we will be pushing to have build.cmd build everything (-all) by default
@ -191,6 +191,25 @@ elseif((-not $BuildNative) -and (-not $BuildManaged) -and (-not $BuildNodeJS) -a
$BuildManaged = $true
}
if ($BuildManaged -or ($All -and (-not $NoBuildManaged))) {
if ((-not $BuildNodeJS) -and (-not $NoBuildNodeJS)) {
$node = Get-Command node -ErrorAction Ignore -CommandType Application
if ($node) {
$nodeHome = Split-Path -Parent (Split-Path -Parent $node.Path)
Write-Host -f Magenta "Building of C# project is enabled and has dependencies on NodeJS projects. Building of NodeJS projects is enabled since node is detected in $nodeHome."
}
else {
Write-Host -f Magenta "Building of NodeJS projects is disabled since node is not detected on Path and no BuildNodeJs or NoBuildNodeJs setting is set explicitly."
$NoBuildNodeJS = $true
}
}
if ($NoBuildNodeJS){
Write-Warning "Some managed projects depend on NodeJS projects. Building NodeJS is disabled so the managed projects will fallback to using the output from previous builds. The output may not be correct or up to date."
}
}
if ($BuildInstallers) { $MSBuildArguments += "/p:BuildInstallers=true" }
if ($BuildManaged) { $MSBuildArguments += "/p:BuildManaged=true" }
if ($BuildNative) { $MSBuildArguments += "/p:BuildNative=true" }

View File

@ -213,7 +213,7 @@ elif [ ! -z "$build_projects" ]; then
elif [ -z "$build_managed" ] && [ -z "$build_nodejs" ] && [ -z "$build_java" ] && [ -z "$build_native" ] && [ -z "$build_installers" ]; then
# This goal of this is to pick a sensible default for `build.sh` with zero arguments.
# We believe the most common thing our contributors will work on is C#, so if no other build group was picked, build the C# projects.
__warn "No default group of projects was specified, so building the 'managed' subset of projects. Run ``build.sh --help`` for more details."
__warn "No default group of projects was specified, so building the 'managed' and its dependent subset of projects. Run ``build.sh --help`` for more details."
build_managed=true
fi
@ -221,6 +221,21 @@ if [ "$build_deps" = false ]; then
msbuild_args[${#msbuild_args[*]}]="-p:BuildProjectReferences=false"
fi
if [ "$build_managed" = true ] || (["$build_all" = true ] && [ "$build_managed" != false ]); then
if [ -z "$build_nodejs" ]; then
if [ -x "$(command -v node)" ]; then
__warn "Building of C# project is enabled and has dependencies on NodeJS projects. Building of NodeJS projects is enabled since node is detected on PATH."
else
__warn "Building of NodeJS projects is disabled since node is not detected on Path and no BuildNodeJs or NoBuildNodeJs setting is set explicitly."
build_nodejs=false
fi
fi
if [ "$build_nodejs" = false ]; then
__warn "Some managed projects depend on NodeJS projects. Building NodeJS is disabled so the managed projects will fallback to using the output from previous builds. The output may not be correct or up to date."
fi
fi
# Only set these MSBuild properties if they were explicitly set by build parameters.
[ ! -z "$build_java" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildJava=$build_java"
[ ! -z "$build_native" ] && msbuild_args[${#msbuild_args[*]}]="-p:BuildNative=$build_native"

View File

@ -97,14 +97,14 @@ The cause of this problem is that the solution you are using does not include th
```
### Common error: Unable to locate the .NET Core SDK
Executing `.\restore.cmd` or `.\build.cmd` may produce these errors:
> error : Unable to locate the .NET Core SDK. Check that it is installed and that the version specified in global.json (if any) matches the installed version.
> error MSB4236: The SDK 'Microsoft.NET.Sdk' specified could not be found.
In most cases, this is because the option _Use previews of the .NET Core SDK_ in VS2019 is not checked. Start Visual Studio, go to _Tools > Options_ and check _Use previews of the .NET Core SDK_ under _Environment > Preview Features_.
## Building with Visual Studio Code
Using Visual Studio Code with this repo requires setting environment variables on command line first.
@ -138,6 +138,8 @@ On macOS/Linux:
./build.sh
```
By default, all of the C# projects are built. Some C# projects requires NodeJS to be installed to compile JavaScript assets which are then checked in as source. If NodeJS is detected on the path, the NodeJS projects will be compiled as part of building C# projects. If NodeJS is not detected on the path, the JavaScript assets checked in previously will be used instead. To disable building NodeJS projects, specify /p:BuildNodeJs=false on the command line.
### Using `dotnet` on command line in this repo
Because we are using pre-release versions of .NET Core, you have to set a handful of environment variables

View File

@ -4,14 +4,16 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(BuildAllProjects)' == 'true' ">
<BuildNative>true</BuildNative>
<BuildManaged>true</BuildManaged>
<BuildNodeJS>true</BuildNodeJS>
<BuildJava>true</BuildJava>
<BuildNative Condition="'$(BuildNative)' == ''">true</BuildNative>
<BuildManaged Condition="'$(BuildManaged)' == ''">true</BuildManaged>
<BuildNodeJS Condition="'$(BuildNodeJS)' == ''">true</BuildNodeJS>
<BuildJava Condition="'$(BuildJava)' == ''">true</BuildJava>
</PropertyGroup>
<!-- These projects are always excluded, even when -projects is specified on command line. -->
<ItemGroup>
<!-- Explicitly excluded projects -->
<ProjectToExclude Include="$(ProjectToExclude)" />
<!-- These projects use 'legacy' csproj, which is not supported by dotnet-msbuild. -->
<ProjectToExclude Include="
@ -31,6 +33,7 @@
$(RepoRoot)src\submodules\**\*.*proj;
$(RepoRoot)src\Installers\**\*.*proj;
$(RepoRoot)src\SignalR\clients\ts\**\node_modules\**\*.*proj;
$(RepoRoot)src\Components\Web.JS\node_modules\**\*.*proj;
$(RepoRoot)src\Components\Blazor\Templates\src\content\**\*.*proj;
$(RepoRoot)src\ProjectTemplates\Web.ProjectTemplates\content\**\*.csproj;
$(RepoRoot)src\ProjectTemplates\Web.ProjectTemplates\content\**\*.fsproj;
@ -43,7 +46,6 @@
$(RepoRoot)src\Servers\Kestrel\perf\PlatformBenchmarks\**\*.csproj;
$(RepoRoot)src\SignalR\perf\benchmarkapps\**\*.csproj;
" />
</ItemGroup>
<Choose>
@ -102,6 +104,7 @@
<ProjectToExclude Condition=" '$(BuildNative)' != 'true'" Include="@(NativeProjects)" />
<NodeJsProjects Include="
$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj;
$(RepoRoot)src\SignalR\**\*.npmproj;
$(RepoRoot)src\Middleware\**\*.npmproj;
"

View File

@ -29,269 +29,269 @@
<Uri>https://github.com/aspnet/AspNetCore-Tooling</Uri>
<Sha>448a88e86d20fd9315901f663318d64c9c6841bf</Sha>
</Dependency>
<Dependency Name="dotnet-ef" Version="3.0.0-preview9.19402.9">
<Dependency Name="dotnet-ef" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore" Version="3.0.0-preview9.19402.9">
<Dependency Name="Microsoft.EntityFrameworkCore" Version="3.0.0-preview9.19405.13">
<Uri>https://github.com/aspnet/EntityFrameworkCore</Uri>
<Sha>49f9f7632c742108e5652f182922cc35c19c9162</Sha>
<Sha>07ed34e80585ca9575ea0265921d42a203193b21</Sha>
</Dependency>
<Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.AspNetCore.Analyzer.Testing" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.AspNetCore.BenchmarkRunner.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.ActivatorUtilities.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.CommandLineUtils.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.KeyPerFile" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Configuration" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.DiagnosticAdapter" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Diagnostics.HealthChecks" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.FileProviders.Embedded" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.HashCodeCombiner.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Hosting" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Http" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Http" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Localization.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Localization" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Localization" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Logging" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.ObjectPool" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Options" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.ParameterDefaultValue.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Primitives" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.Primitives" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.TypeNameHelper.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.ValueStopwatch.Sources" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Extensions.WebEncoders" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.JSInterop" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.JSInterop" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Mono.WebAssembly.Interop" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Mono.WebAssembly.Interop" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Bcl.AsyncInterfaces" Version="1.0.0-preview8.19378.8" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
<Uri>https://github.com/dotnet/corefx</Uri>
@ -412,25 +412,25 @@
<Uri>https://github.com/dotnet/corefx</Uri>
<Sha>80f411d58df8338ccd9430900b541a037a9cb383</Sha>
</Dependency>
<Dependency Name="Internal.AspNetCore.Analyzers" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Internal.AspNetCore.Analyzers" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.GenAPI" Version="1.0.0-beta.19369.2">
<Dependency Name="Microsoft.DotNet.GenAPI" Version="1.0.0-beta.19404.1">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a190d4865fe3c86a168ec49c4fc61c90c96ae051</Sha>
<Sha>b1c2f33f0cef32d1df6e7f388017fd6761d3fcad</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19369.2">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="1.0.0-beta.19404.1">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a190d4865fe3c86a168ec49c4fc61c90c96ae051</Sha>
<Sha>b1c2f33f0cef32d1df6e7f388017fd6761d3fcad</Sha>
</Dependency>
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19369.2">
<Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="2.0.0-beta.19404.1">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>a190d4865fe3c86a168ec49c4fc61c90c96ae051</Sha>
<Sha>b1c2f33f0cef32d1df6e7f388017fd6761d3fcad</Sha>
</Dependency>
<Dependency Name="Microsoft.AspNetCore.Testing" Version="3.0.0-preview9.19401.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Dependency Name="Microsoft.AspNetCore.Testing" Version="3.0.0-preview9.19405.2" CoherentParentDependency="Microsoft.EntityFrameworkCore">
<Uri>https://github.com/aspnet/Extensions</Uri>
<Sha>54d000fda95c2c1f05b13a2e910fc91994da8eb8</Sha>
<Sha>86469ee35cf718e0122f16f52b486303dcfbb1fe</Sha>
</Dependency>
<Dependency Name="Microsoft.Net.Compilers.Toolset" Version="3.3.0-beta3-19401-01" CoherentParentDependency="Microsoft.Extensions.Logging">
<Uri>https://github.com/dotnet/roslyn</Uri>

View File

@ -55,7 +55,7 @@
-->
<PropertyGroup Label="Automated">
<!-- Packages from dotnet/arcade -->
<MicrosoftDotNetGenAPIPackageVersion>1.0.0-beta.19369.2</MicrosoftDotNetGenAPIPackageVersion>
<MicrosoftDotNetGenAPIPackageVersion>1.0.0-beta.19404.1</MicrosoftDotNetGenAPIPackageVersion>
<!-- Packages from dotnet/roslyn -->
<MicrosoftNetCompilersToolsetPackageVersion>3.3.0-beta3-19401-01</MicrosoftNetCompilersToolsetPackageVersion>
<!-- Packages from dotnet/core-setup -->
@ -92,80 +92,87 @@
<!-- Packages from aspnet/Blazor -->
<MicrosoftAspNetCoreBlazorMonoPackageVersion>5.0.0-alpha1.19405.2</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<!-- Packages from aspnet/Extensions -->
<InternalAspNetCoreAnalyzersPackageVersion>3.0.0-preview9.19401.2</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>3.0.0-preview9.19401.2</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-preview9.19401.2</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsCachingSqlServerPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLocalizationAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLocalizationAbstractionsPackageVersion>
<MicrosoftExtensionsLocalizationPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLocalizationPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsObjectPoolPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsObjectPoolPackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-preview9.19401.2</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftInternalExtensionsRefsPackageVersion>3.0.0-preview9.19401.2</MicrosoftInternalExtensionsRefsPackageVersion>
<MicrosoftJSInteropPackageVersion>3.0.0-preview9.19401.2</MicrosoftJSInteropPackageVersion>
<MonoWebAssemblyInteropPackageVersion>3.0.0-preview9.19401.2</MonoWebAssemblyInteropPackageVersion>
<InternalAspNetCoreAnalyzersPackageVersion>3.0.0-preview9.19405.2</InternalAspNetCoreAnalyzersPackageVersion>
<MicrosoftAspNetCoreAnalyzerTestingPackageVersion>3.0.0-preview9.19405.2</MicrosoftAspNetCoreAnalyzerTestingPackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>3.0.0-preview9.19405.2</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsActivatorUtilitiesSourcesPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsCachingSqlServerPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationAzureKeyVaultPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationKeyPerFilePackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsDiagnosticAdapterPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsDiagnosticAdapterPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsDiagnosticsHealthChecksAbstractionsPackageVersion>
<MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsDiagnosticsHealthChecksPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsFileProvidersEmbeddedPackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsHashCodeCombinerSourcesPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsHostFactoryResolverSourcesPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLocalizationAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLocalizationAbstractionsPackageVersion>
<MicrosoftExtensionsLocalizationPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLocalizationPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsObjectPoolPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsObjectPoolPackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsParameterDefaultValueSourcesPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsTypeNameHelperSourcesPackageVersion>
<MicrosoftExtensionsValueStopwatchSourcesPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsValueStopwatchSourcesPackageVersion>
<MicrosoftExtensionsWebEncodersPackageVersion>3.0.0-preview9.19405.2</MicrosoftExtensionsWebEncodersPackageVersion>
<MicrosoftInternalExtensionsRefsPackageVersion>3.0.0-preview9.19405.2</MicrosoftInternalExtensionsRefsPackageVersion>
<MicrosoftJSInteropPackageVersion>3.0.0-preview9.19405.2</MicrosoftJSInteropPackageVersion>
<MonoWebAssemblyInteropPackageVersion>3.0.0-preview9.19405.2</MonoWebAssemblyInteropPackageVersion>
<!-- Packages from aspnet/EntityFrameworkCore -->
<dotnetefPackageVersion>3.0.0-preview9.19402.9</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>3.0.0-preview9.19402.9</MicrosoftEntityFrameworkCorePackageVersion>
<dotnetefPackageVersion>3.0.0-preview9.19405.13</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>3.0.0-preview9.19405.13</MicrosoftEntityFrameworkCorePackageVersion>
<!-- Packages from aspnet/AspNetCore-Tooling -->
<<<<<<< HEAD
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>5.0.0-alpha1.19407.1</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>5.0.0-alpha1.19407.1</MicrosoftAspNetCoreRazorLanguagePackageVersion>
<MicrosoftCodeAnalysisRazorPackageVersion>5.0.0-alpha1.19407.1</MicrosoftCodeAnalysisRazorPackageVersion>
<MicrosoftNETSdkRazorPackageVersion>5.0.0-alpha1.19407.1</MicrosoftNETSdkRazorPackageVersion>
=======
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>3.0.0-preview9.19405.6</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>3.0.0-preview9.19405.6</MicrosoftAspNetCoreRazorLanguagePackageVersion>
<MicrosoftCodeAnalysisRazorPackageVersion>3.0.0-preview9.19405.6</MicrosoftCodeAnalysisRazorPackageVersion>
<MicrosoftNETSdkRazorPackageVersion>3.0.0-preview9.19405.6</MicrosoftNETSdkRazorPackageVersion>
>>>>>>> release/3.0
</PropertyGroup>
<!--
@ -255,25 +262,7 @@
</PropertyGroup>
<!-- Restore feeds -->
<PropertyGroup Label="Restore feeds">
<RestoreSources Condition=" '$(DotNetBuildOffline)' != 'true' ">
$(RestoreSources);
https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json;
https://dotnetfeed.blob.core.windows.net/aspnet-blazor/index.json;
https://dotnetfeed.blob.core.windows.net/aspnet-extensions/index.json;
https://dotnetfeed.blob.core.windows.net/aspnet-entityframeworkcore/index.json;
https://dotnetfeed.blob.core.windows.net/aspnet-aspnetcore-tooling/index.json;
https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev;
</RestoreSources>
<RestoreSources Condition="'$(DotNetBuildOffline)' != 'true' AND $(MicrosoftNetCompilersToolsetPackageVersion.Contains('-')) ">
$(RestoreSources);
https://dotnet.myget.org/F/roslyn/api/v3/index.json;
</RestoreSources>
<!-- TODO remove this once we move Microsoft.Internal.AspNetCore.H2Spec.All to a non-myget feed -->
<RestoreSources>
$(RestoreSources);
https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
</RestoreSources>
<!-- In an orchestrated build, this may be overriden to other Azure feeds. -->
<!-- In an orchestrated build, this may be overridden to other Azure feeds. -->
<DotNetAssetRootUrl Condition="'$(DotNetAssetRootUrl)'==''">https://dotnetcli.blob.core.windows.net/dotnet/</DotNetAssetRootUrl>
</PropertyGroup>
</Project>

View File

@ -8,25 +8,6 @@
<DebugType>portable</DebugType>
</PropertyGroup>
<!--
Workaround for error when running build.cmd from subfolders. It appears RepoRoot is not set correctly.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets(523,5): error : File 'C:\src\aspnet\AspNetCore\src\..NuGet.config' does not exist.
-->
<PropertyGroup>
<RestoreConfigFile>$(RepoRoot)NuGet.config</RestoreConfigFile>
</PropertyGroup>
<!--
Required to restore the RoslynTools.ModifyVsixManifest package.
This isn't auto-detected by Arcade like other feeds are.
-->
<PropertyGroup>
<RestoreSources>
$(RestoreSources);
https://dotnet.myget.org/F/roslyn-tools/api/v3/index.json;
</RestoreSources>
</PropertyGroup>
<!-- Prevent pdb2pdb.exe from running because it is currently causing build failures in Blazor. -->
<PropertyGroup>
<PublishWindowsPdb>false</PublishWindowsPdb>

View File

@ -29,12 +29,6 @@
<ItemGroup>
<PackageReference Include="Internal.AspNetCore.BuildTasks" PrivateAssets="All" Version="$(InternalAspNetCoreBuildTasksPackageVersion)" IsImplicitlyDefined="true" />
</ItemGroup>
<PropertyGroup>
<RestoreSources>
$(RestoreSources);
https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json;
</RestoreSources>
</PropertyGroup>
<!-- Workaround for netstandard2.1 projects until we can get a preview 8 SDK containing https://github.com/dotnet/sdk/pull/3463 fix. -->
<ItemGroup>

View File

@ -98,12 +98,18 @@ try {
}
Write-Verbose "Installing $ToolName version $ToolVersion"
Write-Verbose "Executing '$InstallerPath $LocalInstallerArguments'"
Write-Verbose "Executing '$InstallerPath $($LocalInstallerArguments.Keys.ForEach({"-$_ '$($LocalInstallerArguments.$_)'"}) -join ' ')'"
& $InstallerPath @LocalInstallerArguments
if ($LASTEXITCODE -Ne "0") {
$errMsg = "$ToolName installation failed"
if ((Get-Variable 'DoNotAbortNativeToolsInstallationOnFailure' -ErrorAction 'SilentlyContinue') -and $DoNotAbortNativeToolsInstallationOnFailure) {
Write-Warning $errMsg
$showNativeToolsWarning = $true
if ((Get-Variable 'DoNotDisplayNativeToolsInstallationWarnings' -ErrorAction 'SilentlyContinue') -and $DoNotDisplayNativeToolsInstallationWarnings) {
$showNativeToolsWarning = $false
}
if ($showNativeToolsWarning) {
Write-Warning $errMsg
}
$toolInstallationFailure = $true
} else {
Write-Error $errMsg

View File

@ -70,8 +70,7 @@ function ReadGlobalJsonNativeTools {
# Only extract the contents of the object.
local native_tools_list=$(echo $native_tools_section | awk -F"[{}]" '{print $2}')
native_tools_list=${native_tools_list//[\" ]/}
native_tools_list=${native_tools_list//,/$'\n'}
native_tools_list="$(echo -e "${native_tools_list}" | tr -d '[[:space:]]')"
native_tools_list=$( echo "$native_tools_list" | sed 's/\s//g' | sed 's/,/\n/g' )
local old_IFS=$IFS
while read -r line; do
@ -108,6 +107,7 @@ else
installer_command+=" --baseuri $base_uri"
installer_command+=" --installpath $install_bin"
installer_command+=" --version $tool_version"
echo $installer_command
if [[ $force = true ]]; then
installer_command+=" --force"

0
eng/common/internal-feed-operations.sh Normal file → Executable file
View File

View File

@ -59,9 +59,38 @@ function DownloadAndExtract {
-Verbose:$Verbose
if ($UnzipStatus -Eq $False) {
Write-Error "Unzip failed"
return $False
# Retry Download one more time with Force=true
$DownloadRetryStatus = CommonLibrary\Get-File -Uri $Uri `
-Path $TempToolPath `
-DownloadRetries 1 `
-RetryWaitTimeInSeconds $RetryWaitTimeInSeconds `
-Force:$True `
-Verbose:$Verbose
if ($DownloadRetryStatus -Eq $False) {
Write-Error "Last attempt of download failed as well"
return $False
}
# Retry unzip again one more time with Force=true
$UnzipRetryStatus = CommonLibrary\Expand-Zip -ZipPath $TempToolPath `
-OutputDirectory $InstallDirectory `
-Force:$True `
-Verbose:$Verbose
if ($UnzipRetryStatus -Eq $False)
{
Write-Error "Last attempt of unzip failed as well"
# Clean up partial zips and extracts
if (Test-Path $TempToolPath) {
Remove-Item $TempToolPath -Force
}
if (Test-Path $InstallDirectory) {
Remove-Item $InstallDirectory -Force -Recurse
}
return $False
}
}
return $True
}

View File

@ -0,0 +1,117 @@
#!/usr/bin/env bash
source="${BASH_SOURCE[0]}"
scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
. $scriptroot/common-library.sh
base_uri=
install_path=
version=
clean=false
force=false
download_retries=5
retry_wait_time_seconds=30
while (($# > 0)); do
lowerI="$(echo $1 | awk '{print tolower($0)}')"
case $lowerI in
--baseuri)
base_uri=$2
shift 2
;;
--installpath)
install_path=$2
shift 2
;;
--version)
version=$2
shift 2
;;
--clean)
clean=true
shift 1
;;
--force)
force=true
shift 1
;;
--downloadretries)
download_retries=$2
shift 2
;;
--retrywaittimeseconds)
retry_wait_time_seconds=$2
shift 2
;;
--help)
echo "Common settings:"
echo " --baseuri <value> Base file directory or Url wrom which to acquire tool archives"
echo " --installpath <value> Base directory to install native tool to"
echo " --clean Don't install the tool, just clean up the current install of the tool"
echo " --force Force install of tools even if they previously exist"
echo " --help Print help and exit"
echo ""
echo "Advanced settings:"
echo " --downloadretries Total number of retry attempts"
echo " --retrywaittimeseconds Wait time between retry attempts in seconds"
echo ""
exit 0
;;
esac
done
tool_name="cmake-test"
tool_os=$(GetCurrentOS)
tool_folder=$(echo $tool_os | awk '{print tolower($0)}')
tool_arch="x86_64"
tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch"
tool_install_directory="$install_path/$tool_name/$version"
tool_file_path="$tool_install_directory/$tool_name_moniker/bin/$tool_name"
shim_path="$install_path/$tool_name.sh"
uri="${base_uri}/$tool_folder/$tool_name/$tool_name_moniker.tar.gz"
# Clean up tool and installers
if [[ $clean = true ]]; then
echo "Cleaning $tool_install_directory"
if [[ -d $tool_install_directory ]]; then
rm -rf $tool_install_directory
fi
echo "Cleaning $shim_path"
if [[ -f $shim_path ]]; then
rm -rf $shim_path
fi
tool_temp_path=$(GetTempPathFileName $uri)
echo "Cleaning $tool_temp_path"
if [[ -f $tool_temp_path ]]; then
rm -rf $tool_temp_path
fi
exit 0
fi
# Install tool
if [[ -f $tool_file_path ]] && [[ $force = false ]]; then
echo "$tool_name ($version) already exists, skipping install"
exit 0
fi
DownloadAndExtract $uri $tool_install_directory $force $download_retries $retry_wait_time_seconds
if [[ $? != 0 ]]; then
echo "Installation failed" >&2
exit 1
fi
# Generate Shim
# Always rewrite shims so that we are referencing the expected version
NewScriptShim $shim_path $tool_file_path true
if [[ $? != 0 ]]; then
echo "Shim generation failed" >&2
exit 1
fi
exit 0

View File

@ -69,7 +69,7 @@ tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch"
tool_install_directory="$install_path/$tool_name/$version"
tool_file_path="$tool_install_directory/$tool_name_moniker/bin/$tool_name"
shim_path="$install_path/$tool_name.sh"
uri="${base_uri}/$tool_folder/cmake/$tool_name_moniker.tar.gz"
uri="${base_uri}/$tool_folder/$tool_name/$tool_name_moniker.tar.gz"
# Clean up tool and installers
if [[ $clean = true ]]; then

0
eng/common/performance/performance-setup.sh Normal file → Executable file
View File

0
eng/common/pipeline-logging-functions.sh Normal file → Executable file
View File

View File

@ -0,0 +1,35 @@
param(
[Parameter(Mandatory=$true)][int] $BarBuildId, # ID of the build which assets should be downloaded
[Parameter(Mandatory=$true)][string] $DropLocation, # Where the assets should be downloaded to
[Parameter(Mandatory=$true)][string] $MaestroApiAccessToken, # Token used to access Maestro API
[Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = "https://maestro-prod.westus2.cloudapp.azure.com", # Maestro API URL
[Parameter(Mandatory=$false)][string] $MaestroApiVersion = "2019-01-16" # Version of Maestro API to use
)
. $PSScriptRoot\post-build-utils.ps1
try {
Write-Host "Installing DARC ..."
. $PSScriptRoot\..\darc-init.ps1
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
Write-PipelineTaskError "Something failed while running 'darc-init.ps1'. Check for errors above. Exiting now..."
ExitWithExitCode $exitCode
}
darc gather-drop --non-shipping `
--continue-on-error `
--id $BarBuildId `
--output-dir $DropLocation `
--bar-uri $MaestroApiEndpoint `
--password $MaestroApiAccessToken `
--latest-location
}
catch {
Write-Host $_
Write-Host $_.Exception
Write-Host $_.ScriptStackTrace
ExitWithExitCode 1
}

View File

@ -6,10 +6,7 @@ param(
[Parameter(Mandatory=$true)][string] $ToolDestinationPath # Where the validation tool should be downloaded to
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
. $PSScriptRoot\..\tools.ps1
. $PSScriptRoot\post-build-utils.ps1
try {
$url = "https://raw.githubusercontent.com/NuGet/NuGetGallery/jver-verify/src/VerifyMicrosoftPackage/verify.ps1"

View File

@ -0,0 +1,90 @@
# Most of the functions in this file require the variables `MaestroApiEndPoint`,
# `MaestroApiVersion` and `MaestroApiAccessToken` to be globally available.
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
# `tools.ps1` checks $ci to perform some actions. Since the post-build
# scripts don't necessarily execute in the same agent that run the
# build.ps1/sh script this variable isn't automatically set.
$ci = $true
. $PSScriptRoot\..\tools.ps1
function Create-MaestroApiRequestHeaders([string]$ContentType = "application/json") {
Validate-MaestroVars
$headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]'
$headers.Add('Accept', $ContentType)
$headers.Add('Authorization',"Bearer $MaestroApiAccessToken")
return $headers
}
function Get-MaestroChannel([int]$ChannelId) {
Validate-MaestroVars
$apiHeaders = Create-MaestroApiRequestHeaders
$apiEndpoint = "$MaestroApiEndPoint/api/channels/${ChannelId}?api-version=$MaestroApiVersion"
$result = try { Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" }
return $result
}
function Get-MaestroBuild([int]$BuildId) {
Validate-MaestroVars
$apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken
$apiEndpoint = "$MaestroApiEndPoint/api/builds/${BuildId}?api-version=$MaestroApiVersion"
$result = try { return Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" }
return $result
}
function Get-MaestroSubscriptions([string]$SourceRepository, [int]$ChannelId) {
Validate-MaestroVars
$SourceRepository = [System.Web.HttpUtility]::UrlEncode($SourceRepository)
$apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken
$apiEndpoint = "$MaestroApiEndPoint/api/subscriptions?sourceRepository=$SourceRepository&channelId=$ChannelId&api-version=$MaestroApiVersion"
$result = try { Invoke-WebRequest -Method Get -Uri $apiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" }
return $result
}
function Trigger-Subscription([string]$SubscriptionId) {
Validate-MaestroVars
$apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken
$apiEndpoint = "$MaestroApiEndPoint/api/subscriptions/$SubscriptionId/trigger?api-version=$MaestroApiVersion"
Invoke-WebRequest -Uri $apiEndpoint -Headers $apiHeaders -Method Post | Out-Null
}
function Assign-BuildToChannel([int]$BuildId, [int]$ChannelId) {
Validate-MaestroVars
$apiHeaders = Create-MaestroApiRequestHeaders -AuthToken $MaestroApiAccessToken
$apiEndpoint = "$MaestroApiEndPoint/api/channels/${ChannelId}/builds/${BuildId}?api-version=$MaestroApiVersion"
Invoke-WebRequest -Method Post -Uri $apiEndpoint -Headers $apiHeaders | Out-Null
}
function Validate-MaestroVars {
try {
Get-Variable MaestroApiEndPoint -Scope Global | Out-Null
Get-Variable MaestroApiVersion -Scope Global | Out-Null
Get-Variable MaestroApiAccessToken -Scope Global | Out-Null
if (!($MaestroApiEndPoint -Match "^http[s]?://maestro-(int|prod).westus2.cloudapp.azure.com$")) {
Write-PipelineTaskError "MaestroApiEndPoint is not a valid Maestro URL. '$MaestroApiEndPoint'"
ExitWithExitCode 1
}
if (!($MaestroApiVersion -Match "^[0-9]{4}-[0-9]{2}-[0-9]{2}$")) {
Write-PipelineTaskError "MaestroApiVersion does not match a version string in the format yyyy-MM-DD. '$MaestroApiVersion'"
ExitWithExitCode 1
}
}
catch {
Write-PipelineTaskError "Error: Variables `MaestroApiEndPoint`, `MaestroApiVersion` and `MaestroApiAccessToken` are required while using this script."
Write-Host $_
ExitWithExitCode 1
}
}

View File

@ -1,30 +1,25 @@
param(
[Parameter(Mandatory=$true)][int] $BuildId,
[Parameter(Mandatory=$true)][int] $ChannelId,
[Parameter(Mandatory=$true)][string] $BarToken,
[string] $MaestroEndpoint = "https://maestro-prod.westus2.cloudapp.azure.com",
[string] $ApiVersion = "2019-01-16"
[Parameter(Mandatory=$true)][string] $MaestroApiAccessToken,
[Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = "https://maestro-prod.westus2.cloudapp.azure.com",
[Parameter(Mandatory=$false)][string] $MaestroApiVersion = "2019-01-16"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
. $PSScriptRoot\..\tools.ps1
function Get-Headers([string]$accept, [string]$barToken) {
$headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]'
$headers.Add('Accept',$accept)
$headers.Add('Authorization',"Bearer $barToken")
return $headers
}
. $PSScriptRoot\post-build-utils.ps1
try {
$maestroHeaders = Get-Headers 'application/json' $BarToken
# Check that the channel we are going to promote the build to exist
$channelInfo = Get-MaestroChannel -ChannelId $ChannelId
if (!$channelInfo) {
Write-Host "Channel with BAR ID $ChannelId was not found in BAR!"
ExitWithExitCode 1
}
# Get info about which channels the build has already been promoted to
$getBuildApiEndpoint = "$MaestroEndpoint/api/builds/${BuildId}?api-version=$ApiVersion"
$buildInfo = Invoke-WebRequest -Method Get -Uri $getBuildApiEndpoint -Headers $maestroHeaders | ConvertFrom-Json
$buildInfo = Get-MaestroBuild -BuildId $BuildId
if (!$buildInfo) {
Write-Host "Build with BAR ID $BuildId was not found in BAR!"
ExitWithExitCode 1
@ -40,10 +35,10 @@ try {
}
}
Write-Host "Build not present in channel $ChannelId. Promoting build ... "
Write-Host "Promoting build '$BuildId' to channel '$ChannelId'."
Assign-BuildToChannel -BuildId $BuildId -ChannelId $ChannelId
$promoteBuildApiEndpoint = "$maestroEndpoint/api/channels/${ChannelId}/builds/${BuildId}?api-version=$ApiVersion"
Invoke-WebRequest -Method Post -Uri $promoteBuildApiEndpoint -Headers $maestroHeaders
Write-Host "done."
}
catch {

View File

@ -0,0 +1,26 @@
param(
[Parameter(Mandatory=$true)][string] $ReleaseConfigsPath # Full path to ReleaseConfigs.txt asset
)
. $PSScriptRoot\post-build-utils.ps1
try {
$Content = Get-Content $ReleaseConfigsPath
$BarId = $Content | Select -Index 0
$Channels = ""
$Content | Select -Index 1 | ForEach-Object { $Channels += "$_ ," }
$IsStableBuild = $Content | Select -Index 2
Write-PipelineSetVariable -Name 'BARBuildId' -Value $BarId
Write-PipelineSetVariable -Name 'InitialChannels' -Value "$Channels"
Write-PipelineSetVariable -Name 'IsStableBuild' -Value $IsStableBuild
}
catch {
Write-Host $_
Write-Host $_.Exception
Write-Host $_.ScriptStackTrace
ExitWithExitCode 1
}

View File

@ -6,10 +6,7 @@ param(
[Parameter(Mandatory=$true)][string] $SourcelinkCliVersion # Version of SourceLink CLI to use
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
. $PSScriptRoot\..\tools.ps1
. $PSScriptRoot\post-build-utils.ps1
# Cache/HashMap (File -> Exist flag) used to consult whether a file exist
# in the repository at a specific commit point. This is populated by inserting
@ -200,21 +197,27 @@ function ValidateSourceLinkLinks {
}
}
function CheckExitCode ([string]$stage) {
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
Write-PipelineTaskError "Something failed while '$stage'. Check for errors above. Exiting now..."
ExitWithExitCode $exitCode
function InstallSourcelinkCli {
$sourcelinkCliPackageName = "sourcelink"
$dotnetRoot = InitializeDotNetCli -install:$true
$dotnet = "$dotnetRoot\dotnet.exe"
$toolList = & "$dotnet" tool list --global
if (($toolList -like "*$sourcelinkCliPackageName*") -and ($toolList -like "*$sourcelinkCliVersion*")) {
Write-Host "SourceLink CLI version $sourcelinkCliVersion is already installed."
}
else {
Write-Host "Installing SourceLink CLI version $sourcelinkCliVersion..."
Write-Host "You may need to restart your command window if this is the first dotnet tool you have installed."
& "$dotnet" tool install $sourcelinkCliPackageName --version $sourcelinkCliVersion --verbosity "minimal" --global
}
}
try {
Write-Host "Installing SourceLink CLI..."
Get-Location
. $PSScriptRoot\sourcelink-cli-init.ps1 -sourcelinkCliVersion $SourcelinkCliVersion
CheckExitCode "Running sourcelink-cli-init"
InstallSourcelinkCli
Measure-Command { ValidateSourceLinkLinks }
ValidateSourceLinkLinks
}
catch {
Write-Host $_

View File

@ -4,10 +4,7 @@ param(
[Parameter(Mandatory=$true)][string] $DotnetSymbolVersion # Version of dotnet symbol to use
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
. $PSScriptRoot\..\tools.ps1
. $PSScriptRoot\post-build-utils.ps1
Add-Type -AssemblyName System.IO.Compression.FileSystem
@ -162,19 +159,25 @@ function CheckSymbolsAvailable {
}
}
function CheckExitCode ([string]$stage) {
$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
Write-PipelineTaskError "Something failed while '$stage'. Check for errors above. Exiting now..."
ExitWithExitCode $exitCode
function Installdotnetsymbol {
$dotnetsymbolPackageName = "dotnet-symbol"
$dotnetRoot = InitializeDotNetCli -install:$true
$dotnet = "$dotnetRoot\dotnet.exe"
$toolList = & "$dotnet" tool list --global
if (($toolList -like "*$dotnetsymbolPackageName*") -and ($toolList -like "*$dotnetsymbolVersion*")) {
Write-Host "dotnet-symbol version $dotnetsymbolVersion is already installed."
}
else {
Write-Host "Installing dotnet-symbol version $dotnetsymbolVersion..."
Write-Host "You may need to restart your command window if this is the first dotnet tool you have installed."
& "$dotnet" tool install $dotnetsymbolPackageName --version $dotnetsymbolVersion --verbosity "minimal" --global
}
}
try {
Write-Host "Installing dotnet symbol ..."
Get-Location
. $PSScriptRoot\dotnetsymbol-init.ps1 -dotnetsymbolVersion $DotnetSymbolVersion
CheckExitCode "Running dotnetsymbol-init"
Installdotnetsymbol
CheckSymbolsAvailable
}

View File

@ -1,33 +1,20 @@
param(
param(
[Parameter(Mandatory=$true)][string] $SourceRepo,
[Parameter(Mandatory=$true)][int] $ChannelId,
[string] $MaestroEndpoint = "https://maestro-prod.westus2.cloudapp.azure.com",
[string] $BarToken,
[string] $ApiVersion = "2019-01-16"
[Parameter(Mandatory=$true)][string] $MaestroApiAccessToken,
[Parameter(Mandatory=$false)][string] $MaestroApiEndPoint = "https://maestro-prod.westus2.cloudapp.azure.com",
[Parameter(Mandatory=$false)][string] $MaestroApiVersion = "2019-01-16"
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version 2.0
. $PSScriptRoot\..\tools.ps1
function Get-Headers([string]$accept, [string]$barToken) {
$headers = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]'
$headers.Add('Accept',$accept)
$headers.Add('Authorization',"Bearer $barToken")
return $headers
}
. $PSScriptRoot\post-build-utils.ps1
# Get all the $SourceRepo subscriptions
$normalizedSourceRepo = $SourceRepo.Replace('dnceng@', '')
$getSubscriptionsApiEndpoint = "$maestroEndpoint/api/subscriptions?sourceRepository=$normalizedSourceRepo&api-version=$apiVersion"
$headers = Get-Headers 'application/json' $barToken
$subscriptions = Invoke-WebRequest -Uri $getSubscriptionsApiEndpoint -Headers $headers | ConvertFrom-Json
$subscriptions = Get-MaestroSubscriptions -SourceRepository $normalizedSourceRepo -ChannelId $ChannelId
if (!$subscriptions) {
Write-Host "No subscriptions found for source repo '$normalizedSourceRepo' in channel '$ChannelId'"
return
ExitWithExitCode 0
}
$subscriptionsToTrigger = New-Object System.Collections.Generic.List[string]
@ -36,21 +23,18 @@ $failedTriggeredSubscription = $false
# Get all enabled subscriptions that need dependency flow on 'everyBuild'
foreach ($subscription in $subscriptions) {
if ($subscription.enabled -and $subscription.policy.updateFrequency -like 'everyBuild' -and $subscription.channel.id -eq $ChannelId) {
Write-Host "$subscription.id"
Write-Host "Should trigger this subscription: $subscription.id"
[void]$subscriptionsToTrigger.Add($subscription.id)
}
}
foreach ($subscriptionToTrigger in $subscriptionsToTrigger) {
try {
$triggerSubscriptionApiEndpoint = "$maestroEndpoint/api/subscriptions/$subscriptionToTrigger/trigger?api-version=$apiVersion"
$headers = Get-Headers 'application/json' $BarToken
Write-Host "Triggering subscription '$subscriptionToTrigger'..."
Write-Host "Triggering subscription '$subscriptionToTrigger'."
Invoke-WebRequest -Uri $triggerSubscriptionApiEndpoint -Headers $headers -Method Post
Trigger-Subscription -SubscriptionId $subscriptionToTrigger
Write-Host "Subscription '$subscriptionToTrigger' triggered!"
Write-Host "done."
}
catch
{
@ -61,9 +45,13 @@ foreach ($subscriptionToTrigger in $subscriptionsToTrigger) {
}
}
if ($failedTriggeredSubscription) {
if ($subscriptionsToTrigger.Count -eq 0) {
Write-Host "No subscription matched source repo '$normalizedSourceRepo' and channel ID '$ChannelId'."
}
elseif ($failedTriggeredSubscription) {
Write-Host "At least one subscription failed to be triggered..."
ExitWithExitCode 1
}
Write-Host "All subscriptions were triggered successfully!"
else {
Write-Host "All subscriptions were triggered successfully!"
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Guardian.Cli" version="0.6.0"/>
<package id="Microsoft.Guardian.Cli" version="0.7.1"/>
</packages>

View File

@ -46,7 +46,7 @@ jobs:
continueOnError: ${{ parameters.continueOnError }}
- ${{ if eq(parameters.overrideParameters, '') }}:
- powershell: eng/common/sdl/execute-all-sdl-tools.ps1
-GuardianPackageName Microsoft.Guardian.Cli.0.6.0
-GuardianPackageName Microsoft.Guardian.Cli.0.7.1
-NugetPackageDirectory $(Build.SourcesDirectory)\.packages
-AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw)
${{ parameters.additionalParameters }}

View File

@ -37,6 +37,9 @@ parameters:
# Optional: Enable publishing to the build asset registry
enablePublishBuildAssets: false
# Optional: Prevent gather/push manifest from executing when using publishing pipelines
enablePublishUsingPipelines: false
# Optional: Include PublishTestResults task
enablePublishTestResults: false
@ -187,7 +190,7 @@ jobs:
continueOnError: true
condition: always()
- ${{ if and(eq(parameters.enablePublishBuildAssets, true), ne(variables['_PublishUsingPipelines'], 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- ${{ if and(eq(parameters.enablePublishBuildAssets, true), ne(parameters.enablePublishUsingPipelines, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- task: CopyFiles@2
displayName: Gather Asset Manifests
inputs:
@ -195,6 +198,7 @@ jobs:
TargetFolder: '$(Build.StagingDirectory)/AssetManifests'
continueOnError: ${{ parameters.continueOnError }}
condition: and(succeeded(), eq(variables['_DotNetPublishToBlobFeed'], 'true'))
- task: PublishBuildArtifacts@1
displayName: Push Asset Manifests
inputs:

View File

@ -13,7 +13,7 @@ stages:
- job:
displayName: Symbol Publishing
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.InternalServicing_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.InternalServicing_30_Channel_Id))
variables:
- group: DotNet-Symbol-Server-Pats
pool:
@ -41,13 +41,12 @@ stages:
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.InternalServicing_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.InternalServicing_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -87,8 +86,8 @@ stages:
/p:StaticInternalFeed=$(dotnetfeed-internal-nonstable-feed-url)
/p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
/p:BuildAssetRegistryToken='$(MaestroAccessToken)'
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)\BlobArtifacts'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)\PackageArtifacts'
@ -127,7 +126,7 @@ stages:
- job:
displayName: Symbol Availability
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.InternalServicing_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.InternalServicing_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -143,29 +142,6 @@ stages:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
- job:
displayName: Gather Drop
dependsOn: setupMaestroVars
variables:
BARBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.InternalServicing_30_Channel_Id)
pool:
vmImage: 'windows-2019'
steps:
- task: PowerShell@2
displayName: Setup Darc CLI
inputs:
targetType: filePath
filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
- task: PowerShell@2
displayName: Run Darc gather-drop
inputs:
targetType: inline
script: |
darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com/ --password $(MaestroAccessToken) --latest-location
enabled: false
- template: ../promote-build.yml
parameters:
ChannelId: ${{ variables.InternalServicing_30_Channel_Id }}

View File

@ -0,0 +1,148 @@
parameters:
enableSymbolValidation: true
stages:
- stage: NetCore_Dev5_Publish
dependsOn: validate
variables:
- template: ../common-variables.yml
displayName: .NET Core 5 Dev Channel
jobs:
- template: ../setup-maestro-vars.yml
- job:
displayName: Symbol Publishing
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_5_Dev_Channel_Id))
variables:
- group: DotNet-Symbol-Server-Pats
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Artifacts
inputs:
downloadType: specific files
matchingPattern: "*Artifacts*"
- task: PowerShell@2
displayName: Publish
inputs:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishToSymbolServers -restore -msbuildEngine dotnet
/p:DotNetSymbolServerTokenMsdl=$(microsoft-symbol-server-pat)
/p:DotNetSymbolServerTokenSymWeb=$(symweb-symbol-server-pat)
/p:PDBArtifactsDirectory='$(Build.ArtifactStagingDirectory)/PDBArtifacts/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
/p:Configuration=Release
- job:
displayName: Publish Assets
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_5_Dev_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Package Artifacts
inputs:
buildType: current
artifactName: PackageArtifacts
- task: DownloadBuildArtifacts@0
displayName: Download Blob Artifacts
inputs:
buildType: current
artifactName: BlobArtifacts
- task: DownloadBuildArtifacts@0
displayName: Download Asset Manifests
inputs:
buildType: current
artifactName: AssetManifests
- task: PowerShell@2
displayName: Add Assets Location
env:
AZURE_DEVOPS_EXT_PAT: $(dn-bot-dnceng-unviersal-packages-rw)
inputs:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet
/p:ChannelId=$(NetCore_5_Dev_Channel_Id)
/p:ArtifactsCategory=$(_DotNetArtifactsCategory)
/p:IsStableBuild=$(IsStableBuild)
/p:IsInternalBuild=$(IsInternalBuild)
/p:RepositoryName=$(Build.Repository.Name)
/p:CommitSha=$(Build.SourceVersion)
/p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe
/p:AzdoTargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)'
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts/'
/p:Configuration=Release
- task: NuGetCommand@2
displayName: Publish Packages to AzDO Feed
condition: contains(variables['TargetAzDOFeed'], 'pkgs.visualstudio.com')
inputs:
command: push
vstsFeed: $(AzDoFeedName)
packagesToPush: $(Build.ArtifactStagingDirectory)\PackageArtifacts\*.nupkg
publishVstsFeed: $(AzDoFeedName)
- task: PowerShell@2
displayName: Publish Blobs to AzDO Feed
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-blobs-to-azdo.ps1
arguments: -FeedName $(AzDoFeedName)
-SourceFolderCollection $(Build.ArtifactStagingDirectory)/BlobArtifacts/
-PersonalAccessToken $(dn-bot-dnceng-unviersal-packages-rw)
enabled: false
- stage: NetCore_Dev5_PublishValidation
displayName: Publish Validation
variables:
- template: ../common-variables.yml
jobs:
- template: ../setup-maestro-vars.yml
- ${{ if eq(parameters.enableSymbolValidation, 'true') }}:
- job:
displayName: Symbol Availability
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_5_Dev_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Package Artifacts
inputs:
buildType: current
artifactName: PackageArtifacts
- task: PowerShell@2
displayName: Check Symbol Availability
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
- template: ../darc-gather-drop.yml
parameters:
ChannelId: ${{ variables.NetCore_5_Dev_Channel_Id }}
- template: ../promote-build.yml
parameters:
ChannelId: ${{ variables.NetCore_5_Dev_Channel_Id }}

View File

@ -0,0 +1,148 @@
parameters:
enableSymbolValidation: true
stages:
- stage: NetCore_Tools_Latest_Publish
dependsOn: validate
variables:
- template: ../common-variables.yml
displayName: .NET Tools - Latest
jobs:
- template: ../setup-maestro-vars.yml
- job:
displayName: Symbol Publishing
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_Tools_Latest_Channel_Id))
variables:
- group: DotNet-Symbol-Server-Pats
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Artifacts
inputs:
downloadType: specific files
matchingPattern: "*Artifacts*"
- task: PowerShell@2
displayName: Publish
inputs:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishToSymbolServers -restore -msbuildEngine dotnet
/p:DotNetSymbolServerTokenMsdl=$(microsoft-symbol-server-pat)
/p:DotNetSymbolServerTokenSymWeb=$(symweb-symbol-server-pat)
/p:PDBArtifactsDirectory='$(Build.ArtifactStagingDirectory)/PDBArtifacts/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
/p:Configuration=Release
- job:
displayName: Publish Assets
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_Tools_Latest_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Package Artifacts
inputs:
buildType: current
artifactName: PackageArtifacts
- task: DownloadBuildArtifacts@0
displayName: Download Blob Artifacts
inputs:
buildType: current
artifactName: BlobArtifacts
- task: DownloadBuildArtifacts@0
displayName: Download Asset Manifests
inputs:
buildType: current
artifactName: AssetManifests
- task: PowerShell@2
displayName: Add Assets Location
env:
AZURE_DEVOPS_EXT_PAT: $(dn-bot-dnceng-unviersal-packages-rw)
inputs:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet
/p:ChannelId=$(NetCore_Tools_Latest_Channel_Id)
/p:ArtifactsCategory=$(_DotNetArtifactsCategory)
/p:IsStableBuild=$(IsStableBuild)
/p:IsInternalBuild=$(IsInternalBuild)
/p:RepositoryName=$(Build.Repository.Name)
/p:CommitSha=$(Build.SourceVersion)
/p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe
/p:AzdoTargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)'
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts/'
/p:Configuration=Release
- task: NuGetCommand@2
displayName: Publish Packages to AzDO Feed
condition: contains(variables['TargetAzDOFeed'], 'pkgs.visualstudio.com')
inputs:
command: push
vstsFeed: $(AzDoFeedName)
packagesToPush: $(Build.ArtifactStagingDirectory)\PackageArtifacts\*.nupkg
publishVstsFeed: $(AzDoFeedName)
- task: PowerShell@2
displayName: Publish Blobs to AzDO Feed
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-blobs-to-azdo.ps1
arguments: -FeedName $(AzDoFeedName)
-SourceFolderCollection $(Build.ArtifactStagingDirectory)/BlobArtifacts/
-PersonalAccessToken $(dn-bot-dnceng-unviersal-packages-rw)
enabled: false
- stage: NetCore_Tools_Latest_PublishValidation
displayName: Publish Validation
variables:
- template: ../common-variables.yml
jobs:
- template: ../setup-maestro-vars.yml
- ${{ if eq(parameters.enableSymbolValidation, 'true') }}:
- job:
displayName: Symbol Availability
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.NetCore_Tools_Latest_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
- task: DownloadBuildArtifacts@0
displayName: Download Package Artifacts
inputs:
buildType: current
artifactName: PackageArtifacts
- task: PowerShell@2
displayName: Check Symbol Availability
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
- template: ../darc-gather-drop.yml
parameters:
ChannelId: ${{ variables.NetCore_Tools_Latest_Channel_Id }}
- template: ../promote-build.yml
parameters:
ChannelId: ${{ variables.NetCore_Tools_Latest_Channel_Id }}

View File

@ -13,7 +13,7 @@ stages:
- job:
displayName: Symbol Publishing
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicDevRelease_30_Channel_Id))
variables:
- group: DotNet-Symbol-Server-Pats
pool:
@ -41,13 +41,12 @@ stages:
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicDevRelease_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -77,7 +76,7 @@ stages:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet
/p:ChannelId=$(PublicDevRelease_30_Channel_Id)
/p:ArtifactsCategory=.NetCore
/p:ArtifactsCategory=$(_DotNetArtifactsCategory)
/p:IsStableBuild=$(IsStableBuild)
/p:IsInternalBuild=$(IsInternalBuild)
/p:RepositoryName=$(Build.Repository.Name)
@ -87,8 +86,8 @@ stages:
/p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)'
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
/p:BuildAssetRegistryToken='$(MaestroAccessToken)'
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)/BlobArtifacts/'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts/'
@ -124,7 +123,7 @@ stages:
- job:
displayName: Symbol Availability
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicDevRelease_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -140,27 +139,9 @@ stages:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
- job:
displayName: Gather Drop
dependsOn: setupMaestroVars
variables:
BARBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicDevRelease_30_Channel_Id)
pool:
vmImage: 'windows-2019'
steps:
- task: PowerShell@2
displayName: Setup Darc CLI
inputs:
targetType: filePath
filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
- task: PowerShell@2
displayName: Run Darc gather-drop
inputs:
targetType: inline
script: |
darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com/ --password $(MaestroAccessToken) --latest-location
- template: ../darc-gather-drop.yml
parameters:
ChannelId: ${{ variables.PublicDevRelease_30_Channel_Id }}
- template: ../promote-build.yml
parameters:

View File

@ -13,7 +13,7 @@ stages:
- job:
displayName: Symbol Publishing
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicRelease_30_Channel_Id))
variables:
- group: DotNet-Symbol-Server-Pats
pool:
@ -41,13 +41,12 @@ stages:
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicRelease_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -87,8 +86,8 @@ stages:
/p:StaticInternalFeed=$(dotnetfeed-internal-nonstable-feed-url)
/p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
/p:BuildAssetRegistryToken='$(MaestroAccessToken)'
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)\BlobArtifacts'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)\PackageArtifacts'
@ -127,7 +126,7 @@ stages:
- job:
displayName: Symbol Availability
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicRelease_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -143,29 +142,6 @@ stages:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/symbols-validation.ps1
arguments: -InputPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/ -ExtractPath $(Agent.BuildDirectory)/Temp/ -DotnetSymbolVersion $(SymbolToolVersion)
- job:
displayName: Gather Drop
dependsOn: setupMaestroVars
variables:
BARBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicRelease_30_Channel_Id)
pool:
vmImage: 'windows-2019'
steps:
- task: PowerShell@2
displayName: Setup Darc CLI
inputs:
targetType: filePath
filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
- task: PowerShell@2
displayName: Run Darc gather-drop
inputs:
targetType: inline
script: |
darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com/ --password $(MaestroAccessToken) --latest-location
enabled: false
- template: ../promote-build.yml
parameters:
ChannelId: ${{ variables.PublicRelease_30_Channel_Id }}

View File

@ -12,13 +12,12 @@ stages:
dependsOn: setupMaestroVars
variables:
- group: DotNet-Blob-Feed
- group: Publish-Build-Assets
- group: AzureDevOps-Artifact-Feeds-Pats
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: IsStableBuild
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.IsStableBuild'] ]
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicValidationRelease_30_Channel_Id)
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', variables.PublicValidationRelease_30_Channel_Id))
pool:
vmImage: 'windows-2019'
steps:
@ -48,7 +47,7 @@ stages:
filePath: eng\common\sdk-task.ps1
arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet
/p:ChannelId=$(PublicValidationRelease_30_Channel_Id)
/p:ArtifactsCategory=.NetCoreValidation
/p:ArtifactsCategory=$(_DotNetValidationArtifactsCategory)
/p:IsStableBuild=$(IsStableBuild)
/p:IsInternalBuild=$(IsInternalBuild)
/p:RepositoryName=$(Build.Repository.Name)
@ -58,13 +57,13 @@ stages:
/p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)'
/p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)'
/p:BARBuildId=$(BARBuildId)
/p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com'
/p:BuildAssetRegistryToken='$(MaestroAccessToken)'
/p:MaestroApiEndpoint='$(MaestroApiEndPoint)'
/p:BuildAssetRegistryToken='$(MaestroApiAccessToken)'
/p:ManifestsBasePath='$(Build.ArtifactStagingDirectory)/AssetManifests/'
/p:BlobBasePath='$(Build.ArtifactStagingDirectory)\BlobArtifacts'
/p:PackageBasePath='$(Build.ArtifactStagingDirectory)\PackageArtifacts'
/p:Configuration=Release
- task: NuGetCommand@2
displayName: Publish Packages to AzDO Feed
condition: contains(variables['TargetAzDOFeed'], 'pkgs.visualstudio.com')
@ -91,29 +90,9 @@ stages:
jobs:
- template: ../setup-maestro-vars.yml
- job:
displayName: Gather Drop
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], variables.PublicValidationRelease_30_Channel_Id)
variables:
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- group: Publish-Build-Assets
pool:
vmImage: 'windows-2019'
steps:
- task: PowerShell@2
displayName: Setup Darc CLI
inputs:
targetType: filePath
filePath: '$(Build.SourcesDirectory)/eng/common/darc-init.ps1'
- task: PowerShell@2
displayName: Run Darc gather-drop
inputs:
targetType: inline
script: |
darc gather-drop --non-shipping --continue-on-error --id $(BARBuildId) --output-dir $(Agent.BuildDirectory)/Temp/Drop/ --bar-uri https://maestro-prod.westus2.cloudapp.azure.com --password $(MaestroAccessToken) --latest-location
- template: ../darc-gather-drop.yml
parameters:
ChannelId: ${{ variables.PublicValidationRelease_30_Channel_Id }}
- template: ../promote-build.yml
parameters:

View File

@ -1,21 +1,47 @@
variables:
- group: Publish-Build-Assets
# .NET Core 3 Dev
PublicDevRelease_30_Channel_Id: 3
- name: PublicDevRelease_30_Channel_Id
value: 3
# .NET Core 5 Dev
- name: NetCore_5_Dev_Channel_Id
value: 131
# .NET Tools - Validation
PublicValidationRelease_30_Channel_Id: 9
- name: PublicValidationRelease_30_Channel_Id
value: 9
# .NET Tools - Latest
- name: NetCore_Tools_Latest_Channel_Id
value: 2
# .NET Core 3.0 Internal Servicing
InternalServicing_30_Channel_Id: 184
- name: InternalServicing_30_Channel_Id
value: 184
# .NET Core 3.0 Release
PublicRelease_30_Channel_Id: 19
- name: PublicRelease_30_Channel_Id
value: 19
# Whether the build is internal or not
IsInternalBuild: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }}
- name: IsInternalBuild
value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }}
# Storage account name for proxy-backed feeds
ProxyBackedFeedsAccountName: dotnetfeed
- name: ProxyBackedFeedsAccountName
value: dotnetfeed
SourceLinkCLIVersion: 3.0.0
SymbolToolVersion: 1.0.1
# Default Maestro++ API Endpoint and API Version
- name: MaestroApiEndPoint
value: "https://maestro-prod.westus2.cloudapp.azure.com"
- name: MaestroApiAccessToken
value: $(MaestroAccessToken)
- name: MaestroApiVersion
value: "2019-01-16"
- name: SourceLinkCLIVersion
value: 3.0.0
- name: SymbolToolVersion
value: 1.0.1

View File

@ -0,0 +1,23 @@
parameters:
ChannelId: 0
jobs:
- job: gatherDrop
displayName: Gather Drop
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', ${{ parameters.ChannelId }}))
variables:
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
pool:
vmImage: 'windows-2019'
steps:
- task: PowerShell@2
displayName: Darc gather-drop
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/darc-gather-drop.ps1
arguments: -BarBuildId $(BARBuildId)
-DropLocation $(Agent.BuildDirectory)/Temp/Drop/
-MaestroApiAccessToken $(MaestroApiAccessToken)
-MaestroApiEndPoint $(MaestroApiEndPoint)
-MaestroApiVersion $(MaestroApiVersion)

View File

@ -7,9 +7,12 @@ parameters:
enable: false
params: ''
# Which stages should finish execution before post-build stages start
dependsOn: [build]
stages:
- stage: validate
dependsOn: build
dependsOn: ${{ parameters.dependsOn }}
displayName: Validate
jobs:
- ${{ if eq(parameters.enableNugetValidation, 'true') }}:
@ -80,10 +83,18 @@ stages:
parameters:
additionalParameters: ${{ parameters.SDLValidationParameters.params }}
- template: \eng\common\templates\post-build\channels\netcore-dev-5.yml
parameters:
enableSymbolValidation: ${{ parameters.enableSymbolValidation }}
- template: \eng\common\templates\post-build\channels\public-dev-release.yml
parameters:
enableSymbolValidation: ${{ parameters.enableSymbolValidation }}
- template: \eng\common\templates\post-build\channels\netcore-tools-latest.yml
parameters:
enableSymbolValidation: ${{ parameters.enableSymbolValidation }}
- template: \eng\common\templates\post-build\channels\public-validation-release.yml
- template: \eng\common\templates\post-build\channels\public-release.yml

View File

@ -5,13 +5,12 @@ jobs:
- job:
displayName: Promote Build
dependsOn: setupMaestroVars
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], ${{ parameters.ChannelId }})
condition: contains(dependencies.setupMaestroVars.outputs['setReleaseVars.InitialChannels'], format('[{0}]', ${{ parameters.ChannelId }}))
variables:
- name: BARBuildId
value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ]
- name: ChannelId
value: ${{ parameters.ChannelId }}
- group: Publish-Build-Assets
pool:
vmImage: 'windows-2019'
steps:
@ -21,4 +20,6 @@ jobs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/promote-build.ps1
arguments: -BuildId $(BARBuildId)
-ChannelId $(ChannelId)
-BarToken $(MaestroAccessToken)
-MaestroApiAccessToken $(MaestroApiAccessToken)
-MaestroApiEndPoint $(MaestroApiEndPoint)
-MaestroApiVersion $(MaestroApiVersion)

View File

@ -14,22 +14,5 @@ jobs:
name: setReleaseVars
displayName: Set Release Configs Vars
inputs:
targetType: inline
script: |
# This is needed to make Write-PipelineSetVariable works in this context
$ci = $true
. "$(Build.SourcesDirectory)/eng/common/tools.ps1"
$Content = Get-Content "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt"
$BarId = $Content | Select -Index 0
$Channels = ""
$Content | Select -Index 1 | ForEach-Object { $Channels += "$_ ," }
$IsStableBuild = $Content | Select -Index 2
Write-PipelineSetVariable -Name 'BARBuildId' -Value $BarId
Write-PipelineSetVariable -Name 'InitialChannels' -Value "$Channels"
Write-PipelineSetVariable -Name 'IsStableBuild' -Value $IsStableBuild
filePath: $(Build.SourcesDirectory)/eng/common/post-build/setup-maestro-vars.ps1
arguments: -ReleaseConfigsPath '$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt'

View File

@ -8,4 +8,6 @@ steps:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1
arguments: -SourceRepo $(Build.Repository.Uri)
-ChannelId ${{ parameters.ChannelId }}
-BarToken $(MaestroAccessTokenInt)
-MaestroApiAccessToken $(MaestroAccessToken)
-MaestroApiEndPoint $(MaestroApiEndPoint)
-MaestroApiVersion $(MaestroApiVersion)

View File

@ -39,6 +39,10 @@
# installed on the machine instead of downloading one.
[bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true }
# Enable repos to use a particular version of the on-line dotnet-install scripts.
# default URL: https://dot.net/v1/dotnet-install.ps1
[string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { "v1" }
# True to use global NuGet cache instead of restoring packages to repository-local directory.
[bool]$useGlobalNuGetCache = if (Test-Path variable:useGlobalNuGetCache) { $useGlobalNuGetCache } else { !$ci }
@ -159,7 +163,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) {
$installScript = Join-Path $dotnetRoot "dotnet-install.ps1"
if (!(Test-Path $installScript)) {
Create-Directory $dotnetRoot
Invoke-WebRequest "https://dot.net/v1/dotnet-install.ps1" -OutFile $installScript
Invoke-WebRequest "https://dot.net/$dotnetInstallScriptVersion/dotnet-install.ps1" -OutFile $installScript
}
return $installScript
@ -518,6 +522,9 @@ function MSBuild-Core() {
if ($warnAsError) {
$cmdArgs += " /warnaserror /p:TreatWarningsAsErrors=true"
}
else {
$cmdArgs += " /p:TreatWarningsAsErrors=false"
}
foreach ($arg in $args) {
if ($arg -ne $null -and $arg.Trim() -ne "") {

View File

@ -45,6 +45,10 @@ warn_as_error=${warn_as_error:-true}
# installed on the machine instead of downloading one.
use_installed_dotnet_cli=${use_installed_dotnet_cli:-true}
# Enable repos to use a particular version of the on-line dotnet-install scripts.
# default URL: https://dot.net/v1/dotnet-install.sh
dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'}
# True to use global NuGet cache instead of restoring packages to repository-local directory.
if [[ "$ci" == true ]]; then
use_global_nuget_cache=${use_global_nuget_cache:-false}
@ -77,7 +81,7 @@ function ReadGlobalVersion {
local pattern="\"$key\" *: *\"(.*)\""
if [[ ! $line =~ $pattern ]]; then
Write-PipelineTelemetryError -category 'InitializeTools' "Error: Cannot find \"$key\" in $global_json_file"
Write-PipelineTelemetryError -category 'InitializeToolset' "Error: Cannot find \"$key\" in $global_json_file"
ExitWithExitCode 1
fi
@ -195,7 +199,7 @@ function InstallDotNet {
function GetDotNetInstallScript {
local root=$1
local install_script="$root/dotnet-install.sh"
local install_script_url="https://dot.net/v1/dotnet-install.sh"
local install_script_url="https://dot.net/$dotnetInstallScriptVersion/dotnet-install.sh"
if [[ ! -a "$install_script" ]]; then
mkdir -p "$root"
@ -245,7 +249,7 @@ function InitializeNativeTools() {
then
local nativeArgs=""
if [[ "$ci" == true ]]; then
nativeArgs="-InstallDirectory $tools_dir"
nativeArgs="--installDirectory $tools_dir"
fi
"$_script_dir/init-tools-native.sh" $nativeArgs
fi

View File

@ -166,11 +166,6 @@ try {
& dotnet run -p "$repoRoot/eng/tools/BaselineGenerator/"
}
Write-Host "Re-generating Web.JS files"
Invoke-Block {
& dotnet build "$repoRoot\src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj"
}
Write-Host "Run git diff to check for pending changes"
# Redirect stderr to stdout because PowerShell does not consistently handle output to stderr

View File

@ -11,15 +11,28 @@
<IntermediateOutputPath>$([MSBuild]::NormalizeDirectory('$(BaseIntermediateOutputPath)'))$(Configuration)\</IntermediateOutputPath>
<InstallArgs Condition="'$(RestoreLockedMode)' == 'true'">--frozen-lockfile</InstallArgs>
<_BackupPackageJson>$(IntermediateOutputPath)$(MSBuildProjectName).package.json.bak</_BackupPackageJson>
<BuildDependsOn>
PrepareForBuild;
ResolveProjectReferences;
_Build;
</BuildDependsOn>
<NpmBuildArgs Condition="'$(NpmBuildArgs)' == ''">run build</NpmBuildArgs>
</PropertyGroup>
<ItemGroup>
<TSFiles Include="$(MSBuildProjectDirectory)\*\*.ts" />
<TSFiles Include="$(MSBuildProjectDirectory)\package.json" />
<TSFiles Include="$(MSBuildProjectDirectory)\*.npmproj" />
</ItemGroup>
<Target Name="_CheckForInvalidConfiguration">
<Error Text="Missing expected property: PackageId" Condition="'$(IsPackable)' != 'false' and '$(PackageId)' == ''" />
<Exec ContinueOnError="true" Command="node -v">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode"/>
</Exec>
<Error Text="Building *.npmproj but NodeJS was not detected on path. Ensure NodeJS is on path or disable building NodeJS projects with /p:BuildNodeJs=false. Skipping NodeJS projects will also skip managed projects depending on them, including Components, Mvc and Analysers." Condition="'$(ErrorCode)' != '0'"/>
</Target>
<Target Name="Restore">
@ -36,13 +49,13 @@
BuildInParallel="true" />
</Target>
<Target Name="Build" DependsOnTargets="PrepareForBuild;ResolveProjectReferences;_Build" />
<Target Name="Build" DependsOnTargets="$(BuildDependsOn)" />
<Target Name="_Build"
Condition="'$(IsBuildable)' != 'false'"
Inputs="@(TSFiles)"
Outputs="$(BaseIntermediateOutputPath)\build-sentinel" >
<Yarn Command="run build" StandardOutputImportance="High" StandardErrorImportance="High" />
<Yarn Command="$(NpmBuildArgs)" StandardOutputImportance="High" StandardErrorImportance="High" />
<WriteLinesToFile Overwrite="true" File="$(BaseIntermediateOutputPath)\build-sentinel" />
</Target>

View File

@ -24,7 +24,7 @@
},
"msbuild-sdks": {
"Yarn.MSBuild": "1.15.2",
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19369.2",
"Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19369.2"
"Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19404.1",
"Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19404.1"
}
}

View File

@ -0,0 +1,39 @@
// 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.Immutable;
using Microsoft.AspNetCore.Components.Analyzers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.Extensions.Internal
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ComponentInternalUsageDiagnosticAnalyzer : DiagnosticAnalyzer
{
private readonly InternalUsageAnalyzer _inner;
public ComponentInternalUsageDiagnosticAnalyzer()
{
// We don't have in *internal* attribute in Blazor.
_inner = new InternalUsageAnalyzer(IsInInternalNamespace, hasInternalAttribute: null, DiagnosticDescriptors.DoNotUseRenderTreeTypes);
}
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(DiagnosticDescriptors.DoNotUseRenderTreeTypes);
public override void Initialize(AnalysisContext context)
{
_inner.Register(context);
}
private static bool IsInInternalNamespace(ISymbol symbol)
{
if (symbol?.ContainingNamespace?.ToDisplayString() is string ns)
{
return string.Equals(ns, "Microsoft.AspNetCore.Components.RenderTree");
}
return false;
}
}
}

View File

@ -55,5 +55,14 @@ namespace Microsoft.AspNetCore.Components.Analyzers
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: new LocalizableResourceString(nameof(Resources.ComponentParameterShouldNotBeSetOutsideOfTheirDeclaredComponent_Description), Resources.ResourceManager, typeof(Resources)));
public static readonly DiagnosticDescriptor DoNotUseRenderTreeTypes = new DiagnosticDescriptor(
"BL0006",
new LocalizableResourceString(nameof(Resources.DoNotUseRenderTreeTypes_Title), Resources.ResourceManager, typeof(Resources)),
new LocalizableResourceString(nameof(Resources.DoNotUseRenderTreeTypes_Description), Resources.ResourceManager, typeof(Resources)),
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: new LocalizableResourceString(nameof(Resources.DoNotUseRenderTreeTypes_Description), Resources.ResourceManager, typeof(Resources)));
}
}

View File

@ -0,0 +1,126 @@
// 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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.Extensions.Internal
{
internal class InternalUsageAnalyzer
{
private readonly Func<ISymbol, bool> _isInternalNamespace;
private readonly Func<ISymbol, bool> _hasInternalAttribute;
private readonly DiagnosticDescriptor _descriptor;
/// <summary>
/// Creates a new instance of <see cref="InternalUsageAnalyzer" />. The creator should provide delegates to help determine whether
/// a given symbol is internal or not, and a <see cref="DiagnosticDescriptor" /> to create errors.
/// </summary>
/// <param name="isInInternalNamespace">The delegate used to check if a symbol belongs to an internal namespace.</param>
/// <param name="hasInternalAttribute">The delegate used to check if a symbol has an internal attribute.</param>
/// <param name="descriptor">
/// The <see cref="DiagnosticDescriptor" /> used to create errors. The error message should expect a single parameter
/// used for the display name of the member.
/// </param>
public InternalUsageAnalyzer(Func<ISymbol, bool> isInInternalNamespace, Func<ISymbol, bool> hasInternalAttribute, DiagnosticDescriptor descriptor)
{
_isInternalNamespace = isInInternalNamespace ?? new Func<ISymbol, bool>((_) => false);
_hasInternalAttribute = hasInternalAttribute ?? new Func<ISymbol, bool>((_) => false);
_descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
}
public void Register(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode,
SyntaxKind.SimpleMemberAccessExpression,
SyntaxKind.ObjectCreationExpression,
SyntaxKind.ClassDeclaration,
SyntaxKind.Parameter);
}
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
switch (context.Node)
{
case MemberAccessExpressionSyntax memberAccessSyntax:
{
if (context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol is ISymbol symbol &&
symbol.ContainingAssembly != context.Compilation.Assembly)
{
var containingType = symbol.ContainingType;
if (HasInternalAttribute(symbol))
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, memberAccessSyntax.Name.GetLocation(), $"{containingType}.{symbol.Name}"));
return;
}
if (IsInInternalNamespace(containingType) || HasInternalAttribute(containingType))
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, memberAccessSyntax.Name.GetLocation(), containingType));
return;
}
}
return;
}
case ObjectCreationExpressionSyntax creationSyntax:
{
if (context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol is ISymbol symbol &&
symbol.ContainingAssembly != context.Compilation.Assembly)
{
var containingType = symbol.ContainingType;
if (HasInternalAttribute(symbol))
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, creationSyntax.GetLocation(), containingType));
return;
}
if (IsInInternalNamespace(containingType) || HasInternalAttribute(containingType))
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, creationSyntax.Type.GetLocation(), containingType));
return;
}
}
return;
}
case ClassDeclarationSyntax declarationSyntax:
{
if (context.SemanticModel.GetDeclaredSymbol(declarationSyntax)?.BaseType is ISymbol symbol &&
symbol.ContainingAssembly != context.Compilation.Assembly &&
(IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)) &&
declarationSyntax.BaseList?.Types.Count > 0)
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, declarationSyntax.BaseList.Types[0].GetLocation(), symbol));
}
return;
}
case ParameterSyntax parameterSyntax:
{
if (context.SemanticModel.GetDeclaredSymbol(parameterSyntax)?.Type is ISymbol symbol &&
symbol.ContainingAssembly != context.Compilation.Assembly &&
(IsInInternalNamespace(symbol) || HasInternalAttribute(symbol)))
{
context.ReportDiagnostic(Diagnostic.Create(_descriptor, parameterSyntax.GetLocation(), symbol));
}
return;
}
}
}
private bool HasInternalAttribute(ISymbol symbol) => _hasInternalAttribute(symbol);
private bool IsInInternalNamespace(ISymbol symbol) => _isInternalNamespace(symbol);
}
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
@ -165,4 +165,13 @@
<data name="ComponentParameterShouldNotBeSetOutsideOfTheirDeclaredComponent_Title" xml:space="preserve">
<value>Component parameter should not be set outside of its component.</value>
</data>
<data name="DoNotUseRenderTreeTypes_Description" xml:space="preserve">
<value>The types in 'Microsoft.AspNetCore.Components.RenderTree' are not recommended for use outside of the Blazor framework. These type definitions will change in future releases.</value>
</data>
<data name="DoNotUseRenderTreeTypes_Format" xml:space="preserve">
<value>The type or member {0} is is not recommended for use outside of the Blazor frameworks. Types defined in 'Microsoft.AspNetCore.Components.RenderTree' will change in future releases.</value>
</data>
<data name="DoNotUseRenderTreeTypes_Title" xml:space="preserve">
<value>Do not use RenderTree types</value>
</data>
</root>

View File

@ -0,0 +1,68 @@
// 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Components.Analyzers
{
public abstract class AnalyzerTestBase
{
private static readonly string ProjectDirectory = GetProjectDirectory();
public TestSource Read(string source)
{
if (!source.EndsWith(".cs"))
{
source = source + ".cs";
}
var filePath = Path.Combine(ProjectDirectory, "TestFiles", GetType().Name, source);
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"TestFile {source} could not be found at {filePath}.", filePath);
}
var fileContent = File.ReadAllText(filePath);
return TestSource.Read(fileContent);
}
public Project CreateProject(string source)
{
if (!source.EndsWith(".cs"))
{
source = source + ".cs";
}
var read = Read(source);
return DiagnosticProject.Create(GetType().Assembly, new[] { read.Source, });
}
public Task<Compilation> CreateCompilationAsync(string source)
{
return CreateProject(source).GetCompilationAsync();
}
private static string GetProjectDirectory()
{
// On helix we use the published test files
if (SkipOnHelixAttribute.OnHelix())
{
return AppContext.BaseDirectory;
}
// This test code needs to be updated to support distributed testing.
// See https://github.com/aspnet/AspNetCore/issues/10422
#pragma warning disable 0618
var solutionDirectory = TestPathUtilities.GetSolutionRootDirectory("Components");
#pragma warning restore 0618
var projectDirectory = Path.Combine(solutionDirectory, "Analyzers", "test");
return projectDirectory;
}
}
}

View File

@ -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.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.AspNetCore.Components.Analyzers
{
internal class ComponentAnalyzerDiagnosticAnalyzerRunner : DiagnosticAnalyzerRunner
{
public ComponentAnalyzerDiagnosticAnalyzerRunner(DiagnosticAnalyzer analyzer)
{
Analyzer = analyzer;
}
public DiagnosticAnalyzer Analyzer { get; }
public Task<Diagnostic[]> GetDiagnosticsAsync(string source)
{
return GetDiagnosticsAsync(sources: new[] { source }, Analyzer, Array.Empty<string>());
}
public Task<Diagnostic[]> GetDiagnosticsAsync(Project project)
{
return GetDiagnosticsAsync(new[] { project }, Analyzer, Array.Empty<string>());
}
}
}

View File

@ -0,0 +1,60 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Analyzer.Testing;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Components.Analyzers
{
public class ComponentInternalUsageDiagnoticsAnalyzerTest : AnalyzerTestBase
{
public ComponentInternalUsageDiagnoticsAnalyzerTest()
{
Analyzer = new ComponentInternalUsageDiagnosticAnalyzer();
Runner = new ComponentAnalyzerDiagnosticAnalyzerRunner(Analyzer);
}
private ComponentInternalUsageDiagnosticAnalyzer Analyzer { get; }
private ComponentAnalyzerDiagnosticAnalyzerRunner Runner { get; }
[Fact]
public async Task InternalUsage_FindsUseOfRenderTreeFrameAsParameter()
{
// Arrange
var source = Read("UsesRenderTreeFrameAsParameter");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
// Assert
Assert.Collection(
diagnostics,
diagnostic =>
{
Assert.Same(DiagnosticDescriptors.DoNotUseRenderTreeTypes, diagnostic.Descriptor);
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
});
}
[Fact]
public async Task InternalUsage_FindsUseOfRenderTreeType()
{
// Arrange
var source = Read("UsesRenderTreeFrameTypeAsLocal");
// Act
var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
// Assert
Assert.Collection(
diagnostics,
diagnostic =>
{
Assert.Same(DiagnosticDescriptors.DoNotUseRenderTreeTypes, diagnostic.Descriptor);
AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
});
}
}
}

View File

@ -2,16 +2,24 @@
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
</ItemGroup>
<!-- Tests do not work on Helix or when bin/ directory is not in project directory due to undeclared dependency on test content. -->
<!-- https://github.com/aspnet/AspNetCore/issues/10422 -->
<BuildHelixPayload>false</BuildHelixPayload>
<BaseOutputPath />
</PropertyGroup>
<ItemGroup>
<!-- This is set to a ProjectReference because analyzers cannot be referenced via Reference. -->
<ProjectReference Include="..\src\Microsoft.AspNetCore.Components.Analyzers.csproj" />
<Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<Reference Include="Microsoft.AspNetCore.Analyzer.Testing" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(SharedSourceRoot)test\SkipOnHelixAttribute.cs" />
<Content Include="TestFiles\**\*.*" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Analyzers.Tests.TestFiles.ComponentInternalUsageDiagnoticsAnalyzerTest
{
class UsesRenderTreeFrameAsParameter
{
private void Test(/*MM*/RenderTreeFrame frame)
{
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using Microsoft.AspNetCore.Components.RenderTree;
namespace Microsoft.AspNetCore.Components.Analyzers.Tests.TestFiles.ComponentInternalUsageDiagnoticsAnalyzerTest
{
class UsesRenderTreeFrameTypeAsLocal
{
private void Test()
{
var test = RenderTreeFrameType./*MM*/Attribute;
GC.KeepAlive(test);
}
}
}

View File

@ -26,7 +26,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Condition="'$(BuildNodeJS)' != 'false'" Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj" ReferenceOutputAssembly="false" />
<ProjectReference Condition="'$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'" Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj" ReferenceOutputAssembly="false" />
<Reference Include="Microsoft.AspNetCore.Components" />
<Reference Include="Microsoft.Extensions.CommandLineUtils.Sources" />
<Reference Include="Microsoft.Extensions.FileProviders.Composite" />

View File

@ -1,5 +1,10 @@
<Router AppAssembly="typeof(Program).Assembly">
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<p>Sorry, there's nothing at this address.</p>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

View File

@ -1 +1,8 @@
<Router AppAssembly=typeof(Program).Assembly />
<Router AppAssembly=typeof(Program).Assembly>
<Found Context="routeData">
<RouteView RouteData="@routeData" />
</Found>
<NotFound>
Sorry, there's nothing here.
</NotFound>
</Router>

View File

@ -11,7 +11,4 @@
<!-- loader.js is hard-coded to assume it can load .pdbs regardless of Debug/Release configuration -->
<BlazorEnableDebugging>true</BlazorEnableDebugging>
</PropertyGroup>
<ItemGroup>
</ItemGroup>
</Project>

View File

@ -1,5 +1,11 @@
<!--
Configuring this stuff here is temporary. Later we'll move the app config
into Program.cs, and it won't be necessary to specify AppAssembly.
-->
<Router AppAssembly=typeof(StandaloneApp.Program).Assembly />
<Router AppAssembly=typeof(StandaloneApp.Program).Assembly>
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<h2>Not found</h2>
Sorry, there's nothing at this address.
</LayoutView>
</NotFound>
</Router>

View File

@ -1 +0,0 @@
@layout MainLayout

View File

@ -16,6 +16,15 @@ namespace Microsoft.AspNetCore.Components
public abstract System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> GetAuthenticationStateAsync();
protected void NotifyAuthenticationStateChanged(System.Threading.Tasks.Task<Microsoft.AspNetCore.Components.AuthenticationState> task) { }
}
public sealed partial class AuthorizeRouteView : Microsoft.AspNetCore.Components.RouteView
{
public AuthorizeRouteView() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
protected override void Render(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
}
public partial class AuthorizeView : Microsoft.AspNetCore.Components.AuthorizeViewCore
{
public AuthorizeView() { }
@ -278,6 +287,16 @@ namespace Microsoft.AspNetCore.Components
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Body { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
public partial class LayoutView : Microsoft.AspNetCore.Components.IComponent
{
public LayoutView() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment ChildContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Type Layout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }
public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
}
public sealed partial class LocationChangeException : System.Exception
{
public LocationChangeException(string message, System.Exception innerException) { }
@ -323,20 +342,6 @@ namespace Microsoft.AspNetCore.Components
protected OwningComponentBase() { }
protected TService Service { get { throw null; } }
}
public partial class PageDisplay : Microsoft.AspNetCore.Components.IComponent
{
public PageDisplay() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Type Page { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Collections.Generic.IDictionary<string, object> PageParameters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }
public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
}
[System.AttributeUsageAttribute(System.AttributeTargets.Property, AllowMultiple=false, Inherited=true)]
public sealed partial class ParameterAttribute : System.Attribute
{
@ -391,6 +396,23 @@ namespace Microsoft.AspNetCore.Components
public RouteAttribute(string template) { }
public string Template { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public sealed partial class RouteData
{
public RouteData(System.Type pageType, System.Collections.Generic.IReadOnlyDictionary<string, object> routeValues) { }
public System.Type PageType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public System.Collections.Generic.IReadOnlyDictionary<string, object> RouteValues { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public partial class RouteView : Microsoft.AspNetCore.Components.IComponent
{
public RouteView() { }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Type DefaultLayout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RouteData RouteData { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }
protected virtual void Render(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder builder) { }
public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) { throw null; }
}
}
namespace Microsoft.AspNetCore.Components.CompilerServices
{
@ -513,13 +535,13 @@ namespace Microsoft.AspNetCore.Components.Rendering
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
protected internal virtual void AddToRenderQueue(int componentId, Microsoft.AspNetCore.Components.RenderFragment renderFragment) { }
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo fieldInfo, System.EventArgs eventArgs) { throw null; }
public void Dispose() { }
protected virtual void Dispose(bool disposing) { }
protected abstract void HandleException(System.Exception exception);
protected Microsoft.AspNetCore.Components.IComponent InstantiateComponent(System.Type componentType) { throw null; }
protected virtual void ProcessPendingRender() { }
protected System.Threading.Tasks.Task RenderRootComponentAsync(int componentId) { throw null; }
[System.Diagnostics.DebuggerStepThroughAttribute]
protected System.Threading.Tasks.Task RenderRootComponentAsync(int componentId, Microsoft.AspNetCore.Components.ParameterView initialParameters) { throw null; }
@ -648,9 +670,7 @@ namespace Microsoft.AspNetCore.Components.Routing
[Microsoft.AspNetCore.Components.ParameterAttribute]
public System.Reflection.Assembly AppAssembly { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment Authorizing { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.AuthenticationState> NotAuthorized { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.RouteData> Found { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
[Microsoft.AspNetCore.Components.ParameterAttribute]
public Microsoft.AspNetCore.Components.RenderFragment NotFound { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { }

View File

@ -0,0 +1,118 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Auth;
using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Combines the behaviors of <see cref="AuthorizeView"/> and <see cref="RouteView"/>,
/// so that it displays the page matching the specified route but only if the user
/// is authorized to see it.
///
/// Additionally, this component supplies a cascading parameter of type <see cref="Task{AuthenticationState}"/>,
/// which makes the user's current authentication state available to descendants.
/// </summary>
public sealed class AuthorizeRouteView : RouteView
{
// We expect applications to supply their own authorizing/not-authorized content, but
// it's better to have defaults than to make the parameters mandatory because in some
// cases they will never be used (e.g., "authorizing" in out-of-box server-side Blazor)
private static readonly RenderFragment<AuthenticationState> _defaultNotAuthorizedContent
= state => builder => builder.AddContent(0, "Not authorized");
private static readonly RenderFragment _defaultAuthorizingContent
= builder => builder.AddContent(0, "Authorizing...");
private readonly RenderFragment _renderAuthorizeRouteViewCoreDelegate;
private readonly RenderFragment<AuthenticationState> _renderAuthorizedDelegate;
private readonly RenderFragment<AuthenticationState> _renderNotAuthorizedDelegate;
private readonly RenderFragment _renderAuthorizingDelegate;
public AuthorizeRouteView()
{
// Cache the rendering delegates so that we only construct new closure instances
// when they are actually used (e.g., we never prepare a RenderFragment bound to
// the NotAuthorized content except when you are displaying that particular state)
RenderFragment renderBaseRouteViewDelegate = builder => base.Render(builder);
_renderAuthorizedDelegate = authenticateState => renderBaseRouteViewDelegate;
_renderNotAuthorizedDelegate = authenticationState => builder => RenderNotAuthorizedInDefaultLayout(builder, authenticationState);
_renderAuthorizingDelegate = RenderAuthorizingInDefaultLayout;
_renderAuthorizeRouteViewCoreDelegate = RenderAuthorizeRouteViewCore;
}
/// <summary>
/// The content that will be displayed if the user is not authorized.
/// </summary>
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
/// <summary>
/// The content that will be displayed while asynchronous authorization is in progress.
/// </summary>
[Parameter]
public RenderFragment Authorizing { get; set; }
[CascadingParameter]
private Task<AuthenticationState> ExistingCascadedAuthenticationState { get; set; }
/// <inheritdoc />
protected override void Render(RenderTreeBuilder builder)
{
if (ExistingCascadedAuthenticationState != null)
{
// If this component is already wrapped in a <CascadingAuthenticationState> (or another
// compatible provider), then don't interfere with the cascaded authentication state.
_renderAuthorizeRouteViewCoreDelegate(builder);
}
else
{
// Otherwise, implicitly wrap the output in a <CascadingAuthenticationState>
builder.OpenComponent<CascadingAuthenticationState>(0);
builder.AddAttribute(1, nameof(CascadingAuthenticationState.ChildContent), _renderAuthorizeRouteViewCoreDelegate);
builder.CloseComponent();
}
}
private void RenderAuthorizeRouteViewCore(RenderTreeBuilder builder)
{
builder.OpenComponent<AuthorizeRouteViewCore>(0);
builder.AddAttribute(1, nameof(AuthorizeRouteViewCore.RouteData), RouteData);
builder.AddAttribute(2, nameof(AuthorizeRouteViewCore.Authorized), _renderAuthorizedDelegate);
builder.AddAttribute(3, nameof(AuthorizeRouteViewCore.Authorizing), _renderAuthorizingDelegate);
builder.AddAttribute(4, nameof(AuthorizeRouteViewCore.NotAuthorized), _renderNotAuthorizedDelegate);
builder.CloseComponent();
}
private void RenderContentInDefaultLayout(RenderTreeBuilder builder, RenderFragment content)
{
builder.OpenComponent<LayoutView>(0);
builder.AddAttribute(1, nameof(LayoutView.Layout), DefaultLayout);
builder.AddAttribute(2, nameof(LayoutView.ChildContent), content);
builder.CloseComponent();
}
private void RenderNotAuthorizedInDefaultLayout(RenderTreeBuilder builder, AuthenticationState authenticationState)
{
var content = NotAuthorized ?? _defaultNotAuthorizedContent;
RenderContentInDefaultLayout(builder, content(authenticationState));
}
private void RenderAuthorizingInDefaultLayout(RenderTreeBuilder builder)
{
var content = Authorizing ?? _defaultAuthorizingContent;
RenderContentInDefaultLayout(builder, content);
}
private class AuthorizeRouteViewCore : AuthorizeViewCore
{
[Parameter]
public RouteData RouteData { get; set; }
protected override IAuthorizeData[] GetAuthorizeData()
=> AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}
}
}

View File

@ -52,6 +52,8 @@ namespace Microsoft.AspNetCore.Components
/// <inheritdoc />
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// We're using the same sequence number for each of the content items here
// so that we can update existing instances if they are the same shape
if (currentAuthenticationState == null)
{
builder.AddContent(0, Authorizing);
@ -59,11 +61,11 @@ namespace Microsoft.AspNetCore.Components
else if (isAuthorized)
{
var authorized = Authorized ?? ChildContent;
builder.AddContent(1, authorized?.Invoke(currentAuthenticationState));
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState));
}
else
{
builder.AddContent(2, NotAuthorized?.Invoke(currentAuthenticationState));
builder.AddContent(0, NotAuthorized?.Invoke(currentAuthenticationState));
}
}
@ -102,6 +104,12 @@ namespace Microsoft.AspNetCore.Components
private async Task<bool> IsAuthorizedAsync(ClaimsPrincipal user)
{
var authorizeData = GetAuthorizeData();
if (authorizeData == null)
{
// No authorization applies, so no need to consult the authorization service
return true;
}
EnsureNoAuthenticationSchemeSpecified(authorizeData);
var policy = await AuthorizationPolicy.CombineAsync(

View File

@ -171,10 +171,25 @@ namespace Microsoft.AspNetCore.Components
_renderHandle = renderHandle;
}
/// <summary>
/// Method invoked to apply initial or updated parameters to the component.
/// Sets parameters supplied by the component's parent in the render tree.
/// </summary>
/// <param name="parameters">The parameters to apply.</param>
/// <param name="parameters">The parameters.</param>
/// <returns>A <see cref="Task"/> that completes when the component has finished updating and rendering itself.</returns>
/// <remarks>
/// <para>
/// The <see cref="SetParametersAsync(ParameterView)"/> method should be passed the entire set of parameter values each
/// time <see cref="SetParametersAsync(ParameterView)"/> is called. It not required that the caller supply a parameter
/// value for all parameters that are logically understood by the component.
/// </para>
/// <para>
/// The default implementation of <see cref="SetParametersAsync(ParameterView)"/> will set the value of each property
/// decorated with <see cref="ParameterAttribute" /> or <see cref="CascadingParameterAttribute" /> that has
/// a corresponding value in the <see cref="ParameterView" />. Parameters that do not have a corresponding value
/// will be unchanged.
/// </para>
/// </remarks>
public virtual Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);

View File

@ -61,7 +61,7 @@ namespace Microsoft.AspNetCore.Components
(
propertyName: property.Name,
propertyType: property.PropertyType,
setter: MemberAssignment.CreatePropertySetter(type, property)
setter: MemberAssignment.CreatePropertySetter(type, property, cascading: false)
)).ToArray();
return Initialize;

View File

@ -21,6 +21,11 @@ namespace Microsoft.AspNetCore.Components
/// </summary>
/// <param name="parameters">The parameters.</param>
/// <returns>A <see cref="Task"/> that completes when the component has finished updating and rendering itself.</returns>
/// <remarks>
/// The <see cref="SetParametersAsync(ParameterView)"/> method should be passed the entire set of parameter values each
/// time <see cref="SetParametersAsync(ParameterView)"/> is called. It not required that the caller supply a parameter
/// value for all parameters that are logically understood by the component.
/// </remarks>
Task SetParametersAsync(ParameterView parameters);
}
}

View File

@ -0,0 +1,77 @@
// 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.Reflection;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Displays the specified content inside the specified layout and any further
/// nested layouts.
/// </summary>
public class LayoutView : IComponent
{
private static readonly RenderFragment EmptyRenderFragment = builder => { };
private RenderHandle _renderHandle;
/// <summary>
/// Gets or sets the content to display.
/// </summary>
[Parameter]
public RenderFragment ChildContent { get; set; }
/// <summary>
/// Gets or sets the type of the layout in which to display the content.
/// The type must implement <see cref="IComponent"/> and accept a parameter named <see cref="LayoutComponentBase.Body"/>.
/// </summary>
[Parameter]
public Type Layout { get; set; }
/// <inheritdoc />
public void Attach(RenderHandle renderHandle)
{
_renderHandle = renderHandle;
}
/// <inheritdoc />
public Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);
Render();
return Task.CompletedTask;
}
private void Render()
{
// In the middle goes the supplied content
var fragment = ChildContent ?? EmptyRenderFragment;
// Then repeatedly wrap that in each layer of nested layout until we get
// to a layout that has no parent
var layoutType = Layout;
while (layoutType != null)
{
fragment = WrapInLayout(layoutType, fragment);
layoutType = GetParentLayoutType(layoutType);
}
_renderHandle.Render(fragment);
}
private static RenderFragment WrapInLayout(Type layoutType, RenderFragment bodyParam)
{
return builder =>
{
builder.OpenComponent(0, layoutType);
builder.AddAttribute(1, LayoutComponentBase.BodyPropertyName, bodyParam);
builder.CloseComponent();
};
}
private static Type GetParentLayoutType(Type type)
=> type.GetCustomAttribute<LayoutAttribute>()?.LayoutType;
}
}

View File

@ -1,139 +0,0 @@
// 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.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Auth;
using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Displays the specified page component, rendering it inside its layout
/// and any further nested layouts, plus applying any authorization rules.
/// </summary>
public class PageDisplay : IComponent
{
private RenderHandle _renderHandle;
/// <summary>
/// Gets or sets the type of the page component to display.
/// The type must implement <see cref="IComponent"/>.
/// </summary>
[Parameter]
public Type Page { get; set; }
/// <summary>
/// Gets or sets the parameters to pass to the page.
/// </summary>
[Parameter]
public IDictionary<string, object> PageParameters { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authorized.
/// </summary>
[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
/// <summary>
/// The content that will be displayed while asynchronous authorization is in progress.
/// </summary>
[Parameter]
public RenderFragment Authorizing { get; set; }
/// <inheritdoc />
public void Attach(RenderHandle renderHandle)
{
_renderHandle = renderHandle;
}
/// <inheritdoc />
public Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);
Render();
return Task.CompletedTask;
}
private void Render()
{
// In the middle goes the requested page
var fragment = (RenderFragment)RenderPageWithParameters;
// Around that goes an AuthorizeViewCore
fragment = WrapInAuthorizeViewCore(fragment);
// Then repeatedly wrap that in each layer of nested layout until we get
// to a layout that has no parent
Type layoutType = Page;
while ((layoutType = GetLayoutType(layoutType)) != null)
{
fragment = WrapInLayout(layoutType, fragment);
}
_renderHandle.Render(fragment);
}
private RenderFragment WrapInLayout(Type layoutType, RenderFragment bodyParam) => builder =>
{
builder.OpenComponent(0, layoutType);
builder.AddAttribute(1, LayoutComponentBase.BodyPropertyName, bodyParam);
builder.CloseComponent();
};
private void RenderPageWithParameters(RenderTreeBuilder builder)
{
builder.OpenComponent(0, Page);
if (PageParameters != null)
{
foreach (var kvp in PageParameters)
{
builder.AddAttribute(1, kvp.Key, kvp.Value);
}
}
builder.CloseComponent();
}
private RenderFragment WrapInAuthorizeViewCore(RenderFragment pageFragment)
{
var authorizeData = AttributeAuthorizeDataCache.GetAuthorizeDataForType(Page);
if (authorizeData == null)
{
// No authorization, so no need to wrap the fragment
return pageFragment;
}
// Some authorization data exists, so we do need to wrap the fragment
RenderFragment<AuthenticationState> authorized = context => pageFragment;
return builder =>
{
builder.OpenComponent<AuthorizeViewWithSuppliedData>(0);
builder.AddAttribute(1, nameof(AuthorizeViewWithSuppliedData.AuthorizeDataParam), authorizeData);
builder.AddAttribute(2, nameof(AuthorizeViewWithSuppliedData.Authorized), authorized);
builder.AddAttribute(3, nameof(AuthorizeViewWithSuppliedData.NotAuthorized), NotAuthorized ?? DefaultNotAuthorized);
builder.AddAttribute(4, nameof(AuthorizeViewWithSuppliedData.Authorizing), Authorizing);
builder.CloseComponent();
};
}
private static Type GetLayoutType(Type type)
=> type.GetCustomAttribute<LayoutAttribute>()?.LayoutType;
private class AuthorizeViewWithSuppliedData : AuthorizeViewCore
{
[Parameter] public IAuthorizeData[] AuthorizeDataParam { get; private set; }
protected override IAuthorizeData[] GetAuthorizeData() => AuthorizeDataParam;
}
// There has to be some default content. If we render blank by default, developers
// will find it hard to guess why their UI isn't appearing.
private static RenderFragment DefaultNotAuthorized(AuthenticationState authenticationState)
=> builder => builder.AddContent(0, "Not authorized");
}
}

View File

@ -14,6 +14,9 @@ namespace Microsoft.AspNetCore.Components.Reflection
{
private const BindingFlags _bindablePropertyFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase;
// Right now it's not possible for a component to define a Parameter and a Cascading Parameter with
// the same name. We don't give you a way to express this in code (would create duplicate properties),
// and we don't have the ability to represent it in our data structures.
private readonly static ConcurrentDictionary<Type, WritersForType> _cachedWritersByType
= new ConcurrentDictionary<Type, WritersForType>();
@ -44,6 +47,24 @@ namespace Microsoft.AspNetCore.Components.Reflection
ThrowForUnknownIncomingParameterName(targetType, parameterName);
throw null; // Unreachable
}
else if (writer.Cascading && !parameter.Cascading)
{
// We don't allow you to set a cascading parameter with a non-cascading value. Put another way:
// cascading parameters are not part of the public API of a component, so it's not reasonable
// for someone to set it directly.
//
// If we find a strong reason for this to work in the future we can reverse our decision since
// this throws today.
ThrowForSettingCascadingParameterWithNonCascadingValue(targetType, parameterName);
throw null; // Unreachable
}
else if (!writer.Cascading && parameter.Cascading)
{
// We're giving a more specific error here because trying to set a non-cascading parameter
// with a cascading value is likely deliberate (but not supported), or is a bug in our code.
ThrowForSettingParameterWithCascadingValue(targetType, parameterName);
throw null; // Unreachable
}
SetProperty(target, writer, parameterName, parameter.Value);
}
@ -62,7 +83,24 @@ namespace Microsoft.AspNetCore.Components.Reflection
}
var isUnmatchedValue = !writers.WritersByName.TryGetValue(parameterName, out var writer);
if (isUnmatchedValue)
if ((isUnmatchedValue && parameter.Cascading) || (writer != null && !writer.Cascading && parameter.Cascading))
{
// Don't allow an "extra" cascading value to be collected - or don't allow a non-cascading
// parameter to be set with a cascading value.
//
// This is likely a bug in our infrastructure or an attempt to deliberately do something unsupported.
ThrowForSettingParameterWithCascadingValue(targetType, parameterName);
throw null; // Unreachable
}
else if (isUnmatchedValue ||
// Allow unmatched parameters to collide with the names of cascading parameters. This is
// valid because cascading parameter names are not part of the public API. There's no
// way for the user of a component to know what the names of cascading parameters
// are.
(writer.Cascading && !parameter.Cascading))
{
unmatched ??= new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
unmatched[parameterName] = parameter.Value;
@ -138,6 +176,20 @@ namespace Microsoft.AspNetCore.Components.Reflection
}
}
private static void ThrowForSettingCascadingParameterWithNonCascadingValue(Type targetType, string parameterName)
{
throw new InvalidOperationException(
$"Object of type '{targetType.FullName}' has a property matching the name '{parameterName}', " +
$"but it does not have [{nameof(ParameterAttribute)}] applied.");
}
private static void ThrowForSettingParameterWithCascadingValue(Type targetType, string parameterName)
{
throw new InvalidOperationException(
$"The property '{parameterName}' on component type '{targetType.FullName}' cannot be set " +
$"using a cascading value.");
}
private static void ThrowForCaptureUnmatchedValuesConflict(Type targetType, string parameterName, Dictionary<string, object> unmatched)
{
throw new InvalidOperationException(
@ -179,13 +231,14 @@ namespace Microsoft.AspNetCore.Components.Reflection
foreach (var propertyInfo in GetCandidateBindableProperties(targetType))
{
var parameterAttribute = propertyInfo.GetCustomAttribute<ParameterAttribute>();
var isParameter = parameterAttribute != null || propertyInfo.IsDefined(typeof(CascadingParameterAttribute));
var cascadingParameterAttribute = propertyInfo.GetCustomAttribute<CascadingParameterAttribute>();
var isParameter = parameterAttribute != null || cascadingParameterAttribute != null;
if (!isParameter)
{
continue;
}
var propertySetter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo);
var propertySetter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo, cascading: cascadingParameterAttribute != null);
var propertyName = propertyInfo.Name;
if (WritersByName.ContainsKey(propertyName))
@ -213,7 +266,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
ThrowForInvalidCaptureUnmatchedValuesParameterType(targetType, propertyInfo);
}
CaptureUnmatchedValuesWriter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo);
CaptureUnmatchedValuesWriter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo, cascading: false);
CaptureUnmatchedValuesPropertyName = propertyInfo.Name;
}
}

View File

@ -5,6 +5,8 @@ namespace Microsoft.AspNetCore.Components.Reflection
{
internal interface IPropertySetter
{
bool Cascading { get; }
void SetValue(object target, object value);
}
}

View File

@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Components.Reflection
}
}
public static IPropertySetter CreatePropertySetter(Type targetType, PropertyInfo property)
public static IPropertySetter CreatePropertySetter(Type targetType, PropertyInfo property, bool cascading)
{
if (property.SetMethod == null)
{
@ -37,19 +37,23 @@ namespace Microsoft.AspNetCore.Components.Reflection
return (IPropertySetter)Activator.CreateInstance(
typeof(PropertySetter<,>).MakeGenericType(targetType, property.PropertyType),
property.SetMethod);
property.SetMethod,
cascading);
}
class PropertySetter<TTarget, TValue> : IPropertySetter
{
private readonly Action<TTarget, TValue> _setterDelegate;
public PropertySetter(MethodInfo setMethod)
public PropertySetter(MethodInfo setMethod, bool cascading)
{
_setterDelegate = (Action<TTarget, TValue>)Delegate.CreateDelegate(
typeof(Action<TTarget, TValue>), setMethod);
Cascading = cascading;
}
public bool Cascading { get; }
public void SetValue(object target, object value)
{
if (value == null)

View File

@ -8,9 +8,12 @@ using System.Collections.Generic;
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Represents a range of elements within an instance of <see cref="ArrayBuilder{T}"/>.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
/// <typeparam name="T">The type of the elements in the array</typeparam>
//
// Represents a range of elements within an instance of <see cref="ArrayBuilder{T}"/>.
public readonly struct ArrayBuilderSegment<T> : IEnumerable<T>
{
// The following fields are memory mapped to the WASM client. Do not re-order or use auto-properties.

View File

@ -4,9 +4,12 @@
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Represents a range of elements in an array that are in use.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
/// <typeparam name="T">The array item type.</typeparam>
/// <typeparam name="T"></typeparam>
//
// Represents a range of elements in an array that are in use.
public readonly struct ArrayRange<T>
{
/// <summary>

View File

@ -1,13 +1,14 @@
// 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;
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Describes changes to a component's render tree between successive renders.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
//
// Describes changes to a component's render tree between successive renders.
public readonly struct RenderTreeDiff
{
/// <summary>

View File

@ -6,8 +6,11 @@ using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Represents a single edit operation on a component's render tree.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
//
// Represents a single edit operation on a component's render tree.
[StructLayout(LayoutKind.Explicit)]
public readonly struct RenderTreeEdit
{

View File

@ -4,8 +4,11 @@
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Describes the type of a render tree edit operation.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
//
//Describes the type of a render tree edit operation.
public enum RenderTreeEditType: int
{
/// <summary>

View File

@ -8,8 +8,11 @@ using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Represents an entry in a tree of user interface (UI) items.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
//
// Represents an entry in a tree of user interface (UI) items.
[StructLayout(LayoutKind.Explicit, Pack = 4)]
public readonly struct RenderTreeFrame
{

View File

@ -4,8 +4,11 @@
namespace Microsoft.AspNetCore.Components.RenderTree
{
/// <summary>
/// Describes the type of a <see cref="RenderTreeFrame"/>.
/// Types in the Microsoft.AspNetCore.Components.RenderTree are not recommended for use outside
/// of the Blazor framework. These types will change in future release.
/// </summary>
//
// Describes the type of a <see cref="RenderTreeFrame"/>.
public enum RenderTreeFrameType: short
{
/// <summary>

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
}
internal static void DisposingComponent(ILogger<Renderer> logger, ComponentState componentState)
public static void DisposingComponent(ILogger<Renderer> logger, ComponentState componentState)
{
if (logger.IsEnabled(LogLevel.Debug)) // This is almost always false, so skip the evaluations
{
@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
}
}
internal static void HandlingEvent(ILogger<Renderer> logger, ulong eventHandlerId, EventArgs eventArgs)
public static void HandlingEvent(ILogger<Renderer> logger, ulong eventHandlerId, EventArgs eventArgs)
{
_handlingEvent(logger, eventHandlerId, eventArgs?.GetType().Name ?? "null", null);
}

View File

@ -243,7 +243,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
// Since the task has yielded - process any queued rendering work before we return control
// to the caller.
ProcessRenderQueue();
ProcessPendingRender();
}
// Task completed synchronously or is still running. We already processed all of the rendering
@ -334,7 +334,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
/// </summary>
/// <param name="componentId">The ID of the component to render.</param>
/// <param name="renderFragment">A <see cref="RenderFragment"/> that will supply the updated UI contents.</param>
protected internal virtual void AddToRenderQueue(int componentId, RenderFragment renderFragment)
internal void AddToRenderQueue(int componentId, RenderFragment renderFragment)
{
EnsureSynchronizationContext();
@ -351,7 +351,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
if (!_isBatchInProgress)
{
ProcessRenderQueue();
ProcessPendingRender();
}
}
@ -398,13 +398,33 @@ namespace Microsoft.AspNetCore.Components.Rendering
? componentState
: null;
/// <summary>
/// Processses pending renders requests from components if there are any.
/// </summary>
protected virtual void ProcessPendingRender()
{
ProcessRenderQueue();
}
private void ProcessRenderQueue()
{
EnsureSynchronizationContext();
if (_isBatchInProgress)
{
throw new InvalidOperationException("Cannot start a batch when one is already in progress.");
}
_isBatchInProgress = true;
var updateDisplayTask = Task.CompletedTask;
try
{
if (_batchBuilder.ComponentRenderQueue.Count == 0)
{
return;
}
// Process render queue until empty
while (_batchBuilder.ComponentRenderQueue.Count > 0)
{
@ -423,6 +443,7 @@ namespace Microsoft.AspNetCore.Components.Rendering
{
// Ensure we catch errors while running the render functions of the components.
HandleException(e);
return;
}
finally
{

View File

@ -0,0 +1,90 @@
// 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.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Displays the specified page component, rendering it inside its layout
/// and any further nested layouts.
/// </summary>
public class RouteView : IComponent
{
private readonly RenderFragment _renderDelegate;
private readonly RenderFragment _renderPageWithParametersDelegate;
private RenderHandle _renderHandle;
/// <summary>
/// Gets or sets the route data. This determines the page that will be
/// displayed and the parameter values that will be supplied to the page.
/// </summary>
[Parameter]
public RouteData RouteData { get; set; }
/// <summary>
/// Gets or sets the type of a layout to be used if the page does not
/// declare any layout. If specified, the type must implement <see cref="IComponent"/>
/// and accept a parameter named <see cref="LayoutComponentBase.Body"/>.
/// </summary>
[Parameter]
public Type DefaultLayout { get; set; }
public RouteView()
{
// Cache the delegate instances
_renderDelegate = Render;
_renderPageWithParametersDelegate = RenderPageWithParameters;
}
/// <inheritdoc />
public void Attach(RenderHandle renderHandle)
{
_renderHandle = renderHandle;
}
/// <inheritdoc />
public Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);
if (RouteData == null)
{
throw new InvalidOperationException($"The {nameof(RouteView)} component requires a non-null value for the parameter {nameof(RouteData)}.");
}
_renderHandle.Render(_renderDelegate);
return Task.CompletedTask;
}
/// <summary>
/// Renders the component.
/// </summary>
/// <param name="builder">The <see cref="RenderTreeBuilder"/>.</param>
protected virtual void Render(RenderTreeBuilder builder)
{
var pageLayoutType = RouteData.PageType.GetCustomAttribute<LayoutAttribute>()?.LayoutType
?? DefaultLayout;
builder.OpenComponent<LayoutView>(0);
builder.AddAttribute(1, nameof(LayoutView.Layout), pageLayoutType);
builder.AddAttribute(2, nameof(LayoutView.ChildContent), _renderPageWithParametersDelegate);
builder.CloseComponent();
}
private void RenderPageWithParameters(RenderTreeBuilder builder)
{
builder.OpenComponent(0, RouteData.PageType);
foreach (var kvp in RouteData.RouteValues)
{
builder.AddAttribute(1, kvp.Key, kvp.Value);
}
builder.CloseComponent();
}
}
}

View File

@ -26,6 +26,6 @@ namespace Microsoft.AspNetCore.Components.Routing
public Type Handler { get; set; }
public IDictionary<string, object> Parameters { get; set; }
public IReadOnlyDictionary<string, object> Parameters { get; set; }
}
}

View File

@ -0,0 +1,46 @@
// 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;
namespace Microsoft.AspNetCore.Components
{
/// <summary>
/// Describes information determined during routing that specifies
/// the page to be displayed.
/// </summary>
public sealed class RouteData
{
/// <summary>
/// Constructs an instance of <see cref="RouteData"/>.
/// </summary>
/// <param name="pageType">The type of the page matching the route, which must implement <see cref="IComponent"/>.</param>
/// <param name="routeValues">The route parameter values extracted from the matched route.</param>
public RouteData(Type pageType, IReadOnlyDictionary<string, object> routeValues)
{
if (pageType == null)
{
throw new ArgumentNullException(nameof(pageType));
}
if (!typeof(IComponent).IsAssignableFrom(pageType))
{
throw new ArgumentException($"The value must implement {nameof(IComponent)}.", nameof(pageType));
}
PageType = pageType;
RouteValues = routeValues ?? throw new ArgumentNullException(nameof(routeValues));
}
/// <summary>
/// Gets the type of the page matching the route.
/// </summary>
public Type PageType { get; }
/// <summary>
/// Gets route parameter values extracted from the matched route.
/// </summary>
public IReadOnlyDictionary<string, object> RouteValues { get; }
}
}

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
@ -11,12 +12,13 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Components.Routing
{
/// <summary>
/// A component that displays whichever other component corresponds to the
/// current navigation location.
/// A component that supplies route data corresponding to the current navigation state.
/// </summary>
public class Router : IComponent, IHandleAfterRender, IDisposable
{
static readonly char[] _queryOrHashStartChar = new[] { '?', '#' };
static readonly ReadOnlyDictionary<string, object> _emptyParametersDictionary
= new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
RenderHandle _renderHandle;
string _baseUri;
@ -33,25 +35,19 @@ namespace Microsoft.AspNetCore.Components.Routing
[Inject] private ILoggerFactory LoggerFactory { get; set; }
/// <summary>
/// Gets or sets the assembly that should be searched, along with its referenced
/// assemblies, for components matching the URI.
/// Gets or sets the assembly that should be searched for components matching the URI.
/// </summary>
[Parameter] public Assembly AppAssembly { get; set; }
/// <summary>
/// Gets or sets the type of the component that should be used as a fallback when no match is found for the requested route.
/// Gets or sets the content to display when no match is found for the requested route.
/// </summary>
[Parameter] public RenderFragment NotFound { get; set; }
/// <summary>
/// The content that will be displayed if the user is not authorized.
/// Gets or sets the content to display when a match is found for the requested route.
/// </summary>
[Parameter] public RenderFragment<AuthenticationState> NotAuthorized { get; set; }
/// <summary>
/// The content that will be displayed while asynchronous authorization is in progress.
/// </summary>
[Parameter] public RenderFragment Authorizing { get; set; }
[Parameter] public RenderFragment<RouteData> Found { get; set; }
private RouteTable Routes { get; set; }
@ -69,6 +65,22 @@ namespace Microsoft.AspNetCore.Components.Routing
public Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);
// Found content is mandatory, because even though we could use something like <RouteView ...> as a
// reasonable default, if it's not declared explicitly in the template then people will have no way
// to discover how to customize this (e.g., to add authorization).
if (Found == null)
{
throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(Found)}.");
}
// NotFound content is mandatory, because even though we could display a default message like "Not found",
// it has to be specified explicitly so that it can also be wrapped in a specific layout
if (NotFound == null)
{
throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(NotFound)}.");
}
Routes = RouteTableFactory.Create(AppAssembly);
Refresh(isNavigationIntercepted: false);
return Task.CompletedTask;
@ -80,7 +92,7 @@ namespace Microsoft.AspNetCore.Components.Routing
NavigationManager.LocationChanged -= OnLocationChanged;
}
private string StringUntilAny(string str, char[] chars)
private static string StringUntilAny(string str, char[] chars)
{
var firstIndex = str.IndexOfAny(chars);
return firstIndex < 0
@ -88,17 +100,6 @@ namespace Microsoft.AspNetCore.Components.Routing
: str.Substring(0, firstIndex);
}
/// <inheritdoc />
protected virtual void Render(RenderTreeBuilder builder, Type handler, IDictionary<string, object> parameters)
{
builder.OpenComponent(0, typeof(PageDisplay));
builder.AddAttribute(1, nameof(PageDisplay.Page), handler);
builder.AddAttribute(2, nameof(PageDisplay.PageParameters), parameters);
builder.AddAttribute(3, nameof(PageDisplay.NotAuthorized), NotAuthorized);
builder.AddAttribute(4, nameof(PageDisplay.Authorizing), Authorizing);
builder.CloseComponent();
}
private void Refresh(bool isNavigationIntercepted)
{
var locationPath = NavigationManager.ToBaseRelativePath(_locationAbsolute);
@ -116,16 +117,19 @@ namespace Microsoft.AspNetCore.Components.Routing
Log.NavigatingToComponent(_logger, context.Handler, locationPath, _baseUri);
_renderHandle.Render(builder => Render(builder, context.Handler, context.Parameters));
var routeData = new RouteData(
context.Handler,
context.Parameters ?? _emptyParametersDictionary);
_renderHandle.Render(Found(routeData));
}
else
{
if (!isNavigationIntercepted && NotFound != null)
if (!isNavigationIntercepted)
{
Log.DisplayingNotFound(_logger, locationPath, _baseUri);
// We did not find a Component that matches the route.
// Only show the NotFound if the application developer programatically got us here i.e we did not
// Only show the NotFound content if the application developer programatically got us here i.e we did not
// intercept the navigation. In all other cases, force a browser navigation since this could be non-Blazor content.
_renderHandle.Render(NotFound);
}

View File

@ -0,0 +1,356 @@
// 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.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Components
{
public class AuthorizeRouteViewTest
{
private readonly static IReadOnlyDictionary<string, object> EmptyParametersDictionary = new Dictionary<string, object>();
private readonly TestAuthenticationStateProvider _authenticationStateProvider;
private readonly TestRenderer _renderer;
private readonly RouteView _authorizeRouteViewComponent;
private readonly int _authorizeRouteViewComponentId;
private readonly TestAuthorizationService _testAuthorizationService;
public AuthorizeRouteViewTest()
{
_authenticationStateProvider = new TestAuthenticationStateProvider();
_authenticationStateProvider.CurrentAuthStateTask = Task.FromResult(
new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));
_testAuthorizationService = new TestAuthorizationService();
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<AuthenticationStateProvider>(_authenticationStateProvider);
serviceCollection.AddSingleton<IAuthorizationPolicyProvider, TestAuthorizationPolicyProvider>();
serviceCollection.AddSingleton<IAuthorizationService>(_testAuthorizationService);
_renderer = new TestRenderer(serviceCollection.BuildServiceProvider());
_authorizeRouteViewComponent = new AuthorizeRouteView();
_authorizeRouteViewComponentId = _renderer.AssignRootComponentId(_authorizeRouteViewComponent);
}
[Fact]
public void WhenAuthorized_RendersPageInsideLayout()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), new Dictionary<string, object>
{
{ nameof(TestPageRequiringAuthorization.Message), "Hello, world!" }
});
_testAuthorizationService.NextResult = AuthorizationResult.Success();
// Act
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
}));
// Assert: renders layout
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Component<TestPageRequiringAuthorization>(batch.ReferenceFrames[edit.ReferenceFrameIndex]);
},
edit => AssertPrependText(batch, edit, "Layout ends here"));
// Assert: renders page
var pageDiff = batch.GetComponentDiffs<TestPageRequiringAuthorization>().Single();
Assert.Collection(pageDiff.Edits,
edit => AssertPrependText(batch, edit, "Hello from the page with message: Hello, world!"));
}
[Fact]
public void WhenNotAuthorized_RendersDefaultNotAuthorizedContentInsideLayout()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
_testAuthorizationService.NextResult = AuthorizationResult.Failed();
// Act
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
}));
// Assert: renders layout containing "not authorized" message
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit => AssertPrependText(batch, edit, "Not authorized"),
edit => AssertPrependText(batch, edit, "Layout ends here"));
}
[Fact]
public void WhenNotAuthorized_RendersCustomNotAuthorizedContentInsideLayout()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
_testAuthorizationService.NextResult = AuthorizationResult.Failed();
_authenticationStateProvider.CurrentAuthStateTask = Task.FromResult(new AuthenticationState(
new ClaimsPrincipal(new TestIdentity { Name = "Bert" })));
// Act
RenderFragment<AuthenticationState> customNotAuthorized =
state => builder => builder.AddContent(0, $"Go away, {state.User.Identity.Name}");
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
{ nameof(AuthorizeRouteView.NotAuthorized), customNotAuthorized },
}));
// Assert: renders layout containing "not authorized" message
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit => AssertPrependText(batch, edit, "Go away, Bert"),
edit => AssertPrependText(batch, edit, "Layout ends here"));
}
[Fact]
public async Task WhenAuthorizing_RendersDefaultAuthorizingContentInsideLayout()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
var authStateTcs = new TaskCompletionSource<AuthenticationState>();
_authenticationStateProvider.CurrentAuthStateTask = authStateTcs.Task;
RenderFragment<AuthenticationState> customNotAuthorized =
state => builder => builder.AddContent(0, $"Go away, {state.User.Identity.Name}");
// Act
var firstRenderTask = _renderer.RenderRootComponentAsync(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
{ nameof(AuthorizeRouteView.NotAuthorized), customNotAuthorized },
}));
// Assert: renders layout containing "authorizing" message
Assert.False(firstRenderTask.IsCompleted);
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit => AssertPrependText(batch, edit, "Authorizing..."),
edit => AssertPrependText(batch, edit, "Layout ends here"));
// Act 2: updates when authorization completes
authStateTcs.SetResult(new AuthenticationState(
new ClaimsPrincipal(new TestIdentity { Name = "Bert" })));
await firstRenderTask;
// Assert 2: Only the layout is updated
batch = _renderer.Batches.Skip(1).Single();
var nonEmptyDiff = batch.DiffsInOrder.Where(d => d.Edits.Any()).Single();
Assert.Equal(layoutDiff.ComponentId, nonEmptyDiff.ComponentId);
Assert.Collection(nonEmptyDiff.Edits, edit =>
{
Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Text(batch.ReferenceFrames[edit.ReferenceFrameIndex], "Go away, Bert");
});
}
[Fact]
public void WhenAuthorizing_RendersCustomAuthorizingContentInsideLayout()
{
// Arrange
var routeData = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
var authStateTcs = new TaskCompletionSource<AuthenticationState>();
_authenticationStateProvider.CurrentAuthStateTask = authStateTcs.Task;
RenderFragment customAuthorizing =
builder => builder.AddContent(0, "Hold on, we're checking your papers.");
// Act
var firstRenderTask = _renderer.RenderRootComponentAsync(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
{ nameof(AuthorizeRouteView.Authorizing), customAuthorizing },
}));
// Assert: renders layout containing "authorizing" message
Assert.False(firstRenderTask.IsCompleted);
var batch = _renderer.Batches.Single();
var layoutDiff = batch.GetComponentDiffs<TestLayout>().Single();
Assert.Collection(layoutDiff.Edits,
edit => AssertPrependText(batch, edit, "Layout starts here"),
edit => AssertPrependText(batch, edit, "Hold on, we're checking your papers."),
edit => AssertPrependText(batch, edit, "Layout ends here"));
}
[Fact]
public void WithoutCascadedAuthenticationState_WrapsOutputInCascadingAuthenticationState()
{
// Arrange/Act
var routeData = new RouteData(typeof(TestPageWithNoAuthorization), EmptyParametersDictionary);
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData }
}));
// Assert
var batch = _renderer.Batches.Single();
var componentInstances = batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component);
Assert.Collection(componentInstances,
// This is the hierarchy inside the AuthorizeRouteView, which contains its
// own CascadingAuthenticationState
component => Assert.IsType<CascadingAuthenticationState>(component),
component => Assert.IsType<CascadingValue<Task<AuthenticationState>>>(component),
component => Assert.IsAssignableFrom<AuthorizeViewCore>(component),
component => Assert.IsType<LayoutView>(component),
component => Assert.IsType<TestPageWithNoAuthorization>(component));
}
[Fact]
public void WithCascadedAuthenticationState_DoesNotWrapOutputInCascadingAuthenticationState()
{
// Arrange
var routeData = new RouteData(typeof(TestPageWithNoAuthorization), EmptyParametersDictionary);
var rootComponent = new AuthorizeRouteViewWithExistingCascadedAuthenticationState(
_authenticationStateProvider.CurrentAuthStateTask,
routeData);
var rootComponentId = _renderer.AssignRootComponentId(rootComponent);
// Act
_renderer.RenderRootComponent(rootComponentId);
// Assert
var batch = _renderer.Batches.Single();
var componentInstances = batch.ReferenceFrames
.Where(f => f.FrameType == RenderTreeFrameType.Component)
.Select(f => f.Component);
Assert.Collection(componentInstances,
// This is the externally-supplied cascading value
component => Assert.IsType<CascadingValue<Task<AuthenticationState>>>(component),
component => Assert.IsType<AuthorizeRouteView>(component),
// This is the hierarchy inside the AuthorizeRouteView. It doesn't contain a
// further CascadingAuthenticationState
component => Assert.IsAssignableFrom<AuthorizeViewCore>(component),
component => Assert.IsType<LayoutView>(component),
component => Assert.IsType<TestPageWithNoAuthorization>(component));
}
[Fact]
public void UpdatesOutputWhenRouteDataChanges()
{
// Arrange/Act 1: Start on some route
// Not asserting about the initial output, as that is covered by other tests
var routeData = new RouteData(typeof(TestPageWithNoAuthorization), EmptyParametersDictionary);
_renderer.RenderRootComponent(_authorizeRouteViewComponentId, ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData },
{ nameof(AuthorizeRouteView.DefaultLayout), typeof(TestLayout) },
}));
// Act 2: Move to another route
var routeData2 = new RouteData(typeof(TestPageRequiringAuthorization), EmptyParametersDictionary);
var render2Task = _renderer.Dispatcher.InvokeAsync(() => _authorizeRouteViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(AuthorizeRouteView.RouteData), routeData2 },
})));
// Assert: we retain the layout instance, and mutate its contents
Assert.True(render2Task.IsCompletedSuccessfully);
Assert.Equal(2, _renderer.Batches.Count);
var batch2 = _renderer.Batches[1];
var diff = batch2.DiffsInOrder.Where(d => d.Edits.Any()).Single();
Assert.Collection(diff.Edits,
edit =>
{
// Inside the layout, we add the new content
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Text(batch2.ReferenceFrames[edit.ReferenceFrameIndex], "Not authorized");
},
edit =>
{
// ... and remove the old content
Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
Assert.Equal(2, edit.SiblingIndex);
});
}
private static void AssertPrependText(CapturedBatch batch, RenderTreeEdit edit, string text)
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
ref var referenceFrame = ref batch.ReferenceFrames[edit.ReferenceFrameIndex];
AssertFrame.Text(referenceFrame, text);
}
class TestPageWithNoAuthorization : ComponentBase { }
[Authorize]
class TestPageRequiringAuthorization : ComponentBase
{
[Parameter] public string Message { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, $"Hello from the page with message: {Message}");
}
}
class TestLayout : LayoutComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "Layout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "Layout ends here");
}
}
class AuthorizeRouteViewWithExistingCascadedAuthenticationState : AutoRenderComponent
{
private readonly Task<AuthenticationState> _authenticationState;
private readonly RouteData _routeData;
public AuthorizeRouteViewWithExistingCascadedAuthenticationState(
Task<AuthenticationState> authenticationState,
RouteData routeData)
{
_authenticationState = authenticationState;
_routeData = routeData;
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenComponent<CascadingValue<Task<AuthenticationState>>>(0);
builder.AddAttribute(1, nameof(CascadingValue<object>.Value), _authenticationState);
builder.AddAttribute(2, nameof(CascadingValue<object>.ChildContent), (RenderFragment)(builder =>
{
builder.OpenComponent<AuthorizeRouteView>(0);
builder.AddAttribute(1, nameof(AuthorizeRouteView.RouteData), _routeData);
builder.CloseComponent();
}));
builder.CloseComponent();
}
}
}
}

View File

@ -2,7 +2,6 @@
// 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.Diagnostics;
using System.Linq;
using System.Security.Claims;
@ -332,15 +331,9 @@ namespace Microsoft.AspNetCore.Components
Assert.Equal(2, renderer.Batches.Count);
var batch2 = renderer.Batches[1];
var diff2 = batch2.DiffsByComponentId[authorizeViewComponentId].Single();
Assert.Collection(diff2.Edits,
edit =>
Assert.Collection(diff2.Edits, edit =>
{
Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
Assert.Equal(0, edit.SiblingIndex);
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
Assert.Equal(0, edit.SiblingIndex);
AssertFrame.Text(
batch2.ReferenceFrames[edit.ReferenceFrameIndex],
@ -514,15 +507,6 @@ namespace Microsoft.AspNetCore.Components
=> Task.FromResult(new AuthenticationState(
new ClaimsPrincipal(new TestIdentity { Name = username })));
class TestIdentity : IIdentity
{
public string AuthenticationType => "Test";
public bool IsAuthenticated => true;
public string Name { get; set; }
}
public TestRenderer CreateTestRenderer(IAuthorizationService authorizationService)
{
var serviceCollection = new ServiceCollection();
@ -531,52 +515,6 @@ namespace Microsoft.AspNetCore.Components
return new TestRenderer(serviceCollection.BuildServiceProvider());
}
private class TestAuthorizationService : IAuthorizationService
{
public AuthorizationResult NextResult { get; set; }
= AuthorizationResult.Failed();
public List<(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)> AuthorizeCalls { get; }
= new List<(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)>();
public Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
AuthorizeCalls.Add((user, resource, requirements));
// The TestAuthorizationService doesn't actually apply any authorization requirements
// It just returns the specified NextResult, since we're not trying to test the logic
// in DefaultAuthorizationService or similar here. So it's up to tests to set a desired
// NextResult and assert that the expected criteria were passed by inspecting AuthorizeCalls.
return Task.FromResult(NextResult);
}
public Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
=> throw new NotImplementedException();
}
private class TestAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
private readonly AuthorizationOptions options = new AuthorizationOptions();
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
=> Task.FromResult(options.DefaultPolicy);
public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
=> Task.FromResult(options.FallbackPolicy);
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) => Task.FromResult(
new AuthorizationPolicy(new[]
{
new TestPolicyRequirement { PolicyName = policyName }
},
new[] { $"TestScheme:{policyName}" }));
}
public class TestPolicyRequirement : IAuthorizationRequirement
{
public string PolicyName { get; set; }
}
public class AuthorizeViewCoreWithScheme : AuthorizeViewCore
{
protected override IAuthorizeData[] GetAuthorizeData()

View File

@ -38,7 +38,7 @@ namespace Microsoft.AspNetCore.Components
{
// Arrange: Service
var services = new ServiceCollection();
var authStateProvider = new TestAuthStateProvider()
var authStateProvider = new TestAuthenticationStateProvider()
{
CurrentAuthStateTask = Task.FromResult(CreateAuthenticationState("Bert"))
};
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components
// Arrange: Service
var services = new ServiceCollection();
var authStateTaskCompletionSource = new TaskCompletionSource<AuthenticationState>();
var authStateProvider = new TestAuthStateProvider()
var authStateProvider = new TestAuthenticationStateProvider()
{
CurrentAuthStateTask = authStateTaskCompletionSource.Task
};
@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.Components
{
// Arrange: Service
var services = new ServiceCollection();
var authStateProvider = new TestAuthStateProvider()
var authStateProvider = new TestAuthenticationStateProvider()
{
CurrentAuthStateTask = Task.FromResult(CreateAuthenticationState(null))
};
@ -190,21 +190,6 @@ namespace Microsoft.AspNetCore.Components
}
}
class TestAuthStateProvider : AuthenticationStateProvider
{
public Task<AuthenticationState> CurrentAuthStateTask { get; set; }
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return CurrentAuthStateTask;
}
internal void TriggerAuthenticationStateChanged(Task<AuthenticationState> authState)
{
NotifyAuthenticationStateChanged(authState);
}
}
public static AuthenticationState CreateAuthenticationState(string username)
=> new AuthenticationState(new ClaimsPrincipal(username == null
? new ClaimsIdentity()

View File

@ -0,0 +1,22 @@
// 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.Threading.Tasks;
namespace Microsoft.AspNetCore.Components
{
public class TestAuthenticationStateProvider : AuthenticationStateProvider
{
public Task<AuthenticationState> CurrentAuthStateTask { get; set; }
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return CurrentAuthStateTask;
}
internal void TriggerAuthenticationStateChanged(Task<AuthenticationState> authState)
{
NotifyAuthenticationStateChanged(authState);
}
}
}

View File

@ -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.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace Microsoft.AspNetCore.Components
{
public class TestAuthorizationPolicyProvider : IAuthorizationPolicyProvider
{
private readonly AuthorizationOptions options = new AuthorizationOptions();
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
=> Task.FromResult(options.DefaultPolicy);
public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
=> Task.FromResult(options.FallbackPolicy);
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName) => Task.FromResult(
new AuthorizationPolicy(new[]
{
new TestPolicyRequirement { PolicyName = policyName }
},
new[] { $"TestScheme:{policyName}" }));
}
public class TestPolicyRequirement : IAuthorizationRequirement
{
public string PolicyName { get; set; }
}
}

View File

@ -0,0 +1,34 @@
// 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.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace Microsoft.AspNetCore.Components
{
public class TestAuthorizationService : IAuthorizationService
{
public AuthorizationResult NextResult { get; set; }
= AuthorizationResult.Failed();
public List<(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)> AuthorizeCalls { get; }
= new List<(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)>();
public Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
AuthorizeCalls.Add((user, resource, requirements));
// The TestAuthorizationService doesn't actually apply any authorization requirements
// It just returns the specified NextResult, since we're not trying to test the logic
// in DefaultAuthorizationService or similar here. So it's up to tests to set a desired
// NextResult and assert that the expected criteria were passed by inspecting AuthorizeCalls.
return Task.FromResult(NextResult);
}
public Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName)
=> throw new NotImplementedException();
}
}

View File

@ -0,0 +1,16 @@
// 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.Security.Principal;
namespace Microsoft.AspNetCore.Components
{
public class TestIdentity : IIdentity
{
public string AuthenticationType => "Test";
public bool IsAuthenticated => true;
public string Name { get; set; }
}
}

View File

@ -0,0 +1,326 @@
// 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.Linq;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test
{
public class LayoutViewTest
{
private readonly TestRenderer _renderer;
private readonly LayoutView _layoutViewComponent;
private readonly int _layoutViewComponentId;
public LayoutViewTest()
{
_renderer = new TestRenderer();
_layoutViewComponent = new LayoutView();
_layoutViewComponentId = _renderer.AssignRootComponentId(_layoutViewComponent);
}
[Fact]
public void GivenNoParameters_RendersNothing()
{
// Arrange/Act
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.Empty));
Assert.True(setParametersTask.IsCompletedSuccessfully);
var frames = _renderer.GetCurrentRenderTreeFrames(_layoutViewComponentId).AsEnumerable();
// Assert
Assert.Single(_renderer.Batches);
Assert.Empty(frames);
}
[Fact]
public void GivenContentButNoLayout_RendersContent()
{
// Arrange/Act
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(123, "Hello");
builder.AddContent(456, "Goodbye");
})}
})));
Assert.True(setParametersTask.IsCompletedSuccessfully);
var frames = _renderer.GetCurrentRenderTreeFrames(_layoutViewComponentId).AsEnumerable();
// Assert
Assert.Single(_renderer.Batches);
Assert.Collection(frames,
frame => AssertFrame.Text(frame, "Hello", 123),
frame => AssertFrame.Text(frame, "Goodbye", 456));
}
[Fact]
public void GivenLayoutButNoContent_RendersLayoutWithEmptyBody()
{
// Arrange/Act
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(RootLayout) }
})));
// Assert
Assert.True(setParametersTask.IsCompletedSuccessfully);
var batch = _renderer.Batches.Single();
var layoutViewFrames = _renderer.GetCurrentRenderTreeFrames(_layoutViewComponentId).AsEnumerable();
Assert.Collection(layoutViewFrames,
frame => AssertFrame.Component<RootLayout>(frame, subtreeLength: 2, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutComponentBase.Body), sequence: 1));
var rootLayoutComponentId = batch.GetComponentFrames<RootLayout>().Single().ComponentId;
var rootLayoutFrames = _renderer.GetCurrentRenderTreeFrames(rootLayoutComponentId).AsEnumerable();
Assert.Collection(rootLayoutFrames,
frame => AssertFrame.Text(frame, "RootLayout starts here", sequence: 0),
frame => AssertFrame.Region(frame, subtreeLength: 1), // i.e., empty region
frame => AssertFrame.Text(frame, "RootLayout ends here", sequence: 2));
}
[Fact]
public void RendersContentInsideLayout()
{
// Arrange/Act
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(RootLayout) },
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(123, "Hello");
builder.AddContent(456, "Goodbye");
})}
})));
// Assert
Assert.True(setParametersTask.IsCompletedSuccessfully);
var batch = _renderer.Batches.Single();
var layoutViewFrames = _renderer.GetCurrentRenderTreeFrames(_layoutViewComponentId).AsEnumerable();
Assert.Collection(layoutViewFrames,
frame => AssertFrame.Component<RootLayout>(frame, subtreeLength: 2, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutComponentBase.Body), sequence: 1));
var rootLayoutComponentId = batch.GetComponentFrames<RootLayout>().Single().ComponentId;
var rootLayoutFrames = _renderer.GetCurrentRenderTreeFrames(rootLayoutComponentId).AsEnumerable();
Assert.Collection(rootLayoutFrames,
frame => AssertFrame.Text(frame, "RootLayout starts here", sequence: 0),
frame => AssertFrame.Region(frame, subtreeLength: 3),
frame => AssertFrame.Text(frame, "Hello", sequence: 123),
frame => AssertFrame.Text(frame, "Goodbye", sequence: 456),
frame => AssertFrame.Text(frame, "RootLayout ends here", sequence: 2));
}
[Fact]
public void RendersContentInsideNestedLayout()
{
// Arrange/Act
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(NestedLayout) },
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(123, "Hello");
builder.AddContent(456, "Goodbye");
})}
})));
// Assert
Assert.True(setParametersTask.IsCompletedSuccessfully);
var batch = _renderer.Batches.Single();
var layoutViewFrames = _renderer.GetCurrentRenderTreeFrames(_layoutViewComponentId).AsEnumerable();
Assert.Collection(layoutViewFrames,
frame => AssertFrame.Component<RootLayout>(frame, subtreeLength: 2, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutComponentBase.Body), sequence: 1));
var rootLayoutComponentId = batch.GetComponentFrames<RootLayout>().Single().ComponentId;
var rootLayoutFrames = _renderer.GetCurrentRenderTreeFrames(rootLayoutComponentId).AsEnumerable();
Assert.Collection(rootLayoutFrames,
frame => AssertFrame.Text(frame, "RootLayout starts here", sequence: 0),
frame => AssertFrame.Region(frame, subtreeLength: 3, sequence: 1),
frame => AssertFrame.Component<NestedLayout>(frame, subtreeLength: 2, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutComponentBase.Body), sequence: 1),
frame => AssertFrame.Text(frame, "RootLayout ends here", sequence: 2));
var nestedLayoutComponentId = batch.GetComponentFrames<NestedLayout>().Single().ComponentId;
var nestedLayoutFrames = _renderer.GetCurrentRenderTreeFrames(nestedLayoutComponentId).AsEnumerable();
Assert.Collection(nestedLayoutFrames,
frame => AssertFrame.Text(frame, "NestedLayout starts here", sequence: 0),
frame => AssertFrame.Region(frame, subtreeLength: 3, sequence: 1),
frame => AssertFrame.Text(frame, "Hello", sequence: 123),
frame => AssertFrame.Text(frame, "Goodbye", sequence: 456),
frame => AssertFrame.Text(frame, "NestedLayout ends here", sequence: 2));
}
[Fact]
public void CanChangeContentWithSameLayout()
{
// Arrange
var setParametersTask = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(NestedLayout) },
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(0, "Initial content");
})}
})));
// Act
Assert.True(setParametersTask.IsCompletedSuccessfully);
_renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(NestedLayout) },
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(0, "Changed content");
})}
})));
// Assert
Assert.Equal(2, _renderer.Batches.Count);
var batch = _renderer.Batches[1];
Assert.Equal(0, batch.DisposedComponentIDs.Count);
Assert.Collection(batch.DiffsInOrder,
diff => Assert.Empty(diff.Edits), // LayoutView rerendered, but with no changes
diff => Assert.Empty(diff.Edits), // RootLayout rerendered, but with no changes
diff =>
{
// NestedLayout rerendered, patching content in place
Assert.Collection(diff.Edits, edit =>
{
Assert.Equal(RenderTreeEditType.UpdateText, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"Changed content",
sequence: 0);
});
});
}
[Fact]
public void CanChangeLayout()
{
// Arrange
var setParametersTask1 = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(NestedLayout) },
{ nameof(LayoutView.ChildContent), (RenderFragment)(builder => {
builder.AddContent(0, "Some content");
})}
})));
Assert.True(setParametersTask1.IsCompletedSuccessfully);
// Act
var setParametersTask2 = _renderer.Dispatcher.InvokeAsync(() => _layoutViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(LayoutView.Layout), typeof(OtherNestedLayout) },
})));
// Assert
Assert.True(setParametersTask2.IsCompletedSuccessfully);
Assert.Equal(2, _renderer.Batches.Count);
var batch = _renderer.Batches[1];
Assert.Equal(1, batch.DisposedComponentIDs.Count); // Disposes NestedLayout
Assert.Collection(batch.DiffsInOrder,
diff => Assert.Empty(diff.Edits), // LayoutView rerendered, but with no changes
diff =>
{
// RootLayout rerendered, changing child
Assert.Collection(diff.Edits,
edit =>
{
Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Component<OtherNestedLayout>(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
sequence: 0);
});
},
diff =>
{
// Inserts new OtherNestedLayout
Assert.Collection(diff.Edits,
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(0, edit.SiblingIndex);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"OtherNestedLayout starts here");
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"Some content");
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(2, edit.SiblingIndex);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"OtherNestedLayout ends here");
});
});
}
private class RootLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (Body == null)
{
// Prove that we don't expect layouts to tolerate null values for Body
throw new InvalidOperationException("Got a null body when not expecting it");
}
builder.AddContent(0, "RootLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "RootLayout ends here");
}
}
[Layout(typeof(RootLayout))]
private class NestedLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "NestedLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "NestedLayout ends here");
}
}
[Layout(typeof(RootLayout))]
private class OtherNestedLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "OtherNestedLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "OtherNestedLayout ends here");
}
}
}
}

View File

@ -1,269 +0,0 @@
// 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 Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test
{
public class PageDisplayTest
{
private TestRenderer _renderer = new TestRenderer();
private PageDisplay _pageDisplayComponent = new PageDisplay();
private int _pageDisplayComponentId;
public PageDisplayTest()
{
_renderer = new TestRenderer();
_pageDisplayComponent = new PageDisplay();
_pageDisplayComponentId = _renderer.AssignRootComponentId(_pageDisplayComponent);
}
[Fact]
public void DisplaysComponentInsideLayout()
{
// Arrange/Act
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(ComponentWithLayout) }
})));
// Assert
var batch = _renderer.Batches.Single();
Assert.Collection(batch.DiffsInOrder,
diff =>
{
// First is the LayoutDisplay component, which contains a RootLayout
var singleEdit = diff.Edits.Single();
Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type);
AssertFrame.Component<RootLayout>(
batch.ReferenceFrames[singleEdit.ReferenceFrameIndex]);
},
diff =>
{
// ... then a RootLayout which contains a ComponentWithLayout
// First is the LayoutDisplay component, which contains a RootLayout
Assert.Collection(diff.Edits,
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"RootLayout starts here");
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Component<ComponentWithLayout>(
batch.ReferenceFrames[edit.ReferenceFrameIndex]);
},
edit =>
{
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Text(
batch.ReferenceFrames[edit.ReferenceFrameIndex],
"RootLayout ends here");
});
},
diff =>
{
// ... then the ComponentWithLayout
var singleEdit = diff.Edits.Single();
Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type);
AssertFrame.Text(
batch.ReferenceFrames[singleEdit.ReferenceFrameIndex],
$"{nameof(ComponentWithLayout)} is here.");
});
}
[Fact]
public void DisplaysComponentInsideNestedLayout()
{
// Arrange/Act
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(ComponentWithNestedLayout) }
})));
// Assert
var batch = _renderer.Batches.Single();
Assert.Collection(batch.DiffsInOrder,
// First, a LayoutDisplay containing a RootLayout
diff => AssertFrame.Component<RootLayout>(
batch.ReferenceFrames[diff.Edits[0].ReferenceFrameIndex]),
// Then a RootLayout containing a NestedLayout
diff => AssertFrame.Component<NestedLayout>(
batch.ReferenceFrames[diff.Edits[1].ReferenceFrameIndex]),
// Then a NestedLayout containing a ComponentWithNestedLayout
diff => AssertFrame.Component<ComponentWithNestedLayout>(
batch.ReferenceFrames[diff.Edits[1].ReferenceFrameIndex]),
// Then the ComponentWithNestedLayout
diff => AssertFrame.Text(
batch.ReferenceFrames[diff.Edits[0].ReferenceFrameIndex],
$"{nameof(ComponentWithNestedLayout)} is here."));
}
[Fact]
public void CanChangeDisplayedPageWithSameLayout()
{
// Arrange
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(ComponentWithLayout) }
})));
// Act
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(DifferentComponentWithLayout) }
})));
// Assert
Assert.Equal(2, _renderer.Batches.Count);
var batch = _renderer.Batches[1];
Assert.Equal(1, batch.DisposedComponentIDs.Count); // Disposed only the inner page component
Assert.Collection(batch.DiffsInOrder,
diff => Assert.Empty(diff.Edits), // LayoutDisplay rerendered, but with no changes
diff =>
{
// RootLayout rerendered
Assert.Collection(diff.Edits,
edit =>
{
// Removed old page
Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
},
edit =>
{
// Inserted new one
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Component<DifferentComponentWithLayout>(
batch.ReferenceFrames[edit.ReferenceFrameIndex]);
});
},
diff =>
{
// New page rendered
var singleEdit = diff.Edits.Single();
Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type);
AssertFrame.Text(
batch.ReferenceFrames[singleEdit.ReferenceFrameIndex],
$"{nameof(DifferentComponentWithLayout)} is here.");
});
}
[Fact]
public void CanChangeDisplayedPageWithDifferentLayout()
{
// Arrange
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(ComponentWithLayout) }
})));
// Act
_renderer.Dispatcher.InvokeAsync(() => _pageDisplayComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(PageDisplay.Page), typeof(ComponentWithNestedLayout) }
})));
// Assert
Assert.Equal(2, _renderer.Batches.Count);
var batch = _renderer.Batches[1];
Assert.Equal(1, batch.DisposedComponentIDs.Count); // Disposed only the inner page component
Assert.Collection(batch.DiffsInOrder,
diff => Assert.Empty(diff.Edits), // LayoutDisplay rerendered, but with no changes
diff =>
{
// RootLayout rerendered
Assert.Collection(diff.Edits,
edit =>
{
// Removed old page
Assert.Equal(RenderTreeEditType.RemoveFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
},
edit =>
{
// Inserted new nested layout
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
Assert.Equal(1, edit.SiblingIndex);
AssertFrame.Component<NestedLayout>(
batch.ReferenceFrames[edit.ReferenceFrameIndex]);
});
},
diff =>
{
// New nested layout rendered
var edit = diff.Edits[1];
Assert.Equal(RenderTreeEditType.PrependFrame, edit.Type);
AssertFrame.Component<ComponentWithNestedLayout>(
batch.ReferenceFrames[edit.ReferenceFrameIndex]);
},
diff =>
{
// New inner page rendered
var singleEdit = diff.Edits.Single();
Assert.Equal(RenderTreeEditType.PrependFrame, singleEdit.Type);
AssertFrame.Text(
batch.ReferenceFrames[singleEdit.ReferenceFrameIndex],
$"{nameof(ComponentWithNestedLayout)} is here.");
});
}
private class RootLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "RootLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "RootLayout ends here");
}
}
[Layout(typeof(RootLayout))]
private class NestedLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "NestedLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "NestedLayout ends here");
}
}
[Layout(typeof(RootLayout))]
private class ComponentWithLayout : AutoRenderComponent
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
=> builder.AddContent(0, $"{nameof(ComponentWithLayout)} is here.");
}
[Layout(typeof(RootLayout))]
private class DifferentComponentWithLayout : AutoRenderComponent
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
=> builder.AddContent(0, $"{nameof(DifferentComponentWithLayout)} is here.");
}
[Layout(typeof(NestedLayout))]
private class ComponentWithNestedLayout : AutoRenderComponent
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
=> builder.AddContent(0, $"{nameof(ComponentWithNestedLayout)} is here.");
}
}
}

View File

@ -5,10 +5,8 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
namespace Microsoft.AspNetCore.Components
@ -97,23 +95,58 @@ namespace Microsoft.AspNetCore.Components
}
[Fact]
public void IncomingParameterMatchesNoDeclaredParameter_Throws()
public void IncomingCascadingValueMatchesCascadingParameter_SetsValue()
{
// Arrange
var target = new HasPropertyWithoutParameterAttribute();
var parameters = new ParameterViewBuilder
{
{ "AnyOtherKey", 123 },
}.Build();
var builder = new ParameterViewBuilder();
builder.Add(nameof(HasCascadingParameter.Cascading), "hi", cascading: true);
var parameters = builder.Build();
var target = new HasCascadingParameter();
// Act
var ex = Assert.Throws<InvalidOperationException>(
() => parameters.SetParameterProperties(target));
parameters.SetParameterProperties(target);
// Assert
Assert.Equal("hi", target.Cascading);
}
[Fact]
public void NoIncomingCascadingValueMatchesDeclaredCascadingParameter_LeavesValueUnchanged()
{
// Arrange
var builder = new ParameterViewBuilder();
var parameters = builder.Build();
var target = new HasCascadingParameter()
{
Cascading = "bye",
};
// Act
parameters.SetParameterProperties(target);
// Assert
Assert.Equal("bye", target.Cascading);
}
[Fact]
public void IncomingCascadingValueMatchesNoDeclaredParameter_Throws()
{
// Arrange
var builder = new ParameterViewBuilder();
builder.Add("SomethingElse", "hi", cascading: true);
var parameters = builder.Build();
var target = new HasCascadingParameter();
// Act
var ex = Assert.Throws<InvalidOperationException>(() => parameters.SetParameterProperties(target));
// Assert
Assert.Equal(
$"Object of type '{typeof(HasPropertyWithoutParameterAttribute).FullName}' does not have a property " +
$"matching the name 'AnyOtherKey'.",
$"Object of type '{typeof(HasCascadingParameter).FullName}' does not have a property " +
$"matching the name 'SomethingElse'.",
ex.Message);
}
@ -139,6 +172,45 @@ namespace Microsoft.AspNetCore.Components
ex.Message);
}
[Fact]
public void IncomingNonCascadingValueMatchesCascadingParameter_Throws()
{
// Arrange
var target = new HasCascadingParameter();
var parameters = new ParameterViewBuilder
{
{ nameof(HasCascadingParameter.Cascading), 123 },
}.Build();
// Act
var ex = Assert.Throws<InvalidOperationException>(() => parameters.SetParameterProperties(target));
// Assert
Assert.Equal(
$"Object of type '{typeof(HasCascadingParameter).FullName}' has a property matching the name '{nameof(HasCascadingParameter.Cascading)}', " +
$"but it does not have [{nameof(ParameterAttribute)}] applied.",
ex.Message);
}
[Fact]
public void IncomingCascadingValueMatchesNonCascadingParameter_Throws()
{
// Arrange
var target = new HasInstanceProperties();
var builder = new ParameterViewBuilder();
builder.Add(nameof(HasInstanceProperties.IntProp), 16, cascading: true);
var parameters = builder.Build();
// Act
var ex = Assert.Throws<InvalidOperationException>(() => parameters.SetParameterProperties(target));
// Assert
Assert.Equal(
$"The property '{nameof(HasInstanceProperties.IntProp)}' on component type '{typeof(HasInstanceProperties).FullName}' " +
$"cannot be set using a cascading value.",
ex.Message);
}
[Fact]
public void SettingCaptureUnmatchedValuesParameterExplicitlyWorks()
{
@ -275,6 +347,51 @@ namespace Microsoft.AspNetCore.Components
ex.Message);
}
[Fact]
public void IncomingNonCascadingValueMatchesCascadingParameter_WithCaptureUnmatchedValues_DoesNotThrow()
{
// Arrange
var target = new HasCaptureUnmatchedValuesPropertyAndCascadingParameter()
{
Cascading = "bye",
};
var parameters = new ParameterViewBuilder
{
{ nameof(HasCaptureUnmatchedValuesPropertyAndCascadingParameter.Cascading), "hi" },
}.Build();
// Act
parameters.SetParameterProperties(target);
Assert.Collection(
target.CaptureUnmatchedValues,
kvp =>
{
Assert.Equal(nameof(HasCaptureUnmatchedValuesPropertyAndCascadingParameter.Cascading), kvp.Key);
Assert.Equal("hi", kvp.Value);
});
Assert.Equal("bye", target.Cascading);
}
[Fact]
public void IncomingCascadingValueMatchesNonCascadingParameter_WithCaptureUnmatchedValues_Throws()
{
// Arrange
var target = new HasCaptureUnmatchedValuesProperty();
var builder = new ParameterViewBuilder();
builder.Add(nameof(HasInstanceProperties.IntProp), 16, cascading: true);
var parameters = builder.Build();
// Act
var ex = Assert.Throws<InvalidOperationException>(() => parameters.SetParameterProperties(target));
// Assert
Assert.Equal(
$"The property '{nameof(HasCaptureUnmatchedValuesProperty.IntProp)}' on component type '{typeof(HasCaptureUnmatchedValuesProperty).FullName}' " +
$"cannot be set using a cascading value.",
ex.Message);
}
[Fact]
public void IncomingParameterValueMismatchesDeclaredParameterType_Throws()
{
@ -397,6 +514,11 @@ namespace Microsoft.AspNetCore.Components
}
}
class HasCascadingParameter
{
[CascadingParameter] public string Cascading { get; set; }
}
class HasPropertyWithoutParameterAttribute
{
internal int IntProp { get; set; }
@ -436,6 +558,12 @@ namespace Microsoft.AspNetCore.Components
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> CaptureUnmatchedValues { get; set; }
}
class HasCaptureUnmatchedValuesPropertyAndCascadingParameter
{
[CascadingParameter] public string Cascading { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> CaptureUnmatchedValues { get; set; }
}
class HasDupliateCaptureUnmatchedValuesProperty
{
[Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object> CaptureUnmatchedValuesProp1 { get; set; }
@ -449,11 +577,11 @@ namespace Microsoft.AspNetCore.Components
class ParameterViewBuilder : IEnumerable
{
private readonly List<(string Name, object Value)> _keyValuePairs
= new List<(string, object)>();
private readonly List<(string Name, object Value, bool Cascading)> _keyValuePairs
= new List<(string, object, bool)>();
public void Add(string name, object value)
=> _keyValuePairs.Add((name, value));
public void Add(string name, object value, bool cascading = false)
=> _keyValuePairs.Add((name, value, cascading));
public IEnumerator GetEnumerator()
=> throw new NotImplementedException();
@ -461,13 +589,56 @@ namespace Microsoft.AspNetCore.Components
public ParameterView Build()
{
var builder = new RenderTreeBuilder();
builder.OpenComponent<FakeComponent>(0);
foreach (var kvp in _keyValuePairs)
{
builder.AddAttribute(1, kvp.Name, kvp.Value);
if (!kvp.Cascading)
{
builder.AddAttribute(1, kvp.Name, kvp.Value);
}
}
builder.CloseComponent();
return new ParameterView(builder.GetFrames().Array, ownerIndex: 0);
var view = new ParameterView(builder.GetFrames().Array, ownerIndex: 0);
var cascadingParameters = new List<CascadingParameterState>();
foreach (var kvp in _keyValuePairs)
{
if (kvp.Cascading)
{
cascadingParameters.Add(new CascadingParameterState(kvp.Name, new TestCascadingValueProvider(kvp.Value)));
}
}
return view.WithCascadingParameters(cascadingParameters);
}
}
private class TestCascadingValueProvider : ICascadingValueComponent
{
public TestCascadingValueProvider(object value)
{
CurrentValue = value;
}
public object CurrentValue { get; }
public bool CurrentValueIsFixed => throw new NotImplementedException();
public bool CanSupplyValue(Type valueType, string valueName)
{
throw new NotImplementedException();
}
public void Subscribe(ComponentState subscriber)
{
throw new NotImplementedException();
}
public void Unsubscribe(ComponentState subscriber)
{
throw new NotImplementedException();
}
}
}

View File

@ -3395,6 +3395,27 @@ namespace Microsoft.AspNetCore.Components.Test
}
}
[Fact]
public void CannotStartOverlappingBatches()
{
// Arrange
var renderer = new InvalidRecursiveRenderer();
var component = new CallbackOnRenderComponent(() =>
{
// The renderer disallows one batch to be started inside another, because that
// would violate all kinds of state tracking invariants. It's not something that
// would ever happen except if you subclass the renderer and do something unsupported
// that commences batches from inside each other.
renderer.ProcessPendingRender();
});
var componentId = renderer.AssignRootComponentId(component);
// Act/Assert
var ex = Assert.Throws<InvalidOperationException>(
() => renderer.RenderRootComponent(componentId));
Assert.Contains("Cannot start a batch when one is already in progress.", ex.Message);
}
private class NoOpRenderer : Renderer
{
public NoOpRenderer() : base(new TestServiceProvider(), NullLoggerFactory.Instance)
@ -4109,5 +4130,24 @@ namespace Microsoft.AspNetCore.Components.Test
private class DerivedEventArgs : EventArgs
{
}
class CallbackOnRenderComponent : AutoRenderComponent
{
private readonly Action _callback;
public CallbackOnRenderComponent(Action callback)
{
_callback = callback;
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
=> _callback();
}
class InvalidRecursiveRenderer : TestRenderer
{
public new void ProcessPendingRender()
=> base.ProcessPendingRender();
}
}
}

View File

@ -0,0 +1,209 @@
// 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.Linq;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
using Xunit;
namespace Microsoft.AspNetCore.Components.Test
{
public class RouteViewTest
{
private readonly TestRenderer _renderer;
private readonly RouteView _routeViewComponent;
private readonly int _routeViewComponentId;
public RouteViewTest()
{
_renderer = new TestRenderer();
_routeViewComponent = new RouteView();
_routeViewComponentId = _renderer.AssignRootComponentId(_routeViewComponent);
}
[Fact]
public void ThrowsIfNoRouteDataSupplied()
{
var ex = Assert.Throws<InvalidOperationException>(() =>
{
// Throws synchronously, so no need to await
_ = _routeViewComponent.SetParametersAsync(ParameterView.Empty);
});
Assert.Equal($"The {nameof(RouteView)} component requires a non-null value for the parameter {nameof(RouteView.RouteData)}.", ex.Message);
}
[Fact]
public void RendersPageInsideLayoutView()
{
// Arrange
var routeParams = new Dictionary<string, object>
{
{ nameof(ComponentWithLayout.Message), "Test message" }
};
var routeData = new RouteData(typeof(ComponentWithLayout), routeParams);
// Act
_renderer.Dispatcher.InvokeAsync(() => _routeViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(RouteView.RouteData), routeData },
})));
// Assert: RouteView renders LayoutView
var batch = _renderer.Batches.Single();
var routeViewFrames = _renderer.GetCurrentRenderTreeFrames(_routeViewComponentId).AsEnumerable();
Assert.Collection(routeViewFrames,
frame => AssertFrame.Component<LayoutView>(frame, subtreeLength: 3, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.Layout), (object)typeof(TestLayout), sequence: 1),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.ChildContent), sequence: 2));
// Assert: LayoutView renders TestLayout
var layoutViewComponentId = batch.GetComponentFrames<LayoutView>().Single().ComponentId;
var layoutViewFrames = _renderer.GetCurrentRenderTreeFrames(layoutViewComponentId).AsEnumerable();
Assert.Collection(layoutViewFrames,
frame => AssertFrame.Component<TestLayout>(frame, subtreeLength: 2, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutComponentBase.Body), sequence: 1));
// Assert: TestLayout renders page
var testLayoutComponentId = batch.GetComponentFrames<TestLayout>().Single().ComponentId;
var testLayoutFrames = _renderer.GetCurrentRenderTreeFrames(testLayoutComponentId).AsEnumerable();
Assert.Collection(testLayoutFrames,
frame => AssertFrame.Text(frame, "Layout starts here", sequence: 0),
frame => AssertFrame.Region(frame, subtreeLength: 3),
frame => AssertFrame.Component<ComponentWithLayout>(frame, sequence: 0, subtreeLength: 2),
frame => AssertFrame.Attribute(frame, nameof(ComponentWithLayout.Message), "Test message", sequence: 1),
frame => AssertFrame.Text(frame, "Layout ends here", sequence: 2));
// Assert: page itself is rendered, having received parameters from the original route data
var pageComponentId = batch.GetComponentFrames<ComponentWithLayout>().Single().ComponentId;
var pageFrames = _renderer.GetCurrentRenderTreeFrames(pageComponentId).AsEnumerable();
Assert.Collection(pageFrames,
frame => AssertFrame.Text(frame, "Hello from the page with message 'Test message'", sequence: 0));
// Assert: nothing else was rendered
Assert.Equal(4, batch.DiffsInOrder.Count);
}
[Fact]
public void UsesDefaultLayoutIfNoneSetOnPage()
{
// Arrange
var routeParams = new Dictionary<string, object>();
var routeData = new RouteData(typeof(ComponentWithoutLayout), routeParams);
// Act
_renderer.Dispatcher.InvokeAsync(() => _routeViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(RouteView.RouteData), routeData },
{ nameof(RouteView.DefaultLayout), typeof(OtherLayout) },
})));
// Assert: uses default layout
// Not asserting about what else gets rendered as that's covered by other tests
var batch = _renderer.Batches.Single();
var routeViewFrames = _renderer.GetCurrentRenderTreeFrames(_routeViewComponentId).AsEnumerable();
Assert.Collection(routeViewFrames,
frame => AssertFrame.Component<LayoutView>(frame, subtreeLength: 3, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.Layout), (object)typeof(OtherLayout), sequence: 1),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.ChildContent), sequence: 2));
}
[Fact]
public void UsesNoLayoutIfNoneSetOnPageAndNoDefaultSet()
{
// Arrange
var routeParams = new Dictionary<string, object>();
var routeData = new RouteData(typeof(ComponentWithoutLayout), routeParams);
// Act
_renderer.Dispatcher.InvokeAsync(() => _routeViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(RouteView.RouteData), routeData },
})));
// Assert: uses no layout
// Not asserting about what else gets rendered as that's covered by other tests
var batch = _renderer.Batches.Single();
var routeViewFrames = _renderer.GetCurrentRenderTreeFrames(_routeViewComponentId).AsEnumerable();
Assert.Collection(routeViewFrames,
frame => AssertFrame.Component<LayoutView>(frame, subtreeLength: 3, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.Layout), (object)null, sequence: 1),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.ChildContent), sequence: 2));
}
[Fact]
public void PageLayoutSupersedesDefaultLayout()
{
// Arrange
var routeParams = new Dictionary<string, object>();
var routeData = new RouteData(typeof(ComponentWithLayout), routeParams);
// Act
_renderer.Dispatcher.InvokeAsync(() => _routeViewComponent.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object>
{
{ nameof(RouteView.RouteData), routeData },
{ nameof(RouteView.DefaultLayout), typeof(OtherLayout) },
})));
// Assert: uses layout specified by page
// Not asserting about what else gets rendered as that's covered by other tests
var batch = _renderer.Batches.Single();
var routeViewFrames = _renderer.GetCurrentRenderTreeFrames(_routeViewComponentId).AsEnumerable();
Assert.Collection(routeViewFrames,
frame => AssertFrame.Component<LayoutView>(frame, subtreeLength: 3, sequence: 0),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.Layout), (object)typeof(TestLayout), sequence: 1),
frame => AssertFrame.Attribute(frame, nameof(LayoutView.ChildContent), sequence: 2));
}
private class ComponentWithoutLayout : AutoRenderComponent
{
[Parameter] public string Message { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, $"Hello from the page with message '{Message}'");
}
}
[Layout(typeof(TestLayout))]
private class ComponentWithLayout : AutoRenderComponent
{
[Parameter] public string Message { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, $"Hello from the page with message '{Message}'");
}
}
private class TestLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "Layout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "Layout ends here");
}
}
private class OtherLayout : AutoRenderComponent
{
[Parameter]
public RenderFragment Body { get; set; }
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.AddContent(0, "OtherLayout starts here");
builder.AddContent(1, Body);
builder.AddContent(2, "OtherLayout ends here");
}
}
}
}

View File

@ -5,6 +5,16 @@
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)THIRD-PARTY-NOTICES.txt" Pack="true" PackagePath="." />
<!-- Add a project dependency without reference output assemblies to enforce build order -->
<!-- Applying workaround for https://github.com/microsoft/msbuild/issues/2661 and https://github.com/dotnet/sdk/issues/952 -->
<ProjectReference
Condition="'$(ReferenceBlazorBuildLocally)' == 'true' and '$(BuildNodeJS)' != 'false' and '$(BuildingInsideVisualStudio)' != 'true'"
Include="$(RepoRoot)src\Components\Web.JS\Microsoft.AspNetCore.Components.Web.JS.npmproj"
ReferenceOutputAssemblies="false"
SkipGetTargetFrameworkProperties="true"
UndefineProperties="TargetFramework"
Private="false" />
</ItemGroup>
<Import Project="Blazor\Build\src\ReferenceFromSource.props" Condition="'$(ReferenceBlazorBuildLocally)' == 'true'" />

Some files were not shown because too many files have changed in this diff Show More