diff --git a/eng/pipelines/templates/stages/source-build-stages.yml b/eng/pipelines/templates/stages/source-build-stages.yml index a149ecc9eef..83eae0a477a 100644 --- a/eng/pipelines/templates/stages/source-build-stages.yml +++ b/eng/pipelines/templates/stages/source-build-stages.yml @@ -121,6 +121,8 @@ stages: withPreviousSDK: false # 🚫 disableSigning: true # ✅ createSourceArtifacts: false # 🚫 + ${{ if in(parameters.scope, 'lite') }}: + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true - ${{ if containsValue(parameters.verifications, 'source-build-stage2') }}: @@ -136,6 +138,8 @@ stages: disableSigning: true # ✅ createSourceArtifacts: false # 🚫 reuseBuildArtifactsFrom: 'SB_{0}_Online_MsftSdk_x64' + ${{ if in(parameters.scope, 'lite') }}: + extraProperties: /p:DotNetSkipTestsRequiringMicrosoftArtifacts=true ### Additional legs for full build ### - ${{ if in(parameters.scope, 'full') }}: diff --git a/src/arcade/Arcade.slnx b/src/arcade/Arcade.slnx index a4c51eaafd6..337ee9c2279 100644 --- a/src/arcade/Arcade.slnx +++ b/src/arcade/Arcade.slnx @@ -29,6 +29,7 @@ + diff --git a/src/arcade/eng/Version.Details.xml b/src/arcade/eng/Version.Details.xml index 8e4ff3e2e9b..1a9d65f0b49 100644 --- a/src/arcade/eng/Version.Details.xml +++ b/src/arcade/eng/Version.Details.xml @@ -1,6 +1,6 @@ - + diff --git a/src/arcade/eng/common/SetupNugetSources.ps1 b/src/arcade/eng/common/SetupNugetSources.ps1 index 9445c314325..fc8d618014e 100644 --- a/src/arcade/eng/common/SetupNugetSources.ps1 +++ b/src/arcade/eng/common/SetupNugetSources.ps1 @@ -7,7 +7,7 @@ # See example call for this script below. # # - task: PowerShell@2 -# displayName: Setup Private Feeds Credentials +# displayName: Setup internal Feeds Credentials # condition: eq(variables['Agent.OS'], 'Windows_NT') # inputs: # filePath: $(System.DefaultWorkingDirectory)/eng/common/SetupNugetSources.ps1 @@ -34,19 +34,28 @@ Set-StrictMode -Version 2.0 . $PSScriptRoot\tools.ps1 +# Adds or enables the package source with the given name +function AddOrEnablePackageSource($sources, $disabledPackageSources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { + if ($disabledPackageSources -eq $null -or -not (EnableInternalPackageSource -DisabledPackageSources $disabledPackageSources -Creds $creds -PackageSourceName $SourceName)) { + AddPackageSource -Sources $sources -SourceName $SourceName -SourceEndPoint $SourceEndPoint -Creds $creds -Username $userName -pwd $Password + } +} + # Add source entry to PackageSources function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) { $packageSource = $sources.SelectSingleNode("add[@key='$SourceName']") if ($packageSource -eq $null) { + Write-Host "Adding package source $SourceName" + $packageSource = $doc.CreateElement("add") $packageSource.SetAttribute("key", $SourceName) $packageSource.SetAttribute("value", $SourceEndPoint) $sources.AppendChild($packageSource) | Out-Null } else { - Write-Host "Package source $SourceName already present." + Write-Host "Package source $SourceName already present and enabled." } AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd @@ -59,6 +68,8 @@ function AddCredential($creds, $source, $username, $pwd) { return; } + Write-Host "Inserting credential for feed: " $source + # Looks for credential configuration for the given SourceName. Create it if none is found. $sourceElement = $creds.SelectSingleNode($Source) if ($sourceElement -eq $null) @@ -91,24 +102,27 @@ function AddCredential($creds, $source, $username, $pwd) { $passwordElement.SetAttribute("value", $pwd) } -function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $pwd) { - $maestroPrivateSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]") - - Write-Host "Inserting credentials for $($maestroPrivateSources.Count) Maestro's private feeds." - - ForEach ($PackageSource in $maestroPrivateSources) { - Write-Host "`tInserting credential for Maestro's feed:" $PackageSource.Key - AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -pwd $pwd +# Enable all darc-int package sources. +function EnableMaestroInternalPackageSources($DisabledPackageSources, $Creds) { + $maestroInternalSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") + ForEach ($DisabledPackageSource in $maestroInternalSources) { + EnableInternalPackageSource -DisabledPackageSources $DisabledPackageSources -Creds $Creds -PackageSourceName $DisabledPackageSource.key } } -function EnablePrivatePackageSources($DisabledPackageSources) { - $maestroPrivateSources = $DisabledPackageSources.SelectNodes("add[contains(@key,'darc-int')]") - ForEach ($DisabledPackageSource in $maestroPrivateSources) { - Write-Host "`tEnsuring private source '$($DisabledPackageSource.key)' is enabled by deleting it from disabledPackageSource" +# Enables an internal package source by name, if found. Returns true if the package source was found and enabled, false otherwise. +function EnableInternalPackageSource($DisabledPackageSources, $Creds, $PackageSourceName) { + $DisabledPackageSource = $DisabledPackageSources.SelectSingleNode("add[@key='$PackageSourceName']") + if ($DisabledPackageSource) { + Write-Host "Enabling internal source '$($DisabledPackageSource.key)'." + # Due to https://github.com/NuGet/Home/issues/10291, we must actually remove the disabled entries $DisabledPackageSources.RemoveChild($DisabledPackageSource) + + AddCredential -Creds $creds -Source $DisabledPackageSource.Key -Username $userName -pwd $Password + return $true } + return $false } if (!(Test-Path $ConfigFile -PathType Leaf)) { @@ -121,15 +135,17 @@ $doc = New-Object System.Xml.XmlDocument $filename = (Get-Item $ConfigFile).FullName $doc.Load($filename) -# Get reference to or create one if none exist already +# Get reference to - fail if none exist $sources = $doc.DocumentElement.SelectSingleNode("packageSources") if ($sources -eq $null) { - $sources = $doc.CreateElement("packageSources") - $doc.DocumentElement.AppendChild($sources) | Out-Null + Write-PipelineTelemetryError -Category 'Build' -Message "Eng/common/SetupNugetSources.ps1 returned a non-zero exit code. NuGet config file must contain a packageSources section: $ConfigFile" + ExitWithExitCode 1 } $creds = $null +$feedSuffix = "v3/index.json" if ($Password) { + $feedSuffix = "v2" # Looks for a node. Create it if none is found. $creds = $doc.DocumentElement.SelectSingleNode("packageSourceCredentials") if ($creds -eq $null) { @@ -138,33 +154,22 @@ if ($Password) { } } +$userName = "dn-bot" + # Check for disabledPackageSources; we'll enable any darc-int ones we find there $disabledSources = $doc.DocumentElement.SelectSingleNode("disabledPackageSources") if ($disabledSources -ne $null) { Write-Host "Checking for any darc-int disabled package sources in the disabledPackageSources node" - EnablePrivatePackageSources -DisabledPackageSources $disabledSources -} - -$userName = "dn-bot" - -# Insert credential nodes for Maestro's private feeds -InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -pwd $Password - -# 3.1 uses a different feed url format so it's handled differently here -$dotnet31Source = $sources.SelectSingleNode("add[@key='dotnet3.1']") -if ($dotnet31Source -ne $null) { - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password + EnableMaestroInternalPackageSources -DisabledPackageSources $disabledSources -Creds $creds } - $dotnetVersions = @('5','6','7','8','9','10') foreach ($dotnetVersion in $dotnetVersions) { $feedPrefix = "dotnet" + $dotnetVersion; $dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']") if ($dotnetSource -ne $null) { - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password - AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password + AddOrEnablePackageSource -Sources $sources -DisabledPackageSources $disabledSources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/$feedSuffix" -Creds $creds -Username $userName -pwd $Password } } diff --git a/src/arcade/eng/common/SetupNugetSources.sh b/src/arcade/eng/common/SetupNugetSources.sh index ddf4efc81a4..dd2564aef01 100755 --- a/src/arcade/eng/common/SetupNugetSources.sh +++ b/src/arcade/eng/common/SetupNugetSources.sh @@ -52,78 +52,126 @@ if [[ `uname -s` == "Darwin" ]]; then TB='' fi -# Ensure there is a ... section. -grep -i "" $ConfigFile -if [ "$?" != "0" ]; then - echo "Adding ... section." - ConfigNodeHeader="" - PackageSourcesTemplate="${TB}${NL}${TB}" +# Enables an internal package source by name, if found. Returns 0 if found and enabled, 1 if not found. +EnableInternalPackageSource() { + local PackageSourceName="$1" + + # Check if disabledPackageSources section exists + grep -i "" "$ConfigFile" > /dev/null + if [ "$?" != "0" ]; then + return 1 # No disabled sources section + fi + + # Check if this source name is disabled + grep -i " /dev/null + if [ "$?" == "0" ]; then + echo "Enabling internal source '$PackageSourceName'." + # Remove the disabled entry + local OldDisableValue="" + local NewDisableValue="" + sed -i.bak "s|$OldDisableValue|$NewDisableValue|" "$ConfigFile" + + # Add the source name to PackageSources for credential handling + PackageSources+=("$PackageSourceName") + return 0 # Found and enabled + fi + + return 1 # Not found in disabled sources +} + +# Add source entry to PackageSources +AddPackageSource() { + local SourceName="$1" + local SourceEndPoint="$2" + + # Check if source already exists + grep -i " /dev/null + if [ "$?" == "0" ]; then + echo "Package source $SourceName already present and enabled." + PackageSources+=("$SourceName") + return + fi + + echo "Adding package source $SourceName" + PackageSourcesNodeFooter="" + PackageSourceTemplate="${TB}" + + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" "$ConfigFile" + PackageSources+=("$SourceName") +} + +# Adds or enables the package source with the given name +AddOrEnablePackageSource() { + local SourceName="$1" + local SourceEndPoint="$2" + + # Try to enable if disabled, if not found then add new source + EnableInternalPackageSource "$SourceName" + if [ "$?" != "0" ]; then + AddPackageSource "$SourceName" "$SourceEndPoint" + fi +} - sed -i.bak "s|$ConfigNodeHeader|$ConfigNodeHeader${NL}$PackageSourcesTemplate|" $ConfigFile -fi +# Enable all darc-int package sources +EnableMaestroInternalPackageSources() { + # Check if disabledPackageSources section exists + grep -i "" "$ConfigFile" > /dev/null + if [ "$?" != "0" ]; then + return # No disabled sources section + fi + + # Find all darc-int disabled sources + local DisabledDarcIntSources=() + DisabledDarcIntSources+=$(grep -oh '"darc-int-[^"]*" value="true"' "$ConfigFile" | tr -d '"') + + for DisabledSourceName in ${DisabledDarcIntSources[@]} ; do + if [[ $DisabledSourceName == darc-int* ]]; then + EnableInternalPackageSource "$DisabledSourceName" + fi + done +} -# Ensure there is a ... section. -grep -i "" $ConfigFile +# Ensure there is a ... section. +grep -i "" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding ... section." - - PackageSourcesNodeFooter="" - PackageSourceCredentialsTemplate="${TB}${NL}${TB}" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourcesNodeFooter${NL}$PackageSourceCredentialsTemplate|" $ConfigFile + Write-PipelineTelemetryError -Category 'Build' "Error: Eng/common/SetupNugetSources.sh returned a non-zero exit code. NuGet config file must contain a packageSources section: $ConfigFile" + ExitWithExitCode 1 fi PackageSources=() -# Ensure dotnet3.1-internal and dotnet3.1-internal-transport are in the packageSources if the public dotnet3.1 feeds are present -grep -i "... section. + grep -i "" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding dotnet3.1-internal to the packageSources." - PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" + echo "Adding ... section." - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=('dotnet3.1-internal') - - grep -i "" $ConfigFile - if [ "$?" != "0" ]; then - echo "Adding dotnet3.1-internal-transport to the packageSources." PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" + PackageSourceCredentialsTemplate="${TB}${NL}${TB}" - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile + sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourcesNodeFooter${NL}$PackageSourceCredentialsTemplate|" $ConfigFile fi - PackageSources+=('dotnet3.1-internal-transport') +fi + +# Check for disabledPackageSources; we'll enable any darc-int ones we find there +grep -i "" $ConfigFile > /dev/null +if [ "$?" == "0" ]; then + echo "Checking for any darc-int disabled package sources in the disabledPackageSources node" + EnableMaestroInternalPackageSources fi DotNetVersions=('5' '6' '7' '8' '9' '10') for DotNetVersion in ${DotNetVersions[@]} ; do FeedPrefix="dotnet${DotNetVersion}"; - grep -i " /dev/null if [ "$?" == "0" ]; then - grep -i "" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=("$FeedPrefix-internal") - - grep -i "" $ConfigFile - if [ "$?" != "0" ]; then - echo "Adding $FeedPrefix-internal-transport to the packageSources." - PackageSourcesNodeFooter="" - PackageSourceTemplate="${TB}" - - sed -i.bak "s|$PackageSourcesNodeFooter|$PackageSourceTemplate${NL}$PackageSourcesNodeFooter|" $ConfigFile - fi - PackageSources+=("$FeedPrefix-internal-transport") + AddOrEnablePackageSource "$FeedPrefix-internal" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$FeedPrefix-internal/nuget/$FeedSuffix" + AddOrEnablePackageSource "$FeedPrefix-internal-transport" "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$FeedPrefix-internal-transport/nuget/$FeedSuffix" fi done @@ -139,29 +187,12 @@ if [ "$CredToken" ]; then # Check if there is no existing credential for this FeedName grep -i "<$FeedName>" $ConfigFile if [ "$?" != "0" ]; then - echo "Adding credentials for $FeedName." + echo " Inserting credential for feed: $FeedName" PackageSourceCredentialsNodeFooter="" - NewCredential="${TB}${TB}<$FeedName>${NL}${NL}${NL}" + NewCredential="${TB}${TB}<$FeedName>${NL}${TB}${NL}${TB}${TB}${NL}${TB}${TB}" sed -i.bak "s|$PackageSourceCredentialsNodeFooter|$NewCredential${NL}$PackageSourceCredentialsNodeFooter|" $ConfigFile fi done fi - -# Re-enable any entries in disabledPackageSources where the feed name contains darc-int -grep -i "" $ConfigFile -if [ "$?" == "0" ]; then - DisabledDarcIntSources=() - echo "Re-enabling any disabled \"darc-int\" package sources in $ConfigFile" - DisabledDarcIntSources+=$(grep -oh '"darc-int-[^"]*" value="true"' $ConfigFile | tr -d '"') - for DisabledSourceName in ${DisabledDarcIntSources[@]} ; do - if [[ $DisabledSourceName == darc-int* ]] - then - OldDisableValue="" - NewDisableValue="" - sed -i.bak "s|$OldDisableValue|$NewDisableValue|" $ConfigFile - echo "Neutralized disablePackageSources entry for '$DisabledSourceName'" - fi - done -fi diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs new file mode 100644 index 00000000000..96366159bed --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/BoundaryConditionTests.cs @@ -0,0 +1,186 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class BoundaryConditionTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public BoundaryConditionTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task EmptyConfiguration_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithoutPackageSourcesSection_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithMissingDisabledPackageSourcesSection_StillAddsInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should still add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + } + + [Fact] + public async Task NonExistentConfigFile_ReturnsError() + { + // Arrange + var nonExistentPath = Path.Combine(_testOutputDirectory, "nonexistent.config"); + // Act + var result = await _scriptRunner.RunScript(nonExistentPath); + + // Assert + result.exitCode.Should().Be(1, "should return error code for nonexistent file"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("Couldn't find the NuGet config file", "should report missing file error"); + } + + [Fact] + public async Task ConfigWithOnlyDisabledSources_FailsWithoutPackageSourcesSection() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(1, "Script should fail when packageSources section is missing"); + // Check both output and error for the message (scripts may write to stdout instead of stderr) + var errorMessage = string.IsNullOrEmpty(result.error) ? result.output : result.error; + errorMessage.Should().Contain("packageSources section", "should report missing packageSources section error"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Config should remain unchanged when script fails + modifiedConfig.Should().BeEquivalentTo(originalConfig, "config should not be modified when script fails"); + } + + [Fact] + public async Task ConfigWithEmptyPackageSourcesSection_HandlesCorrectly() + { + // Arrange + var originalConfig = @" + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should enable darc-int feeds but not add any dotnet internal feeds since no dotnet feeds exist + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "should enable darc-int feed"); + modifiedConfig.GetPackageSourceCount().Should().Be(0, "should not add dotnet internal feeds without dotnet public feeds"); + } + } +} + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs new file mode 100644 index 00000000000..a9cf811728b --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/CredentialHandlingTests.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class CredentialHandlingTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public CredentialHandlingTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task ConfigWithCredentialProvided_AddsCredentialsForInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + + // Should add credentials for internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed"); + + // Should use v2 endpoints when credentials are provided + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v2", + "should use v2 endpoint when credentials provided"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v2", + "should use v2 endpoint when credentials provided"); + } + + [Fact] + public async Task ConfigWithNoCredential_DoesNotAddCredentials() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act - No credential provided + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds + modifiedConfig.ShouldContainPackageSource("dotnet6-internal"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport"); + + // Should NOT add credentials + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal", "should not add credentials without credential"); + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential"); + + // Should use v3 endpoints when no credentials are provided + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should use v3 endpoint when no credentials provided"); + } + + [Fact] + public async Task ConfigWithExistingCredentials_PreservesAndAddsNew() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should preserve existing credentials + modifiedConfig.ShouldContainCredentials("existing-private", "existing-user", "should preserve existing credentials"); + + // Should add new credentials for internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for new internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for new transport feed"); + } + + [Fact] + public async Task ConfigWithDarcIntFeeds_AddsCredentialsForEnabledFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + var testCredential = "Placeholder"; + // Act + var result = await _scriptRunner.RunScript(configPath, testCredential); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should enable the darc-int feed + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + + // Should add credentials for enabled darc-int feed + modifiedConfig.ShouldContainCredentials("darc-int-dotnet-roslyn-12345", "dn-bot", "should add credentials for enabled darc-int feed"); + + // Should add credentials for new internal feeds + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should add credentials for internal feed"); + modifiedConfig.ShouldContainCredentials("dotnet6-internal-transport", "dn-bot", "should add credentials for transport feed"); + } + + [Fact] + public async Task ConfigWithNoCredentialButExistingCredentials_DoesNotRemoveExistingCredentials() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act - No credential provided + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should preserve existing credentials + modifiedConfig.ShouldContainCredentials("dotnet6-internal", "dn-bot", "should preserve existing credentials"); + + // Should not add credentials for new feeds without credential + modifiedConfig.ShouldNotContainCredentials("dotnet6-internal-transport", "should not add credentials without credential"); + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs new file mode 100644 index 00000000000..7af694a1db8 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/FeedEnablingTests.cs @@ -0,0 +1,179 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class FeedEnablingTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public FeedEnablingTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Fact] + public async Task ConfigWithDisabledDarcIntFeeds_EnablesFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Darc-int feeds should no longer be disabled + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-runtime-67890", "darc-int feed should be enabled"); + + // Should also add internal feeds for dotnet6 + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should add dotnet6-internal feed"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add dotnet6-internal-transport feed"); + } + + [Fact] + public async Task ConfigWithMixedDisabledFeeds_OnlyEnablesDarcIntFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Darc-int feeds should be enabled + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-roslyn-12345", "darc-int feed should be enabled"); + modifiedConfig.ShouldNotBeDisabled("darc-int-dotnet-runtime-67890", "darc-int feed should be enabled"); + + // Non-darc-int feeds should remain disabled + modifiedConfig.ShouldBeDisabled("some-other-feed", "non-darc-int feed should remain disabled"); + modifiedConfig.ShouldBeDisabled("another-disabled-feed", "non-darc-int feed should remain disabled"); + } + + [Fact] + public async Task ConfigWithDisabledInternalFeed_EnablesExistingInsteadOfAdding() + { + // Arrange + var originalConfig = @" + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // The dotnet6-internal feed should be enabled (removed from disabled sources) + modifiedConfig.ShouldNotBeDisabled("dotnet6-internal", "internal feed should be enabled"); + + // Should still add the transport feed + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add transport feed"); + + // Should have 4 package sources (original 3, with dotnet6-internal enabled + transport added) + modifiedConfig.GetPackageSourceCount().Should().Be(4, "should enable existing feed and add transport feed"); + } + + [Fact] + public async Task ConfigWithNoDisabledSources_StillAddsInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds even without disabled sources section + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "should add dotnet6-internal feed"); + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add dotnet6-internal-transport feed"); + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs new file mode 100644 index 00000000000..ca42421c401 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/InternalFeedAdditionTests.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class InternalFeedAdditionTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public InternalFeedAdditionTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + [Theory] + [InlineData("dotnet5")] + [InlineData("dotnet6")] + [InlineData("dotnet7")] + [InlineData("dotnet8")] + [InlineData("dotnet9")] + [InlineData("dotnet10")] + public async Task ConfigWithSpecificDotNetVersion_AddsCorrespondingInternalFeeds(string dotnetVersion) + { + // Arrange + var originalConfig = $@" + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + modifiedConfig.ShouldContainPackageSource($"{dotnetVersion}-internal", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{dotnetVersion}-internal/nuget/v3/index.json", + $"should add {dotnetVersion}-internal feed"); + modifiedConfig.ShouldContainPackageSource($"{dotnetVersion}-internal-transport", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{dotnetVersion}-internal-transport/nuget/v3/index.json", + $"should add {dotnetVersion}-internal-transport feed"); + } + + [Fact] + public async Task ConfigWithMultipleDotNetVersions_AddsAllInternalFeeds() + { + // Arrange + var originalConfig = @" + + + + + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should add internal feeds for all versions + var versions = new[] { "dotnet5", "dotnet6", "dotnet7", "dotnet8", "dotnet9", "dotnet10" }; + foreach (var version in versions) + { + modifiedConfig.ShouldContainPackageSource($"{version}-internal", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{version}-internal/nuget/v3/index.json", + $"should add {version}-internal feed"); + modifiedConfig.ShouldContainPackageSource($"{version}-internal-transport", + $"https://pkgs.dev.azure.com/dnceng/internal/_packaging/{version}-internal-transport/nuget/v3/index.json", + $"should add {version}-internal-transport feed"); + } + + // Original count (7 sources) + 12 internal sources = 19 total + modifiedConfig.GetPackageSourceCount().Should().Be(19, "should have all original sources plus internal feeds"); + } + + [Fact] + public async Task ConfigWithExistingInternalFeed_DoesNotDuplicate() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + + // Should still contain the dotnet6-internal feed (only once) + modifiedConfig.ShouldContainPackageSource("dotnet6-internal", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal/nuget/v3/index.json", + "existing internal feed should be preserved"); + + // Should add the missing transport feed + modifiedConfig.ShouldContainPackageSource("dotnet6-internal-transport", + "https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet6-internal-transport/nuget/v3/index.json", + "should add missing transport feed"); + + // Should have 4 total sources (3 original + 1 added transport) + modifiedConfig.GetPackageSourceCount().Should().Be(4, "should not duplicate existing sources"); + } + } +} + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj new file mode 100644 index 00000000000..83d868c3bdc --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/Microsoft.DotNet.SetupNugetSources.Tests.csproj @@ -0,0 +1,25 @@ + + + + $(NetToolCurrent) + true + + + + + + + + + + + + + + + + + + + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs new file mode 100644 index 00000000000..632511d3441 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NoChangeScenarioTests.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public class NoChangeScenarioTests : IClassFixture, IDisposable + { + private readonly ScriptRunner _scriptRunner; + private readonly string _testOutputDirectory; + + public NoChangeScenarioTests(SetupNugetSourcesFixture fixture) + { + _testOutputDirectory = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(_testOutputDirectory); + _scriptRunner = fixture.ScriptRunner; + } + + public void Dispose() + { + try + { + if (Directory.Exists(_testOutputDirectory)) + { + Directory.Delete(_testOutputDirectory, true); + } + } + catch { } + } + + + + [Fact] + public async Task BasicConfig_NoChanges() + { + // Arrange + var originalConfig = @" + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + modifiedConfig.ShouldBeSemanticallySame(originalConfig, "basic config with no special feeds should not be modified"); + } + + [Fact] + public async Task ConfigWithNonDotNetFeeds_NoChanges() + { + // Arrange + var originalConfig = @" + + + + + + +"; + var configPath = Path.Combine(_testOutputDirectory, "nuget.config"); + await Task.Run(() => File.WriteAllText(configPath, originalConfig)); + // Act + var result = await _scriptRunner.RunScript(configPath); + + // Assert + result.exitCode.Should().Be(0, "Script should succeed, but got error: {result.error}"); + var modifiedConfig = await Task.Run(() => File.ReadAllText(configPath)); + modifiedConfig.ShouldBeSemanticallySame(originalConfig, "config with non-dotnet feeds should not be modified"); + } + } +} + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs new file mode 100644 index 00000000000..0bd0f5b5dfd --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/NuGetConfigAssertions.cs @@ -0,0 +1,190 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Linq; +using FluentAssertions; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public static class NuGetConfigAssertions + { + /// + /// Compares two NuGet.config files for semantic equality, ignoring whitespace differences + /// + public static void ShouldBeSemanticallySame(this string actualContent, string expectedContent, string because = "") + { + var actualNormalized = NormalizeXml(actualContent); + var expectedNormalized = NormalizeXml(expectedContent); + + actualNormalized.Should().Be(expectedNormalized, because); + } + + /// + /// Asserts that the config contains a package source with the specified key + /// + public static void ShouldContainPackageSource(this string configContent, string key, string value = null, string because = "") + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + packageSources.Should().NotBeNull($"packageSources section should exist {because}"); + + var source = packageSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == key); + source.Should().NotBeNull($"package source '{key}' should exist {because}"); + + if (value != null) + { + source.Attribute("value")?.Value.Should().Be(value, $"package source '{key}' should have the correct value {because}"); + } + } + + /// + /// Asserts that the config does not contain a package source with the specified key + /// + public static void ShouldNotContainPackageSource(this string configContent, string key, string because = "") + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + + if (packageSources != null) + { + var source = packageSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == key); + source.Should().BeNull($"package source '{key}' should not exist {because}"); + } + } + + /// + /// Asserts that the config contains credentials for the specified source + /// + public static void ShouldContainCredentials(this string configContent, string sourceName, string username = null, string because = "") + { + var doc = XDocument.Parse(configContent); + var credentials = doc.Root?.Element("packageSourceCredentials"); + credentials.Should().NotBeNull($"packageSourceCredentials section should exist {because}"); + + var sourceCredentials = credentials.Element(sourceName); + sourceCredentials.Should().NotBeNull($"credentials for '{sourceName}' should exist {because}"); + + if (username != null) + { + var usernameElement = sourceCredentials.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == "Username"); + usernameElement.Should().NotBeNull($"username credential should exist for '{sourceName}' {because}"); + usernameElement.Attribute("value")?.Value.Should().Be(username, $"username should match for '{sourceName}' {because}"); + } + + var passwordElement = sourceCredentials.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == "ClearTextPassword"); + passwordElement.Should().NotBeNull($"password credential should exist for '{sourceName}' {because}"); + } + + /// + /// Asserts that the config does not contain credentials for the specified source + /// + public static void ShouldNotContainCredentials(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var credentials = doc.Root?.Element("packageSourceCredentials"); + + if (credentials != null) + { + var sourceCredentials = credentials.Element(sourceName); + sourceCredentials.Should().BeNull($"credentials for '{sourceName}' should not exist {because}"); + } + } + + /// + /// Asserts that a source is not in the disabled sources list + /// + public static void ShouldNotBeDisabled(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var disabledSources = doc.Root?.Element("disabledPackageSources"); + + if (disabledSources != null) + { + var disabledSource = disabledSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == sourceName); + disabledSource.Should().BeNull($"source '{sourceName}' should not be disabled {because}"); + } + } + + /// + /// Asserts that a source is in the disabled sources list + /// + public static void ShouldBeDisabled(this string configContent, string sourceName, string because = "") + { + var doc = XDocument.Parse(configContent); + var disabledSources = doc.Root?.Element("disabledPackageSources"); + disabledSources.Should().NotBeNull($"disabledPackageSources section should exist {because}"); + + var disabledSource = disabledSources.Elements("add").FirstOrDefault(e => e.Attribute("key")?.Value == sourceName); + disabledSource.Should().NotBeNull($"source '{sourceName}' should be disabled {because}"); + } + + /// + /// Counts the number of package sources in the config + /// + public static int GetPackageSourceCount(this string configContent) + { + var doc = XDocument.Parse(configContent); + var packageSources = doc.Root?.Element("packageSources"); + return packageSources?.Elements("add").Count() ?? 0; + } + + /// + /// Normalizes XML content for comparison by removing whitespace differences and sorting elements consistently + /// + private static string NormalizeXml(string xmlContent) + { + var doc = XDocument.Parse(xmlContent); + + // Sort package sources by key for consistent comparison + var packageSources = doc.Root?.Element("packageSources"); + if (packageSources != null) + { + var sortedSources = packageSources.Elements("add") + .OrderBy(e => e.Attribute("key")?.Value) + .ToList(); + packageSources.RemoveAll(); + foreach (var source in sortedSources) + { + packageSources.Add(source); + } + } + + // Sort disabled sources by key + var disabledSources = doc.Root?.Element("disabledPackageSources"); + if (disabledSources != null) + { + var sortedDisabled = disabledSources.Elements("add") + .OrderBy(e => e.Attribute("key")?.Value) + .ToList(); + disabledSources.RemoveAll(); + foreach (var source in sortedDisabled) + { + disabledSources.Add(source); + } + } + + // Sort credentials by source name + var credentials = doc.Root?.Element("packageSourceCredentials"); + if (credentials != null) + { + var sortedCredentials = credentials.Elements() + .OrderBy(e => e.Name.LocalName) + .ToList(); + credentials.RemoveAll(); + foreach (var cred in sortedCredentials) + { + credentials.Add(cred); + } + } + + return doc.ToString(SaveOptions.DisableFormatting); + } + } +} + + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs new file mode 100644 index 00000000000..37dd8cb9d37 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/ScriptRunner.cs @@ -0,0 +1,125 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + public enum ScriptType + { + PowerShell, + Shell + } + + public class ScriptRunner + { + private readonly string _repoRoot; + + public ScriptRunner(string repoRoot) + { + _repoRoot = repoRoot ?? throw new ArgumentNullException(nameof(repoRoot)); + } + + public async Task<(int exitCode, string output, string error)> RunPowerShellScript(string configFilePath, string password = null) + { + var scriptPath = Path.Combine(_repoRoot, "eng", "common", "SetupNugetSources.ps1"); + var arguments = $"-ExecutionPolicy Bypass -File \"{scriptPath}\" -ConfigFile \"{configFilePath}\""; + + if (!string.IsNullOrEmpty(password)) + { + arguments += $" -Password \"{password}\""; + } + + return await RunProcess("powershell.exe", arguments, _repoRoot); + } + + public async Task<(int exitCode, string output, string error)> RunShellScript(string configFilePath, string credToken = null) + { + var scriptPath = Path.Combine(_repoRoot, "eng", "common", "SetupNugetSources.sh"); + + // Make script executable if on Unix + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + await RunProcess("chmod", $"+x \"{scriptPath}\"", _repoRoot); + } + + var arguments = $"\"{scriptPath}\" \"{configFilePath}\""; + if (!string.IsNullOrEmpty(credToken)) + { + arguments += $" \"{credToken}\""; + } + + var shell = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bash.exe" : "/bin/bash"; + return await RunProcess(shell, arguments, _repoRoot); + } + + private async Task<(int exitCode, string output, string error)> RunProcess(string fileName, string arguments, string workingDirectory = null) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = fileName, + Arguments = arguments, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + WorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory() + } + }; + + var outputBuilder = new StringBuilder(); + var errorBuilder = new StringBuilder(); + + process.OutputDataReceived += (sender, e) => + { + if (e.Data != null) + { + outputBuilder.AppendLine(e.Data); + } + }; + + process.ErrorDataReceived += (sender, e) => + { + if (e.Data != null) + { + errorBuilder.AppendLine(e.Data); + } + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + await Task.Run(() => process.WaitForExit()); + + return (process.ExitCode, outputBuilder.ToString(), errorBuilder.ToString()); + } + + public async Task<(int exitCode, string output, string error)> RunScript(string configFilePath, string credential = null) + { + var scriptType = GetPlatformAppropriateScriptType(); + switch (scriptType) + { + case ScriptType.PowerShell: + return await RunPowerShellScript(configFilePath, credential); + case ScriptType.Shell: + return await RunShellScript(configFilePath, credential); + default: + throw new ArgumentException($"Unsupported script type: {scriptType}"); + } + } + + public static ScriptType GetPlatformAppropriateScriptType() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ScriptType.PowerShell : ScriptType.Shell; + } + } +} + diff --git a/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs new file mode 100644 index 00000000000..7e25ccd1b88 --- /dev/null +++ b/src/arcade/src/Microsoft.DotNet.SetupNugetSources.Tests/SetupNugetSourcesFixture.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +namespace Microsoft.DotNet.SetupNugetSources.Tests +{ + /// + /// xUnit fixture that prepares a temporary repository root containing the + /// scaffolded files (global.json + eng/common scripts) copied from the + /// build output's RepoScaffold directory. Shared per test class. + /// + public class SetupNugetSourcesFixture : IDisposable + { + public string RepoRoot { get; } + public ScriptRunner ScriptRunner { get; } + + public SetupNugetSourcesFixture() + { + var scaffoldRoot = Path.Combine(AppContext.BaseDirectory, "RepoScaffold"); + if (!Directory.Exists(scaffoldRoot)) + { + throw new InvalidOperationException($"Expected scaffold directory not found: {scaffoldRoot}"); + } + + RepoRoot = Path.Combine(Path.GetTempPath(), "SetupNugetSourcesTestRepo", Guid.NewGuid().ToString()); + CopyDirectory(scaffoldRoot, RepoRoot); + + ScriptRunner = new ScriptRunner(RepoRoot); + } + + private static void CopyDirectory(string sourceDir, string destinationDir) + { + foreach (var dir in Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories)) + { + var relative = Path.GetRelativePath(sourceDir, dir); + Directory.CreateDirectory(Path.Combine(destinationDir, relative)); + } + + foreach (var file in Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories)) + { + var relative = Path.GetRelativePath(sourceDir, file); + var destPath = Path.Combine(destinationDir, relative); + Directory.CreateDirectory(Path.GetDirectoryName(destPath)!); + File.Copy(file, destPath, overwrite: true); + } + } + + public void Dispose() + { + try + { + if (Directory.Exists(RepoRoot)) + { + Directory.Delete(RepoRoot, recursive: true); + } + } + catch + { + // Best effort cleanup. + } + } + } +} diff --git a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs index 3b4cd8581ea..03672d1c263 100644 --- a/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs +++ b/src/arcade/src/Microsoft.DotNet.SignTool.Tests/SignToolTests.cs @@ -480,7 +480,9 @@ private void ValidateProducedRpmContent( string layout = Path.Combine(tempDir, "layout"); Directory.CreateDirectory(layout); - ZipData.ExtractRpmPayloadContents(log: null, rpmPackage, layout); + var fakeBuildEngine = new FakeBuildEngine(_output); + var fakeLog = new TaskLoggingHelper(fakeBuildEngine, "TestLog"); + ZipData.ExtractRpmPayloadContents(fakeLog, rpmPackage, layout); // Checks: // Expected files are present diff --git a/src/aspnetcore/eng/Version.Details.props b/src/aspnetcore/eng/Version.Details.props index ffe9c35f973..06e740a8cae 100644 --- a/src/aspnetcore/eng/Version.Details.props +++ b/src/aspnetcore/eng/Version.Details.props @@ -6,98 +6,98 @@ This file should be imported by eng/Versions.props - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-beta.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 3.2.0-preview.25507.103 - 7.0.0-rc.803 - 7.0.0-rc.803 - 7.0.0-rc.803 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 - 10.0.0-rtm.25507.103 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 3.2.0-preview.25509.106 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 4.13.0-3.24613.7 4.13.0-3.24613.7 diff --git a/src/aspnetcore/eng/Version.Details.xml b/src/aspnetcore/eng/Version.Details.xml index 0f0a7ebe0bf..cc99cb2648b 100644 --- a/src/aspnetcore/eng/Version.Details.xml +++ b/src/aspnetcore/eng/Version.Details.xml @@ -8,333 +8,333 @@ See https://github.com/dotnet/arcade/blob/master/Documentation/Darc.md for instructions on using darc. --> - + - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac @@ -358,37 +358,37 @@ - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac https://github.com/dotnet/extensions @@ -440,17 +440,17 @@ https://github.com/dotnet/msbuild d1cce8d7cc03c23a4f1bad8e9240714fd9d199a3 - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac diff --git a/src/aspnetcore/global.json b/src/aspnetcore/global.json index c4cb92a9a95..abf6225284d 100644 --- a/src/aspnetcore/global.json +++ b/src/aspnetcore/global.json @@ -27,9 +27,9 @@ "jdk": "latest" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25507.103", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25507.103", - "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25507.103", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25509.106", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25509.106", + "Microsoft.DotNet.SharedFramework.Sdk": "10.0.0-beta.25509.106", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.WixToolset.Sdk": "5.0.2-dotnet.2737382" diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs index a8161054279..7ce2a54419b 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSource.cs @@ -20,8 +20,8 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp private readonly object _lock = new(); private readonly List> _conventions = []; private readonly List> _finallyConventions = []; + private readonly List> _componentApplicationBuilderActions = []; private readonly RazorComponentDataSourceOptions _options = new(); - private readonly ComponentApplicationBuilder _builder; private readonly IEndpointRouteBuilder _endpointRouteBuilder; private readonly ResourceCollectionResolver _resourceCollectionResolver; private readonly RenderModeEndpointProvider[] _renderModeEndpointProviders; @@ -32,33 +32,29 @@ internal class RazorComponentEndpointDataSource<[DynamicallyAccessedMembers(Comp private IChangeToken _changeToken; private IDisposable? _disposableChangeToken; // THREADING: protected by _lock - public Func SetDisposableChangeTokenAction = disposableChangeToken => disposableChangeToken; - // Internal for testing. - internal ComponentApplicationBuilder Builder => _builder; internal List> Conventions => _conventions; + internal List> ComponentApplicationBuilderActions => _componentApplicationBuilderActions; + internal CancellationTokenSource ChangeTokenSource => _cancellationTokenSource; public RazorComponentEndpointDataSource( - ComponentApplicationBuilder builder, IEnumerable renderModeEndpointProviders, IEndpointRouteBuilder endpointRouteBuilder, RazorComponentEndpointFactory factory, HotReloadService? hotReloadService = null) { - _builder = builder; _endpointRouteBuilder = endpointRouteBuilder; _resourceCollectionResolver = new ResourceCollectionResolver(endpointRouteBuilder); _renderModeEndpointProviders = renderModeEndpointProviders.ToArray(); _factory = factory; _hotReloadService = hotReloadService; - HotReloadService.ClearCacheEvent += OnHotReloadClearCache; DefaultBuilder = new RazorComponentsEndpointConventionBuilder( _lock, - builder, endpointRouteBuilder, _options, _conventions, - _finallyConventions); + _finallyConventions, + _componentApplicationBuilderActions); _cancellationTokenSource = new CancellationTokenSource(); _changeToken = new CancellationChangeToken(_cancellationTokenSource.Token); @@ -106,8 +102,20 @@ private void UpdateEndpoints() lock (_lock) { + _disposableChangeToken?.Dispose(); + _disposableChangeToken = null; + var endpoints = new List(); - var context = _builder.Build(); + + var componentApplicationBuilder = new ComponentApplicationBuilder(); + + foreach (var action in ComponentApplicationBuilderActions) + { + action?.Invoke(componentApplicationBuilder); + } + + var context = componentApplicationBuilder.Build(); + var configuredRenderModesMetadata = new ConfiguredRenderModesMetadata( [.. Options.ConfiguredRenderModes]); @@ -168,8 +176,7 @@ private void UpdateEndpoints() oldCancellationTokenSource?.Dispose(); if (_hotReloadService is { MetadataUpdateSupported: true }) { - _disposableChangeToken?.Dispose(); - _disposableChangeToken = SetDisposableChangeTokenAction(ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints)); + _disposableChangeToken = ChangeToken.OnChange(_hotReloadService.GetChangeToken, UpdateEndpoints); } } } @@ -195,15 +202,6 @@ private void AddBlazorWebEndpoints(List endpoints) } } - public void OnHotReloadClearCache(Type[]? types) - { - lock (_lock) - { - _disposableChangeToken?.Dispose(); - _disposableChangeToken = null; - } - } - public override IChangeToken GetChangeToken() { Initialize(); diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs index 84327f3f6d5..a8f178a4fc2 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentEndpointDataSourceFactory.cs @@ -17,9 +17,14 @@ internal class RazorComponentEndpointDataSourceFactory( { public RazorComponentEndpointDataSource CreateDataSource<[DynamicallyAccessedMembers(Component)] TRootComponent>(IEndpointRouteBuilder endpoints) { - var builder = ComponentApplicationBuilder.GetBuilder() ?? - DefaultRazorComponentApplication.Instance.GetBuilder(); + var dataSource = new RazorComponentEndpointDataSource(providers, endpoints, factory, hotReloadService); - return new RazorComponentEndpointDataSource(builder, providers, endpoints, factory, hotReloadService); + dataSource.ComponentApplicationBuilderActions.Add(builder => + { + var assembly = typeof(TRootComponent).Assembly; + IRazorComponentApplication.GetBuilderForAssembly(builder, assembly); + }); + + return dataSource; } } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs index 04b1daa9e95..46808fbfdfb 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilder.cs @@ -18,27 +18,25 @@ public sealed class RazorComponentsEndpointConventionBuilder : IEndpointConventi private readonly RazorComponentDataSourceOptions _options; private readonly List> _conventions; private readonly List> _finallyConventions; + private readonly List> _componentApplicationBuilderActions; internal RazorComponentsEndpointConventionBuilder( object @lock, - ComponentApplicationBuilder builder, IEndpointRouteBuilder endpointRouteBuilder, RazorComponentDataSourceOptions options, List> conventions, - List> finallyConventions) + List> finallyConventions, + List> componentApplicationBuilderActions) { _lock = @lock; - ApplicationBuilder = builder; EndpointRouteBuilder = endpointRouteBuilder; _options = options; _conventions = conventions; _finallyConventions = finallyConventions; + _componentApplicationBuilderActions = componentApplicationBuilderActions; } - /// - /// Gets the that is used to build the endpoints. - /// - internal ComponentApplicationBuilder ApplicationBuilder { get; } + internal List> ComponentApplicationBuilderActions => _componentApplicationBuilderActions; internal string? ManifestPath { get => _options.ManifestPath; set => _options.ManifestPath = value; } diff --git a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs index fd0a6aec0c4..7cc4fa4dc76 100644 --- a/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs +++ b/src/aspnetcore/src/Components/Endpoints/src/Builder/RazorComponentsEndpointConventionBuilderExtensions.cs @@ -30,7 +30,7 @@ public static RazorComponentsEndpointConventionBuilder AddAdditionalAssemblies( foreach (var assembly in assemblies) { - builder.ApplicationBuilder.AddAssembly(assembly); + builder.ComponentApplicationBuilderActions.Add(b => b.AddAssembly(assembly)); } return builder; } diff --git a/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs b/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs index 70fa780fcba..e1a5116ee0b 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/Builder/RazorComponentsEndpointConventionBuilderExtensionsTest.cs @@ -245,7 +245,7 @@ public void MapRazorComponents_CanAddConventions_ToBlazorWebEndpoints(string fra private RazorComponentsEndpointConventionBuilder CreateRazorComponentsAppBuilder(IEndpointRouteBuilder endpointBuilder) { var builder = endpointBuilder.MapRazorComponents(); - builder.ApplicationBuilder.AddLibrary(new AssemblyComponentLibraryDescriptor( + builder.ComponentApplicationBuilderActions.Add(b => b.AddLibrary(new AssemblyComponentLibraryDescriptor( "App", [new PageComponentBuilder { PageType = typeof(App), @@ -253,7 +253,7 @@ [new PageComponentBuilder { AssemblyName = "App", }], [] - )); + ))); return builder; } diff --git a/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs b/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs index 011056c2e7c..a8e2280d77e 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/HotReloadServiceTests.cs @@ -23,9 +23,8 @@ public class HotReloadServiceTests public void UpdatesEndpointsWhenHotReloadChangeTokenTriggered() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); var invoked = false; // Act @@ -41,9 +40,8 @@ public void UpdatesEndpointsWhenHotReloadChangeTokenTriggered() public void AddNewEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType( @@ -52,15 +50,17 @@ public void AddNewEndpointWhenDataSourceChanges() Assert.Equal("/server", endpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.Pages.AddFromLibraryInfo("TestAssembly2", new[] - { - new PageComponentBuilder + endpointDataSource.ComponentApplicationBuilderActions.Add( + b => b.Pages.AddFromLibraryInfo("TestAssembly2", new[] { - AssemblyName = "TestAssembly2", - PageType = typeof(StaticComponent), - RouteTemplates = new List { "/app/test" } - } - }); + new PageComponentBuilder + { + AssemblyName = "TestAssembly2", + PageType = typeof(StaticComponent), + RouteTemplates = new List { "/app/test" } + } + })); + HotReloadService.UpdateApplication(null); // Assert - 2 @@ -76,9 +76,8 @@ public void AddNewEndpointWhenDataSourceChanges() public void RemovesEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, @@ -87,7 +86,7 @@ public void RemovesEndpointWhenDataSourceChanges() Assert.Equal("/server", endpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.RemoveLibrary("TestAssembly"); + endpointDataSource.ComponentApplicationBuilderActions.Add(b => b.RemoveLibrary("TestAssembly")); endpointDataSource.Options.ConfiguredRenderModes.Clear(); HotReloadService.UpdateApplication(null); @@ -100,9 +99,8 @@ public void RemovesEndpointWhenDataSourceChanges() public void ModifiesEndpointWhenDataSourceChanges() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); // Assert - 1 var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, e => e.Metadata.GetMetadata() != null)); @@ -124,9 +122,8 @@ public void ModifiesEndpointWhenDataSourceChanges() public void NotifiesCompositeEndpointDataSource() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder); var compositeEndpointDataSource = new CompositeEndpointDataSource( new[] { endpointDataSource }); @@ -137,7 +134,7 @@ public void NotifiesCompositeEndpointDataSource() Assert.Equal("/server", compositeEndpoint.RoutePattern.RawText); // Act - 2 - endpointDataSource.Builder.Pages.RemoveFromAssembly("TestAssembly"); + endpointDataSource.ComponentApplicationBuilderActions.Add(b => b.Pages.RemoveFromAssembly("TestAssembly")); endpointDataSource.Options.ConfiguredRenderModes.Clear(); HotReloadService.UpdateApplication(null); @@ -148,37 +145,14 @@ public void NotifiesCompositeEndpointDataSource() Assert.Empty(compositePageEndpoints); } - private sealed class WrappedChangeTokenDisposable : IDisposable - { - public bool IsDisposed { get; private set; } - private readonly IDisposable _innerDisposable; - - public WrappedChangeTokenDisposable(IDisposable innerDisposable) - { - _innerDisposable = innerDisposable; - } - - public void Dispose() - { - IsDisposed = true; - _innerDisposable.Dispose(); - } - } - [Fact] public void ConfirmChangeTokenDisposedHotReload() { // Arrange - var builder = CreateBuilder(typeof(ServerComponent)); var services = CreateServices(typeof(MockEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); - - WrappedChangeTokenDisposable wrappedChangeTokenDisposable = null; - - endpointDataSource.SetDisposableChangeTokenAction = (IDisposable disposableChangeToken) => { - wrappedChangeTokenDisposable = new WrappedChangeTokenDisposable(disposableChangeToken); - return wrappedChangeTokenDisposable; - }; + var endpointDataSource = CreateDataSource(services, ConfigureServerComponentBuilder, null); + var changeTokenSource = endpointDataSource.ChangeTokenSource; + var changeToken = endpointDataSource.GetChangeToken(); var endpoint = Assert.IsType(Assert.Single(endpointDataSource.Endpoints, e => e.Metadata.GetMetadata() != null)); Assert.Equal("/server", endpoint.RoutePattern.RawText); @@ -187,18 +161,21 @@ public void ConfirmChangeTokenDisposedHotReload() // Make a modification and then perform a hot reload. endpointDataSource.Conventions.Add(builder => builder.Metadata.Add(new TestMetadata())); + HotReloadService.UpdateApplication(null); HotReloadService.ClearCache(null); // Confirm the change token is disposed after ClearCache - Assert.True(wrappedChangeTokenDisposable.IsDisposed); + Assert.True(changeToken.HasChanged); + Assert.Throws(() => changeTokenSource.Token); } private class TestMetadata { } - private ComponentApplicationBuilder CreateBuilder(params Type[] types) + private class TestAssembly : Assembly; + + private static void ConfigureBuilder(ComponentApplicationBuilder builder, params Type[] types) { - var builder = new ComponentApplicationBuilder(); builder.AddLibrary(new AssemblyComponentLibraryDescriptor( "TestAssembly", Array.Empty(), @@ -208,8 +185,11 @@ private ComponentApplicationBuilder CreateBuilder(params Type[] types) ComponentType = t, RenderMode = t.GetCustomAttribute() }).ToArray())); + } - return builder; + private static void ConfigureServerComponentBuilder(ComponentApplicationBuilder builder) + { + ConfigureBuilder(builder, typeof(ServerComponent)); } private IServiceProvider CreateServices(params Type[] types) @@ -227,16 +207,21 @@ private IServiceProvider CreateServices(params Type[] types) } private static RazorComponentEndpointDataSource CreateDataSource( - ComponentApplicationBuilder builder, IServiceProvider services, - IComponentRenderMode[] renderModes = null) + Action configureBuilder = null, + IComponentRenderMode[] renderModes = null, + HotReloadService hotReloadService = null) { var result = new RazorComponentEndpointDataSource( - builder, new[] { new MockEndpointProvider() }, new TestEndpointRouteBuilder(services), new RazorComponentEndpointFactory(), - new HotReloadService() { MetadataUpdateSupported = true }); + hotReloadService ?? new HotReloadService() { MetadataUpdateSupported = true }); + + if (configureBuilder is not null) + { + result.ComponentApplicationBuilderActions.Add(configureBuilder); + } if (renderModes != null) { diff --git a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs index 52cd757d62a..26a154b2cb1 100644 --- a/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs +++ b/src/aspnetcore/src/Components/Endpoints/test/RazorComponentEndpointDataSourceTest.cs @@ -24,6 +24,11 @@ public class RazorComponentEndpointDataSourceTest public void RegistersEndpoints() { var endpointDataSource = CreateDataSource(); + endpointDataSource.ComponentApplicationBuilderActions.Add(builder => + { + var assembly = typeof(App).Assembly; + IRazorComponentApplication.GetBuilderForAssembly(builder, assembly); + }); var endpoints = endpointDataSource.Endpoints; @@ -33,10 +38,15 @@ public void RegistersEndpoints() [Fact] public void NoDiscoveredModesDefaultsToStatic() { - - var builder = CreateBuilder(); var services = CreateServices(typeof(ServerEndpointProvider)); - var endpointDataSource = CreateDataSource(builder, services); + var endpointDataSource = CreateDataSource(services); + + endpointDataSource.ComponentApplicationBuilderActions.Add(builder => + { + builder.AddLibrary(new AssemblyComponentLibraryDescriptor( + "TestAssembly", + Array.Empty(), Array.Empty())); + }); var endpoints = endpointDataSource.Endpoints; @@ -199,22 +209,6 @@ public void NoDiscoveredModesDefaultsToStatic() }, }; - private ComponentApplicationBuilder CreateBuilder(params Type[] types) - { - var builder = new ComponentApplicationBuilder(); - builder.AddLibrary(new AssemblyComponentLibraryDescriptor( - "TestAssembly", - Array.Empty(), - types.Select(t => new ComponentBuilder - { - AssemblyName = "TestAssembly", - ComponentType = t, - RenderMode = t.GetCustomAttribute() - }).ToArray())); - - return builder; - } - private IServiceProvider CreateServices(params Type[] types) { var services = new ServiceCollection(); @@ -230,12 +224,10 @@ private IServiceProvider CreateServices(params Type[] types) } private RazorComponentEndpointDataSource CreateDataSource( - ComponentApplicationBuilder builder = null, IServiceProvider services = null, IComponentRenderMode[] renderModes = null) { var result = new RazorComponentEndpointDataSource( - builder ?? DefaultRazorComponentApplication.Instance.GetBuilder(), services?.GetService>() ?? Enumerable.Empty(), new TestEndpointRouteBuilder(services ?? CreateServices()), new RazorComponentEndpointFactory(), diff --git a/src/diagnostics/.github/ISSUE_TEMPLATE/01_Bug_Report.md b/src/diagnostics/.github/ISSUE_TEMPLATE/01_Bug_Report.md index 918b30c8c8d..d643b2347d8 100644 --- a/src/diagnostics/.github/ISSUE_TEMPLATE/01_Bug_Report.md +++ b/src/diagnostics/.github/ISSUE_TEMPLATE/01_Bug_Report.md @@ -26,7 +26,7 @@ assignees: '' * Do you know whether it is specific to that configuration? * Are you running in any particular type of environment? (e.g. Containers, a cloud scenario, app you are trying to target is a different user) * Is it a self-contained published application? -* What's the output of `dotnet info` +* What's the output of `dotnet --info` --> ### Regression? diff --git a/src/diagnostics/src/SOS/SOS.Package/SOS.Package.csproj b/src/diagnostics/src/SOS/SOS.Package/SOS.Package.csproj index ed219206423..5607b1c05fe 100644 --- a/src/diagnostics/src/SOS/SOS.Package/SOS.Package.csproj +++ b/src/diagnostics/src/SOS/SOS.Package/SOS.Package.csproj @@ -14,7 +14,7 @@ true false - + @@ -30,47 +30,54 @@ - + - SOS - $(FileVersion) - Debugging aid for .NET Core programs and runtimes - - - - - - - - - - - - - - - - - - - - - - - - - - !soshelp - Displays all available SOS commands or details about the command - - - - - + SOS + $(FileVersion) + Debugging aid for .NET Core programs and runtimes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + !soshelp + Displays all available SOS commands or details about the command + + + + + ]]> diff --git a/src/efcore/.config/guardian/.gdnbaselines b/src/efcore/.config/guardian/.gdnbaselines new file mode 100644 index 00000000000..b6b74d33542 --- /dev/null +++ b/src/efcore/.config/guardian/.gdnbaselines @@ -0,0 +1,106 @@ +{ + "hydrated": false, + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" + }, + "version": "1.0.0", + "baselines": { + "default": { + "name": "default", + "createdDate": "2025-10-09 21:18:42Z", + "lastUpdatedDate": "2025-10-09 21:18:42Z" + } + }, + "results": { + "9d8334bec997ff899ba849d8b31406f7c95af0ffb1d237972bd5134b8c6a9b88": { + "signature": "9d8334bec997ff899ba849d8b31406f7c95af0ffb1d237972bd5134b8c6a9b88", + "alternativeSignatures": [ + "b6a603191b00edf78ad2c6116a7a7822864031cc001884be25b3c1543dbe20a7" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "ba7df56f5519347813a7917091709adf2d27b3493d05af0fb8b7037eda3020bb": { + "signature": "ba7df56f5519347813a7917091709adf2d27b3493d05af0fb8b7037eda3020bb", + "alternativeSignatures": [ + "5269e81e8a286ccd1568a00c455c489efa62e6887bfb55ea0dddaceb7183c882" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "bc04851b7440a10a0c2a03f2793ab06b6562fe0529e0d353765503f1bcaf8c46": { + "signature": "bc04851b7440a10a0c2a03f2793ab06b6562fe0529e0d353765503f1bcaf8c46", + "alternativeSignatures": [ + "274b9a26e35f9604cd1c84941eb97f2edaecde6607be139e9d9d687f7d6875f4" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "9fb27502e61c5647554076d6603a1092943fb625efb6c53faa1042e0d7d217ce": { + "signature": "9fb27502e61c5647554076d6603a1092943fb625efb6c53faa1042e0d7d217ce", + "alternativeSignatures": [ + "3355e030588f68be29994a44e2b11079e90945e26c6397345d11424ce36cc5a1" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "60f28802b8ac837691755554b460b422afaeb8dea2129097ab3b7e6c43076ea1": { + "signature": "60f28802b8ac837691755554b460b422afaeb8dea2129097ab3b7e6c43076ea1", + "alternativeSignatures": [ + "7a3aa28c8f6e629099ba288e1a78ddb3191def880ae018f7146b66689daca838" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "3bc55e953e7199f08b4c174f6fb0f026db93865d22fc5ef535e2ba1172c8db03": { + "signature": "3bc55e953e7199f08b4c174f6fb0f026db93865d22fc5ef535e2ba1172c8db03", + "alternativeSignatures": [ + "fb3809bbf91d374d8872aa371ff0d8858232822571dae6f9e6cba6276892d953" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "bd69975ed44d92efc4a265e13462b1471f292ad9e8566b0200b2df7786808469": { + "signature": "bd69975ed44d92efc4a265e13462b1471f292ad9e8566b0200b2df7786808469", + "alternativeSignatures": [ + "609bf3d92ef1f73cbe4d3fa001926c0e274b04f21bf6d103a39a64849437e7e3" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "0b6a4ced009a4e3efdf4ad8f00c0b31ec0791249087560c5c6481ab2824a35a8": { + "signature": "0b6a4ced009a4e3efdf4ad8f00c0b31ec0791249087560c5c6481ab2824a35a8", + "alternativeSignatures": [ + "c19c086ed81a8e7d7877bb2fb5a9d7df126ad553cbdd32086694b927042adc0b" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + }, + "c689fe4e10fee4bdd5a3d759c027be8b89d28303078f76bb5aeb20dc192215c9": { + "signature": "c689fe4e10fee4bdd5a3d759c027be8b89d28303078f76bb5aeb20dc192215c9", + "alternativeSignatures": [ + "4faf3b308c9131f0e11686d21fd36e1ef6779c394d37cfc43cd360b4bfb086f5" + ], + "memberOf": [ + "default" + ], + "createdDate": "2025-10-09 21:18:42Z" + } + } +} \ No newline at end of file diff --git a/src/efcore/.config/guardian/gdnbaselines b/src/efcore/.config/guardian/gdnbaselines deleted file mode 100644 index b0533e8bab7..00000000000 --- a/src/efcore/.config/guardian/gdnbaselines +++ /dev/null @@ -1,26 +0,0 @@ -{ - "hydrated": false, - "properties": { - "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" - }, - "version": "1.0.0", - "baselines": { - "default": { - "name": "default", - "createdDate": "2025-08-19 11:30:04Z", - "lastUpdatedDate": "2025-08-19 11:30:04Z" - } - }, - "results": { - "289457ef952517284338044be0d68120ad96554e2cd793b14a427b2208be2990": { - "signature": "289457ef952517284338044be0d68120ad96554e2cd793b14a427b2208be2990", - "alternativeSignatures": [ - "8b86228cd1b156622e76e1845b23b901cafd908a4dc0a7b7bdd5cdd714b726ab" - ], - "memberOf": [ - "default" - ], - "createdDate": "2025-08-19 11:30:04Z" - } - } -} diff --git a/src/efcore/EFCore.sln b/src/efcore/EFCore.sln index fecc4fafa42..91075f55dfd 100644 --- a/src/efcore/EFCore.sln +++ b/src/efcore/EFCore.sln @@ -1,11 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31521.260 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11026.189 MinimumVisualStudioVersion = 17.0.31521.260 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B9E4CC99-199C-4E3B-9EC5-D1FDFCD6C27B}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + azure-pipelines-internal-tests.yml = azure-pipelines-internal-tests.yml azure-pipelines-public.yml = azure-pipelines-public.yml azure-pipelines.yml = azure-pipelines.yml .github\copilot-instructions.md = .github\copilot-instructions.md diff --git a/src/efcore/azure-pipelines-internal-tests.yml b/src/efcore/azure-pipelines-internal-tests.yml index 5f14dbd13bf..2987ae059ef 100644 --- a/src/efcore/azure-pipelines-internal-tests.yml +++ b/src/efcore/azure-pipelines-internal-tests.yml @@ -1,8 +1,6 @@ variables: - name: _BuildConfig value: Release - - name: _TeamName - value: AspNetCore - name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE value: true - name: _PublishUsingPipelines @@ -11,12 +9,6 @@ variables: value: https://localhost:8081 - name: _CosmosToken value: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== - - ${{ if or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), eq(variables['Build.Reason'], 'Manual')) }}: - - name: PostBuildSign - value: false - - ${{ else }}: - - name: PostBuildSign - value: true - group: DotNet-HelixApi-Access - name: _InternalRuntimeDownloadArgs value: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) @@ -47,7 +39,6 @@ extends: parameters: featureFlags: autoBaseline: false - usePrefastVersion3: true autoEnableRoslynWithNewRuleset: false sdl: createAdoIssuesForJustificationsForDisablement: false @@ -55,19 +46,46 @@ extends: name: $(DncEngInternalBuildPool) image: 1es-windows-2022 os: windows + enabled: false baseline: baselineFile: $(Build.SourcesDirectory)\.config\guardian\.gdnbaselines binskim: - scanOutputDirectoryOnly: true - preReleaseVersion: '4.3.1' - env: - GDN_EXTRACT_TOOLS: "binskim" - GDN_EXTRACT_TARGETS: true - GDN_EXTRACT_FILTER: "f|**\\*.zip;f|**\\*.nupkg;f|**\\*.vsix;f|**\\*.cspkg;f|**\\*.sfpkg;f|**\\*.package" + enabled: false + justificationForDisabling: 'NonProduction' policheck: - enabled: true + enabled: false + justificationForDisabling: 'NonProduction' tsa: - enabled: true + enabled: false + justificationForDisabling: 'NonProduction' + antimalwareScan: + enabled: false + justificationForDisabling: 'NonProduction' + prefast: + enabled: false + justificationForDisabling: 'NonProduction' + codeQL: + compiled: + enabled: false + justificationForDisabling: 'NonProduction' + psscriptanalyzer: + enabled: false + justificationForDisabling: 'NonProduction' + credscan: + enabled: false + justificationForDisabling: 'NonProduction' + spmi: + enabled: false + justificationForDisabling: 'NonProduction' + componentgovernance: + enabled: false + justificationForDisabling: 'NonProduction' + eslint: + enabled: false + justificationForDisabling: 'NonProduction' + armory: + enabled: false + justificationForDisabling: 'NonProduction' customBuildTags: - ES365AIMigrationTooling stages: @@ -78,10 +96,10 @@ extends: parameters: enableMicrobuild: true enablePublishBuildArtifacts: true - enablePublishBuildAssets: true + enablePublishBuildAssets: false enablePublishTestResults: true - enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }} publishAssetsImmediately: true + isAssetlessBuild: true enableTelemetry: true helixRepo: dotnet/efcore jobs: @@ -95,15 +113,6 @@ extends: - _InternalBuildArgs: '' # Rely on task Arcade injects, not auto-injected build step. - skipComponentGovernanceDetection: true - - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - - _SignType: real - - _InternalBuildArgs: /p:DotNetSignType=$(_SignType) /p:TeamName=$(_TeamName) /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) /p:OfficialBuildId=$(BUILD.BUILDNUMBER) - # Ignore test and infrastructure code. - - Codeql.SourceRoot: src - # CodeQL3000 needs this plumbed along as a variable to enable TSA. - - Codeql.TSAEnabled: true - # Default expects tsaoptions.json under SourceRoot. - - Codeql.TSAOptionsPath: '$(Build.SourcesDirectory)/.config/tsaoptions.json' steps: - task: NuGetCommand@2 displayName: 'Clear NuGet caches' @@ -125,25 +134,6 @@ extends: env: Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl) displayName: Build - - task: CopyFiles@2 - displayName: 'Copy binaries for publishing' - inputs: - Contents: | - artifacts/bin/**/?(*.dll|*.exe|*.pdb) - !artifacts/bin/*Tests/** - TargetFolder: $(Agent.TempDirectory)/BinArtifacts - templateContext: - outputs: - - output: pipelineArtifact - displayName: Publish binaries - condition: always() - path: $(Agent.TempDirectory)/BinArtifacts/ - artifact: BinArtifacts - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - job: macOS pool: name: Azure Pipelines @@ -161,13 +151,6 @@ extends: # Work-around for https://github.com/dotnet/runtime/issues/70758 COMPlus_EnableWriteXorExecute: 0 displayName: Build - templateContext: - outputs: - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - job: Linux timeoutInMinutes: 180 pool: @@ -213,12 +196,6 @@ extends: sdl: binskim: prereleaseVersion: ' ' - outputs: - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - job: Helix timeoutInMinutes: 180 pool: @@ -266,13 +243,3 @@ extends: # Work-around for https://github.com/dotnet/runtime/issues/70758 COMPlus_EnableWriteXorExecute: 0 DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token) - - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - - template: /eng/common/templates-official/post-build/post-build.yml@self - parameters: - publishingInfraVersion: 3 - enableSigningValidation: false - enableNugetValidation: false - enableSourceLinkValidation: false - publishAssetsImmediately: true - SDLValidationParameters: - enable: false diff --git a/src/efcore/azure-pipelines-public.yml b/src/efcore/azure-pipelines-public.yml index 6a3bf435d1d..70d4bdfdb3f 100644 --- a/src/efcore/azure-pipelines-public.yml +++ b/src/efcore/azure-pipelines-public.yml @@ -45,6 +45,7 @@ stages: enablePublishBuildAssets: true enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }} publishAssetsImmediately: true + isAssetlessBuild: true enableSourceIndex: false enableTelemetry: true helixRepo: dotnet/efcore diff --git a/src/efcore/azure-pipelines.yml b/src/efcore/azure-pipelines.yml index 5f14dbd13bf..510a2ff8ce6 100644 --- a/src/efcore/azure-pipelines.yml +++ b/src/efcore/azure-pipelines.yml @@ -6,11 +6,7 @@ variables: - name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE value: true - name: _PublishUsingPipelines - value: true - - name: _CosmosConnectionUrl - value: https://localhost:8081 - - name: _CosmosToken - value: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw== + value: false - ${{ if or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), eq(variables['Build.Reason'], 'Manual')) }}: - name: PostBuildSign value: false @@ -59,6 +55,7 @@ extends: baselineFile: $(Build.SourcesDirectory)\.config\guardian\.gdnbaselines binskim: scanOutputDirectoryOnly: true + analyzeTargetGlob: '+:f|artifacts/bin/**/Microsoft.EntityFrameworkCore*.dll;+:f|artifacts/bin/**/Microsoft.Data.Sqlite*.dll;+:f|artifacts/bin/**/ef.exe;+:f|artifacts/bin/**/dotnet-ef.exe;-:f|artifacts/bin/**/shims/**/*.exe;' preReleaseVersion: '4.3.1' env: GDN_EXTRACT_TOOLS: "binskim" @@ -78,10 +75,10 @@ extends: parameters: enableMicrobuild: true enablePublishBuildArtifacts: true - enablePublishBuildAssets: true - enablePublishTestResults: true - enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }} + enablePublishBuildAssets: false + enablePublishTestResults: false publishAssetsImmediately: true + isAssetlessBuild: true enableTelemetry: true helixRepo: dotnet/efcore jobs: @@ -90,13 +87,14 @@ extends: name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2022 os: windows - timeoutInMinutes: 180 + timeoutInMinutes: 60 variables: - _InternalBuildArgs: '' # Rely on task Arcade injects, not auto-injected build step. - skipComponentGovernanceDetection: true - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - _SignType: real + - _Sign: true - _InternalBuildArgs: /p:DotNetSignType=$(_SignType) /p:TeamName=$(_TeamName) /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) /p:OfficialBuildId=$(BUILD.BUILDNUMBER) # Ignore test and infrastructure code. - Codeql.SourceRoot: src @@ -117,162 +115,7 @@ extends: - script: "echo ##vso[build.addbuildtag]release-candidate" condition: and(notin(variables['Build.Reason'], 'PullRequest'), eq(variables['IsFinalBuild'], 'true')) displayName: 'Set CI tags' - - powershell: SqlLocalDB start - displayName: Start LocalDB - - template: /eng/common/templates-official/steps/enable-internal-sources.yml - - template: /eng/common/templates-official/steps/enable-internal-runtimes.yml - - script: eng\common\cibuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_InternalBuildArgs) $(_InternalRuntimeDownloadArgs) - env: - Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl) - displayName: Build - - task: CopyFiles@2 - displayName: 'Copy binaries for publishing' - inputs: - Contents: | - artifacts/bin/**/?(*.dll|*.exe|*.pdb) - !artifacts/bin/*Tests/** - TargetFolder: $(Agent.TempDirectory)/BinArtifacts - templateContext: - outputs: - - output: pipelineArtifact - displayName: Publish binaries - condition: always() - path: $(Agent.TempDirectory)/BinArtifacts/ - artifact: BinArtifacts - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - - job: macOS - pool: - name: Azure Pipelines - image: macOS-13 - os: macOS - variables: - # Rely on task Arcade injects, not auto-injected build step. - - skipComponentGovernanceDetection: true - steps: - - template: /eng/common/templates-official/steps/enable-internal-sources.yml - - template: /eng/common/templates-official/steps/enable-internal-runtimes.yml - - script: eng/common/build.sh --restore --build --test --pack --ci --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs) - env: - Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl) - # Work-around for https://github.com/dotnet/runtime/issues/70758 - COMPlus_EnableWriteXorExecute: 0 - displayName: Build - templateContext: - outputs: - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - - job: Linux - timeoutInMinutes: 180 - pool: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals 1es-ubuntu-2204 - os: linux - variables: - - _runCounter: $[counter(variables['Build.Reason'], 0)] - # Rely on task Arcade injects, not auto-injected build step. - - skipComponentGovernanceDetection: true - - ${{ if notin(variables['Build.Reason'], 'PullRequest', 'Schedule') }}: - - _CosmosConnectionUrl: 'true' - steps: - - bash: | - echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-nightly-test.documents.azure.com:443/" - displayName: Prepare to run Cosmos tests on ef-nightly-test - condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '0'), endsWith(variables['_runCounter'], '2'), endsWith(variables['_runCounter'], '4'), endsWith(variables['_runCounter'], '6'), endsWith(variables['_runCounter'], '8'))) - - bash: | - echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-pr-test.documents.azure.com:443/" - displayName: Prepare to run Cosmos tests on ef-pr-test - condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9'))) - template: /eng/common/templates-official/steps/enable-internal-sources.yml - template: /eng/common/templates-official/steps/enable-internal-runtimes.yml - - script: eng/common/build.sh --restore --build --test --pack --ci --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs) + - script: eng\common\build.cmd -restore -build -sign -pack -publish -ci -configuration $(_BuildConfig) -prepareMachine $(_InternalRuntimeDownloadArgs) displayName: Build - - task: AzureCLI@2 - displayName: Run Cosmos tests - condition: notin(variables['Build.Reason'], 'PullRequest', 'Schedule') - inputs: - azureSubscription: EFCosmosTesting - addSpnToEnvironment: true - scriptType: bash - scriptLocation: 'inlineScript' - inlineScript: | - ./test.sh --ci --configuration $(_BuildConfig) --projects $(Build.SourcesDirectory)/test/EFCore.Cosmos.FunctionalTests/EFCore.Cosmos.FunctionalTests.csproj - env: - Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl) - Test__Cosmos__UseTokenCredential: true - Test__Cosmos__SubscriptionId: d709b837-4a74-4aec-addc-b6e4b9b23e7e - Test__Cosmos__ResourceGroup: efcosmosci - name: Build - templateContext: - sdl: - binskim: - prereleaseVersion: ' ' - outputs: - - output: pipelineArtifact - displayName: Upload TestResults - condition: always() - path: artifacts/TestResults/$(_BuildConfig)/ - artifact: $(Agent.Os)_$(Agent.JobName) TestResults - - job: Helix - timeoutInMinutes: 180 - pool: - name: $(DncEngInternalBuildPool) - demands: ImageOverride -equals 1es-windows-2022 - os: windows - variables: - # Rely on task Arcade injects, not auto-injected build step. - - skipComponentGovernanceDetection: true - - name: _HelixBuildConfig - value: $(_BuildConfig) - - name: HelixTargetQueues - value: Windows.10.Amd64;OSX.13.Amd64;OSX.13.ARM64;Ubuntu.2204.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-22.04-helix-sqlserver-amd64 - - name: _HelixAccessToken - # Needed for internal queues - value: $(HelixApiAccessToken) - steps: - - task: NuGetCommand@2 - displayName: 'Clear NuGet caches' - condition: succeeded() - inputs: - command: custom - arguments: 'locals all -clear' - - template: /eng/common/templates-official/steps/enable-internal-sources.yml - - template: /eng/common/templates-official/steps/enable-internal-runtimes.yml - - ${{ if ne(variables['System.TeamProject'], 'public') }}: - - template: /eng/common/core-templates/steps/get-delegation-sas.yml - parameters: - federatedServiceConnection: 'dotnetbuilds-internal-read' - outputVariableName: 'dotnetbuilds-internal-container-read-token' - expiryInHours: 1 - base64Encode: false - storageAccount: dotnetbuilds - container: internal - permissions: rl - - script: restore.cmd -ci /p:configuration=$(_BuildConfig) $(_InternalRuntimeDownloadArgs) - displayName: Restore packages - - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog $(_InternalRuntimeDownloadArgs) - displayName: Send job to helix - env: - HelixAccessToken: $(_HelixAccessToken) - # We need to set this env var to publish helix results to Azure Dev Ops - SYSTEM_ACCESSTOKEN: $(System.AccessToken) - MSSQL_SA_PASSWORD: "PLACEHOLDERPass$$w0rd" - # Work-around for https://github.com/dotnet/runtime/issues/70758 - COMPlus_EnableWriteXorExecute: 0 - DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token) - - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}: - - template: /eng/common/templates-official/post-build/post-build.yml@self - parameters: - publishingInfraVersion: 3 - enableSigningValidation: false - enableNugetValidation: false - enableSourceLinkValidation: false - publishAssetsImmediately: true - SDLValidationParameters: - enable: false diff --git a/src/efcore/eng/Version.Details.props b/src/efcore/eng/Version.Details.props index 0549f6aee01..397aed1c2f8 100644 --- a/src/efcore/eng/Version.Details.props +++ b/src/efcore/eng/Version.Details.props @@ -6,24 +6,24 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.25479.115 - 10.0.0-beta.25479.115 - 10.0.0-beta.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 - 10.0.0-rtm.25479.115 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 diff --git a/src/efcore/eng/Version.Details.xml b/src/efcore/eng/Version.Details.xml index cda2ae34f9c..709bc9ecf05 100644 --- a/src/efcore/eng/Version.Details.xml +++ b/src/efcore/eng/Version.Details.xml @@ -1,80 +1,80 @@ - + - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e72b5bbe719d747036ce9c36582a205df9f1c361 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac diff --git a/src/efcore/global.json b/src/efcore/global.json index 7576fe190a4..a83f17df244 100644 --- a/src/efcore/global.json +++ b/src/efcore/global.json @@ -18,7 +18,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25479.115", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25479.115" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25509.106", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25509.106" } } diff --git a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs index 392a94f28a9..433b0b317c1 100644 --- a/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs +++ b/src/efcore/src/EFCore.Relational/Query/Internal/RelationalParameterProcessor.cs @@ -31,7 +31,7 @@ private readonly IDictionary _visitedFromSqlExpre /// (i.e. they're prefixed), since /// can be prefixed or not. /// - private readonly HashSet _prefixedParameterNames = []; + private readonly HashSet _prefixedParameterNames = new(StringComparer.OrdinalIgnoreCase); private readonly Dictionary _sqlParameters = new(); diff --git a/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs index 8d066cc59e5..f91ad1dee97 100644 --- a/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs +++ b/src/efcore/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs @@ -1529,13 +1529,39 @@ public override async Task Using_same_parameter_twice_in_query_generates_one_sql AssertSql(); } - public override async Task Two_parameters_with_same_name_get_uniquified(bool async) - { - // Concat with conversion, issue #34963. - await AssertTranslationFailed(() => base.Using_same_parameter_twice_in_query_generates_one_sql_parameter(async)); + public override Task Two_parameters_with_same_name_get_uniquified(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Two_parameters_with_same_name_get_uniquified(async); - AssertSql(); - } + AssertSql( + """ +@customerId='ANATR' +@customerId0='ALFKI' + +SELECT VALUE c +FROM root c +WHERE ((c["id"] = @customerId) OR (c["id"] = @customerId0)) +"""); + }); + + public override Task Two_parameters_with_same_case_insensitive_name_get_uniquified(bool async) + => Fixture.NoSyncTest( + async, async a => + { + await base.Two_parameters_with_same_case_insensitive_name_get_uniquified(async); + + AssertSql( + """ +@customerID='ANATR' +@customerId='ALFKI' + +SELECT VALUE c +FROM root c +WHERE ((c["id"] = @customerID) OR (c["id"] = @customerId)) +"""); + }); public override async Task Where_Queryable_ToList_Count(bool async) { diff --git a/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs index 69c990d6845..e4beae2c27e 100644 --- a/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs +++ b/src/efcore/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs @@ -1327,16 +1327,27 @@ public virtual Task Using_same_parameter_twice_in_query_generates_one_sql_parame .Select(c => c.CustomerID)); } + private readonly string customerId = "ALFKI"; + [ConditionalTheory, MemberData(nameof(IsAsyncData))] public virtual Task Two_parameters_with_same_name_get_uniquified(bool async) { - var i = 10; + var customerId = "ANATR"; + + return AssertQuery( + async, + ss => ss.Set().Where(c => c.CustomerID == customerId || c.CustomerID == this.customerId)); + } + + [ConditionalTheory, MemberData(nameof(IsAsyncData))] + public virtual Task Two_parameters_with_same_case_insensitive_name_get_uniquified(bool async) + { + var customerID = "ANATR"; - // i+1 and i+2 each get parameterized using the same parameter name (since they're complex expressions). - // This exercises that query parameters are properly uniquified. + // Note the parameter names differ only by case (customerID vs. customerId) return AssertQuery( async, - ss => ss.Set().Where(c => (c.CustomerID + (i + 1)) + (c.CustomerID + (i + 2)) == "ALFKI11ALFKI12")); + ss => ss.Set().Where(c => c.CustomerID == customerID || c.CustomerID == customerId)); } [ConditionalTheory, MemberData(nameof(IsAsyncData))] diff --git a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs index bd0137646c4..63a234e7e7c 100644 --- a/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs +++ b/src/efcore/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindWhereQuerySqlServerTest.cs @@ -1704,19 +1704,33 @@ WHERE CAST(@i AS nvarchar(max)) + [c].[CustomerID] + CAST(@i AS nvarchar(max)) = """); } - [ConditionalTheory] public override async Task Two_parameters_with_same_name_get_uniquified(bool async) { await base.Two_parameters_with_same_name_get_uniquified(async); AssertSql( """ -@p='11' -@p0='12' +@customerId='ANATR' (Size = 5) (DbType = StringFixedLength) +@customerId0='ALFKI' (Size = 5) (DbType = StringFixedLength) + +SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +FROM [Customers] AS [c] +WHERE [c].[CustomerID] = @customerId OR [c].[CustomerID] = @customerId0 +"""); + } + + public override async Task Two_parameters_with_same_case_insensitive_name_get_uniquified(bool async) + { + await base.Two_parameters_with_same_case_insensitive_name_get_uniquified(async); + +AssertSql( +""" +@customerID='ANATR' (Size = 5) (DbType = StringFixedLength) +@customerId0='ALFKI' (Size = 5) (DbType = StringFixedLength) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] -WHERE [c].[CustomerID] + CAST(@p AS nvarchar(max)) + [c].[CustomerID] + CAST(@p0 AS nvarchar(max)) = N'ALFKI11ALFKI12' +WHERE [c].[CustomerID] = @customerID OR [c].[CustomerID] = @customerId0 """); } diff --git a/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs b/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs index 21db8554f0a..fc1a8a78f1e 100644 --- a/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs +++ b/src/razor/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorCohostingOptions.cs @@ -5,5 +5,5 @@ internal static class RazorCohostingOptions /// /// True if razor is running in the cohosting mode /// - internal static bool UseRazorCohostServer { get; set; } = true; + internal static bool UseRazorCohostServer { get; set; } = false; } diff --git a/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs b/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs index 8c78aaec0db..20e5cba815c 100644 --- a/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs +++ b/src/razor/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorTests.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Language.Syntax; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Test.Utilities; @@ -2616,9 +2615,6 @@ public async Task SourceGenerator_DoesNotUpdateSources_WhenSourceGeneratorIsSupp // start with the generator suppressed (this is the default state in VS) driver = SetSuppressionState(true); - // Disable co-hosting, this test only applies to non-cohosting scenarios - RazorCohostingOptions.UseRazorCohostServer = false; - // results should be empty, and no recorded steps should have run using var eventListener = new RazorEventListener(); var result = RunGenerator(compilation!, ref driver).VerifyPageOutput(); @@ -3461,7 +3457,6 @@ public async Task UseRazorCohostServer_CanOverride_Suppression() ["Component.Razor"] = "

Hello world

", }); var compilation = await project.GetCompilationAsync(); - RazorCohostingOptions.UseRazorCohostServer = false; // Start with the generator suppressed var (driver, additionalTexts, optionsProvider) = await GetDriverWithAdditionalTextAndProviderAsync(project, configureGlobalOptions: (o) => diff --git a/src/razor/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorAnalyzerAssemblyRedirector.cs b/src/razor/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorAnalyzerAssemblyRedirector.cs index 2da57734d95..092ce70ba9e 100644 --- a/src/razor/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorAnalyzerAssemblyRedirector.cs +++ b/src/razor/src/Razor/src/Microsoft.CodeAnalysis.Razor.CohostingShared/RazorAnalyzerAssemblyRedirector.cs @@ -25,8 +25,10 @@ internal sealed class RazorAnalyzerAssemblyRedirector() : IRazorAnalyzerAssembly GetRedirectEntry(typeof(AspNetCore.Razor.ArgHelper)), // Microsoft.AspNetCore.Razor.Utilities.Shared // The following dependencies will be provided by the Compiler ALC so its not strictly required to redirect them, but we do so for completeness. - GetRedirectEntry(typeof(Microsoft.Extensions.ObjectPool.ObjectPool)), // Microsoft.Extensions.ObjectPool - GetRedirectEntry(typeof(ImmutableArray)) // System.Collections.Immutable + GetRedirectEntry(typeof(ImmutableArray)), // System.Collections.Immutable + + // ObjectPool is special + GetObjectPoolRedirect() // Microsoft.Extensions.ObjectPool ]; private static readonly FrozenDictionary s_compilerAssemblyMap = s_compilerAssemblyTypes.ToFrozenDictionary(t => t.name, t => t.path); @@ -37,6 +39,53 @@ internal sealed class RazorAnalyzerAssemblyRedirector() : IRazorAnalyzerAssembly return s_compilerAssemblyMap.TryGetValue(name, out var path) ? path : null; } + private static (string name, string path) GetObjectPoolRedirect() + { + // Temporary fix for: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2583134 + // In VS (where this code is running) ObjectPool comes from the shared assemblies folder. + + // When Roslyn wins the race to load Razor it shadow copies the assemblies before resolving them. Roslyn shadow copies assemblies + // into a directory based on the directory they came from. Because ObjectPool has a different directory path from the rest of + // our assemblies, we end up with two shadow copy directories: one for ObjectPool and one for everything else. + + // Roslyn creates an ALC per directory where the assemblies are loaded from. + // ObjectPool isn't loaded into the same ALC as the rest of the compiler. When we come to use the ObjectPool in the compiler, it + // has to be resolved because its not already loaded. + // This invokes our assembly resolver code (which is currently in the Roslyn EA). However, our resolver expects to find the ObjectPool assembly + // to load next to the compiler assembly, which it isn't because of the shadow copying. It fails to load ObjectPool. That then means resolution falls back to + // the ServiceHub loader, which *is* able to successfully load a copy of the assembly from the framework, but into its own ALC. This is the copy of the + // ObjectPool that the compiler 'binds' against. Call this ObjectPool(1). + + // When Razor tooling starts up, it also wants to load the same assemblies, and goes through the assembly resolver for any razor assemblies. + // Because it doesn't consider shadow copying, it requests them from a different path than Roslyn loaded them from (the razor language services folder). + // Because the compiler assemblies are already loaded (from the shadow copy folder) the resolver just returns those assemblies. These are, in fact the + // same assemblies so this is correct up to this point. + // However, when the tooling goes to load ObjectPool it calls into the resolver with the path from *the razor language services folder*. The resolver + // previously failed to load ObjectPool, so it tries again. This time, with the updated path, it is able to find it next to the compiler + // assembly and successfully load it. Call this ObjectPool(2) + + // That means that the razor tooling has the compiler assemblies and ObjectPool(2), which are all in the same ALC. However, because of the earlier failed + // load the compiler assemblies are 'bound' to ObjectPool(1). That causes a MissingMethodException when we come to use any of the methods from the compiler + // assemblies: it is looking for a method bound to ObjectPool(1), but can't find it because it has ObjectPool(2). + + // This doesn't happen if Razor tooling loads first. It is able to resolve ObjectPool at the same time as the compiler assemblies, and 'binds' them. When + // the source generator is loaded by Roslyn all of the assemblies are already loaded and can just be re-used. + + // The correct fix is to not shadow copy razor assemblies (see https://github.com/dotnet/razor/issues/12307) which will ensure that the initial resolve + // is able to find the assembly and load it into the same ALC as the compiler assemblies when Roslyn wins the race. + + // For now, to ensure consistent assembly loading and avoid ALC mismatches, explicitly override the location of ObjectPool to be next to the compiler assemblies. + // This ensures that when Roslyn does the shadow copy, all assemblies end up in the same directory and ALC, allowing them to be re-used when Razor loads. + + // Get the compiler assembly location + var (_, compilerAssemblyLocation) = GetRedirectEntry(typeof(CodeAnalysis.Razor.CompilerFeatures)); + + // Get the redirect for the object pool, but override its location to be next to the compiler + var objectPoolRedirect = GetRedirectEntry(typeof(Microsoft.Extensions.ObjectPool.ObjectPool)); + objectPoolRedirect.path = Path.Combine(Path.GetDirectoryName(compilerAssemblyLocation)!, $"{objectPoolRedirect.name}.dll"); + return objectPoolRedirect; + } + private static (string name, string path) GetRedirectEntry(Type type, string? overrideName = null) { return ( diff --git a/src/roslyn/eng/Version.Details.props b/src/roslyn/eng/Version.Details.props index 51efebaf0a7..e24a868f47c 100644 --- a/src/roslyn/eng/Version.Details.props +++ b/src/roslyn/eng/Version.Details.props @@ -9,7 +9,7 @@ This file should be imported by eng/Versions.props 3.11.0 4.10.0-1.24061.4 - 2.0.0-rtm.25507.103 + 2.0.0-rtm.25509.106 9.0.0 9.0.0 diff --git a/src/roslyn/eng/Version.Details.xml b/src/roslyn/eng/Version.Details.xml index 79cd3e0eec7..bf9bdd564bc 100644 --- a/src/roslyn/eng/Version.Details.xml +++ b/src/roslyn/eng/Version.Details.xml @@ -1,15 +1,15 @@ - + https://github.com/dotnet/roslyn ae1fff344d46976624e68ae17164e0607ab68b10 - + https://github.com/dotnet/dotnet - 915977521b41280adbe21a54345be23cb5bf3536 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs index bb6dc5aecab..2c98eac46a0 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.cs @@ -4640,5 +4640,17 @@ void M() Assert.Same(analyzer1.SeenOptions, analyzer2.SeenOptions); } } + + [Fact] + public async Task TestGetAnalysisResultAsyncWithUnknownAnalyzerThrows() + { + var compilation = CreateCompilation("").VerifyDiagnostics(); + + var compilationWithAnalyzers = compilation.WithAnalyzers([new CSharpCompilerDiagnosticAnalyzer()]); + + // Calling with a DiagnosticAnalyzer that was not specified in 'WithAnalyzers' should throw. + await Assert.ThrowsAnyAsync(() => + compilationWithAnalyzers.GetAnalysisResultAsync([new CSharpCompilerDiagnosticAnalyzer()], CancellationToken.None)); + } } } diff --git a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs index 3a7a679a1ab..19f59d0bd4f 100644 --- a/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs +++ b/src/roslyn/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs @@ -1590,6 +1590,61 @@ internal class TestAttribute : Attribute Assert.Equal(DiagnosticSuppressorForCS0657.SuppressionId, suppression.Descriptor.Id); } + [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2581061")] + public async Task TestDiagnosticSuppressor_GetAnalysisResultAsyncCrash() + { + var source = """ + using System; + + public class SomeClass + { + [property: Test] + public string Name; + } + + internal class TestAttribute : Attribute + { + } + """; + var compilation = CreateCompilation(source).VerifyDiagnostics( + // (5,6): warning CS0657: 'property' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'field'. All attributes in this block will be ignored. + // [property: Test] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "property").WithArguments("property", "field").WithLocation(5, 6)); + + // Verify that CS0657 can be suppressed with a DiagnosticSuppressor + var diagnosticAnalyzer = new CSharpCompilerDiagnosticAnalyzer(); + var suppressor = new DiagnosticSuppressorForCS0657(); + + // Create options with our own getAnalyzerConfigOptionsProvider. That way we do initialize the map from + // analyzer to options within the AnalyzerDriver. This map needs to contain all analyzers and suppressors, + // not just the one we're calling into with GetAnalysisResultAsync. + var options = new CompilationWithAnalyzersOptions( + AnalyzerOptions.Empty, + onAnalyzerException: null, + concurrentAnalysis: false, + logAnalyzerExecutionTime: false, + reportSuppressedDiagnostics: true, + analyzerExceptionFilter: null, + getAnalyzerConfigOptionsProvider: _ => new CompilerAnalyzerConfigOptionsProvider( + ImmutableDictionary.Empty, + new DictionaryAnalyzerConfigOptions(ImmutableDictionary.Empty))); + + var compilationWithAnalyzers = compilation.WithAnalyzers([diagnosticAnalyzer, suppressor], options); + + // Calling in with a single analyzer should still properly setup internal maps so that suppressors run properly. + var analysisResult1 = await compilationWithAnalyzers.GetAnalysisResultAsync([diagnosticAnalyzer], CancellationToken.None); + var analysisResult2 = await compilationWithAnalyzers.GetAnalysisResultAsync([diagnosticAnalyzer, suppressor], CancellationToken.None); + + var diagnostic1 = analysisResult1.SemanticDiagnostics.Single().Value.Single().Value.Single(); + var diagnostic2 = analysisResult2.SemanticDiagnostics.Single().Value.Single().Value.Single(); + + Assert.True(diagnostic1.IsSuppressed); + Assert.True(diagnostic2.IsSuppressed); + + Assert.Equal("CS0657", diagnostic1.Id); + Assert.Equal("CS0657", diagnostic2.Id); + } + [DiagnosticAnalyzer(LanguageNames.CSharp)] private sealed class DiagnosticSuppressorForCS0657 : DiagnosticSuppressor { diff --git a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs index 385e075a73c..134ee76bc9d 100644 --- a/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs +++ b/src/roslyn/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs @@ -518,7 +518,7 @@ internal void Initialize( var analyzerExecutor = AnalyzerExecutor.Create( compilation, options, addNotCategorizedDiagnostic, newOnAnalyzerException, analysisOptions.AnalyzerExceptionFilter, - IsCompilerAnalyzer, analysisScope.Analyzers, analysisOptions.GetAnalyzerConfigOptionsProvider, + IsCompilerAnalyzer, this.Analyzers, analysisOptions.GetAnalyzerConfigOptionsProvider, AnalyzerManager, ShouldSkipAnalysisOnGeneratedCode, ShouldSuppressGeneratedCodeDiagnostic, IsGeneratedOrHiddenCodeLocation, IsAnalyzerSuppressedForTree, GetAnalyzerGate, getSemanticModel: GetOrCreateSemanticModel, _severityFilter, analysisOptions.LogAnalyzerExecutionTime, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, s => _programmaticSuppressions!.Add(s)); diff --git a/src/runtime/src/coreclr/jit/lclmorph.cpp b/src/runtime/src/coreclr/jit/lclmorph.cpp index cd464797e79..ecb01980013 100644 --- a/src/runtime/src/coreclr/jit/lclmorph.cpp +++ b/src/runtime/src/coreclr/jit/lclmorph.cpp @@ -2020,7 +2020,7 @@ class LocalAddressVisitor final : public GenTreeVisitor } if ((genTypeSize(indir) == genTypeSize(varDsc)) && (genTypeSize(indir) <= TARGET_POINTER_SIZE) && - (varTypeIsFloating(indir) || varTypeIsFloating(varDsc))) + (varTypeIsFloating(indir) || varTypeIsFloating(varDsc)) && !varDsc->lvPromoted) { return IndirTransform::BitCast; } diff --git a/src/runtime/src/coreclr/vm/appdomain.cpp b/src/runtime/src/coreclr/vm/appdomain.cpp index 7b247b2b8bf..f7d01fd809a 100644 --- a/src/runtime/src/coreclr/vm/appdomain.cpp +++ b/src/runtime/src/coreclr/vm/appdomain.cpp @@ -1871,7 +1871,7 @@ DispIDCache* AppDomain::SetupRefDispIDCache() #endif // FEATURE_COMINTEROP -FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEAssembly * pPEAssembly, Assembly *pAssembly) +FileLoadLock* FileLoadLock::Create(PEFileListLock* pLock, PEAssembly* pPEAssembly) { CONTRACTL { @@ -1884,7 +1884,7 @@ FileLoadLock *FileLoadLock::Create(PEFileListLock *pLock, PEAssembly * pPEAssemb } CONTRACTL_END; - NewHolder result(new FileLoadLock(pLock, pPEAssembly, pAssembly)); + NewHolder result(new FileLoadLock(pLock, pPEAssembly)); pLock->AddElement(result); result->AddRef(); // Add one ref on behalf of the ListLock's reference. The corresponding Release() happens in FileLoadLock::CompleteLoadLevel. @@ -1910,6 +1910,14 @@ Assembly *FileLoadLock::GetAssembly() return m_pAssembly; } +PEAssembly* FileLoadLock::GetPEAssembly() +{ + LIMITED_METHOD_CONTRACT; + // Underlying PEAssembly pointer is stored in the constructor in base ListLockEntry::m_data. + _ASSERTE(m_data != NULL); + return (PEAssembly*)m_data; +} + FileLoadLevel FileLoadLock::GetLoadLevel() { LIMITED_METHOD_CONTRACT; @@ -1958,6 +1966,7 @@ BOOL FileLoadLock::CanAcquire(FileLoadLevel targetLevel) static const char *fileLoadLevelName[] = { "CREATE", // FILE_LOAD_CREATE + "ALLOCATE", // FILE_LOAD_ALLOCATE "BEGIN", // FILE_LOAD_BEGIN "BEFORE_TYPE_LOAD", // FILE_LOAD_BEFORE_TYPE_LOAD "EAGER_FIXUPS", // FILE_LOAD_EAGER_FIXUPS @@ -1983,7 +1992,9 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success) if (level > m_level) { // Must complete each level in turn, unless we have an error - CONSISTENCY_CHECK(m_pAssembly->IsError() || (level == (m_level+1))); + CONSISTENCY_CHECK((level == (m_level+1)) || (m_pAssembly != nullptr && m_pAssembly->IsError())); + CONSISTENCY_CHECK(m_pAssembly != nullptr || level < FILE_LOAD_ALLOCATE); + // Remove the lock from the list if the load is completed if (level >= FILE_ACTIVE) { @@ -2019,7 +2030,7 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success) { m_level = (FileLoadLevel)level; - if (success) + if (success && level >= FILE_LOAD_ALLOCATE) m_pAssembly->SetLoadLevel(level); } @@ -2042,6 +2053,18 @@ BOOL FileLoadLock::CompleteLoadLevel(FileLoadLevel level, BOOL success) return FALSE; } +void FileLoadLock::SetAssembly(Assembly* pAssembly) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(HasLock()); + _ASSERTE(m_level == FILE_LOAD_CREATE); // Only valid to set during CREATE -> ALLOCATE + _ASSERTE(m_pAssembly == nullptr); + _ASSERTE(pAssembly != nullptr && pAssembly->GetPEAssembly() == (PEAssembly *)m_data); + + m_pAssembly = pAssembly; +} + void FileLoadLock::SetError(Exception *ex) { CONTRACTL @@ -2088,10 +2111,10 @@ UINT32 FileLoadLock::Release() return count; } -FileLoadLock::FileLoadLock(PEFileListLock *pLock, PEAssembly * pPEAssembly, Assembly *pAssembly) +FileLoadLock::FileLoadLock(PEFileListLock* pLock, PEAssembly* pPEAssembly) : ListLockEntry(pLock, pPEAssembly, "File load lock"), m_level((FileLoadLevel) (FILE_LOAD_CREATE)), - m_pAssembly(pAssembly), + m_pAssembly(nullptr), m_cachedHR(S_OK) { WRAPPER_NO_CONTRACT; @@ -2420,23 +2443,6 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity, if (result == NULL) { - LoaderAllocator *pLoaderAllocator = NULL; - - AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder(); - // Assemblies loaded with CustomAssemblyBinder need to use a different LoaderAllocator if - // marked as collectible - pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator(); - if (pLoaderAllocator == NULL) - { - pLoaderAllocator = this->GetLoaderAllocator(); - } - - // Allocate the DomainAssembly a bit early to avoid GC mode problems. We could potentially avoid - // a rare redundant allocation by moving this closer to FileLoadLock::Create, but it's not worth it. - AllocMemTracker amTracker; - AllocMemTracker *pamTracker = &amTracker; - NewHolder pDomainAssembly = new DomainAssembly(pPEAssembly, pLoaderAllocator, pamTracker); - LoadLockHolder lock(this); // Find the list lock entry @@ -2448,20 +2454,9 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity, result = FindAssembly(pPEAssembly, FindAssemblyOptions_IncludeFailedToLoad); if (result == NULL) { - // We are the first one in - create the DomainAssembly + // We are the first one in - create the FileLoadLock. Creation of the Assembly will happen at FILE_LOAD_ALLOCATE stage registerNewAssembly = true; - fileLock = FileLoadLock::Create(lock, pPEAssembly, pDomainAssembly->GetAssembly()); - pDomainAssembly.SuppressRelease(); - pamTracker->SuppressRelease(); - - // Set the assembly module to be tenured now that we know it won't be deleted - pDomainAssembly->GetAssembly()->SetIsTenured(); - if (pDomainAssembly->GetAssembly()->IsCollectible()) - { - // We add the assembly to the LoaderAllocator only when we are sure that it can be added - // and won't be deleted in case of a concurrent load from the same ALC - ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly); - } + fileLock = FileLoadLock::Create(lock, pPEAssembly); } } else @@ -2479,6 +2474,8 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity, // so it will not be removed until app domain unload. So there is no need // to release our ref count. result = LoadAssembly(fileLock, targetLevel); + // By now FILE_LOAD_ALLOCATE should have run and the Assembly should exist + _ASSERTE(result != NULL); } else { @@ -2487,7 +2484,7 @@ Assembly *AppDomain::LoadAssemblyInternal(AssemblySpec* pIdentity, if (registerNewAssembly) { - pPEAssembly->GetAssemblyBinder()->AddLoadedAssembly(pDomainAssembly->GetAssembly()); + pPEAssembly->GetAssemblyBinder()->AddLoadedAssembly(result); } } else @@ -2517,6 +2514,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLock)); PRECONDITION(AppDomain::GetCurrentDomain() == this); + PRECONDITION(targetLevel >= FILE_LOAD_ALLOCATE); POSTCONDITION(RETVAL->GetLoadLevel() >= GetCurrentFileLoadLevel() || RETVAL->GetLoadLevel() >= targetLevel); POSTCONDITION(RETVAL->CheckNoError(targetLevel)); @@ -2531,6 +2529,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel // Do a quick out check for the already loaded case. if (pLock->GetLoadLevel() >= targetLevel) { + _ASSERTE(pAssembly != nullptr); pAssembly->ThrowIfError(targetLevel); RETURN pAssembly; @@ -2553,8 +2552,9 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel if (immediateTargetLevel > limit.GetLoadLevel()) immediateTargetLevel = limit.GetLoadLevel(); + const char *simpleName = pLock->GetPEAssembly()->GetSimpleName(); LOG((LF_LOADER, LL_INFO100, "LOADER: ***%s*\t>>>Load initiated, %s/%s\n", - pAssembly->GetSimpleName(), + simpleName, fileLoadLevelName[immediateTargetLevel], fileLoadLevelName[targetLevel])); // Now loop and do the load incrementally to the target level. @@ -2577,30 +2577,32 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel LOG((LF_LOADER, (workLevel == FILE_LOAD_BEGIN - || workLevel == FILE_LOADED - || workLevel == FILE_ACTIVE) - ? LL_INFO10 : LL_INFO1000, - "LOADER: %p:***%s*\t loading at level %s\n", - this, pAssembly->GetSimpleName(), fileLoadLevelName[workLevel])); + || workLevel == FILE_LOADED + || workLevel == FILE_ACTIVE) + ? LL_INFO10 : LL_INFO1000, + "LOADER: %p:***%s*\t loading at level %s\n", + this, simpleName, fileLoadLevelName[workLevel])); - TryIncrementalLoad(pAssembly, workLevel, fileLock); + TryIncrementalLoad(workLevel, fileLock); } } if (pLock->GetLoadLevel() == immediateTargetLevel-1) { LOG((LF_LOADER, LL_INFO100, "LOADER: ***%s*\t<<GetSimpleName(), + simpleName, fileLoadLevelName[immediateTargetLevel-1])); } } LOG((LF_LOADER, LL_INFO100, "LOADER: ***%s*\t<<GetSimpleName(), + simpleName, fileLoadLevelName[pLock->GetLoadLevel()])); - } + pAssembly = pLock->GetAssembly(); + _ASSERTE(pAssembly != nullptr); // We should always be loading to at least FILE_LOAD_ALLOCATE, so the assembly should be created + // There may have been an error stored on the domain file by another thread, or from a previous load pAssembly->ThrowIfError(targetLevel); @@ -2624,7 +2626,7 @@ Assembly *AppDomain::LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel RETURN pAssembly; } -void AppDomain::TryIncrementalLoad(Assembly *pAssembly, FileLoadLevel workLevel, FileLoadLockHolder &lockHolder) +void AppDomain::TryIncrementalLoad(FileLoadLevel workLevel, FileLoadLockHolder& lockHolder) { STANDARD_VM_CONTRACT; @@ -2632,11 +2634,41 @@ void AppDomain::TryIncrementalLoad(Assembly *pAssembly, FileLoadLevel workLevel, BOOL released = FALSE; FileLoadLock* pLoadLock = lockHolder.GetValue(); + Assembly* pAssembly = pLoadLock->GetAssembly(); EX_TRY { - // Do the work - BOOL success = pAssembly->DoIncrementalLoad(workLevel); + BOOL success; + if (workLevel == FILE_LOAD_ALLOCATE) + { + // FileLoadLock should not have an assembly yet + _ASSERTE(pAssembly == NULL); + + // Allocate DomainAssembly & Assembly + PEAssembly *pPEAssembly = pLoadLock->GetPEAssembly(); + AssemblyBinder *pAssemblyBinder = pPEAssembly->GetAssemblyBinder(); + LoaderAllocator *pLoaderAllocator = pAssemblyBinder->GetLoaderAllocator(); + if (pLoaderAllocator == NULL) + pLoaderAllocator = this->GetLoaderAllocator(); + + AllocMemTracker amTracker; + AllocMemTracker *pamTracker = &amTracker; + NewHolder pDomainAssembly = new DomainAssembly(pPEAssembly, pLoaderAllocator, pamTracker); + pLoadLock->SetAssembly(pDomainAssembly->GetAssembly()); + pDomainAssembly->GetAssembly()->SetIsTenured(); + if (pDomainAssembly->GetAssembly()->IsCollectible()) + { + ((AssemblyLoaderAllocator *)pLoaderAllocator)->AddDomainAssembly(pDomainAssembly); + } + pDomainAssembly.SuppressRelease(); + pamTracker->SuppressRelease(); + pAssembly = pLoadLock->GetAssembly(); + success = TRUE; + } + else + { + success = pAssembly->DoIncrementalLoad(workLevel); + } // Complete the level. if (pLoadLock->CompleteLoadLevel(workLevel, success) && @@ -2651,9 +2683,9 @@ void AppDomain::TryIncrementalLoad(Assembly *pAssembly, FileLoadLevel workLevel, { Exception *pEx = GET_EXCEPTION(); - //We will cache this error and wire this load to forever fail, + // We will cache this error and wire this load to forever fail, // unless the exception is transient or the file is loaded OK but just cannot execute - if (!pEx->IsTransient() && !pAssembly->IsLoaded()) + if (pAssembly != nullptr && !pEx->IsTransient() && !pAssembly->IsLoaded()) { if (released) { diff --git a/src/runtime/src/coreclr/vm/appdomain.hpp b/src/runtime/src/coreclr/vm/appdomain.hpp index 8a42c4d7cd2..a58a2314ee0 100644 --- a/src/runtime/src/coreclr/vm/appdomain.hpp +++ b/src/runtime/src/coreclr/vm/appdomain.hpp @@ -293,14 +293,15 @@ class FileLoadLock : public ListLockEntry { private: FileLoadLevel m_level; - Assembly* m_pAssembly; + Assembly* m_pAssembly; // Will be null until FILE_LOAD_ALLOCATE is completed successfully HRESULT m_cachedHR; public: - static FileLoadLock *Create(PEFileListLock *pLock, PEAssembly *pPEAssembly, Assembly *pAssembly); + static FileLoadLock* Create(PEFileListLock* pLock, PEAssembly* pPEAssembly); ~FileLoadLock(); Assembly *GetAssembly(); + PEAssembly* GetPEAssembly(); FileLoadLevel GetLoadLevel(); // CanAcquire will return FALSE if Acquire will definitely not take the lock due @@ -320,6 +321,9 @@ class FileLoadLock : public ListLockEntry // returns TRUE if it updated load level, FALSE if the level was set already BOOL CompleteLoadLevel(FileLoadLevel level, BOOL success); + // Associate an Assembly with this lock + void SetAssembly(Assembly* pAssembly); + void SetError(Exception *ex); void AddRef(); @@ -327,7 +331,7 @@ class FileLoadLock : public ListLockEntry private: - FileLoadLock(PEFileListLock *pLock, PEAssembly *pPEAssembly, Assembly *pAssembly); + FileLoadLock(PEFileListLock* pLock, PEAssembly* pPEAssembly); static void HolderLeave(FileLoadLock *pThis); @@ -1098,7 +1102,7 @@ class AppDomain final Assembly *LoadAssembly(FileLoadLock *pLock, FileLoadLevel targetLevel); - void TryIncrementalLoad(Assembly *pFile, FileLoadLevel workLevel, FileLoadLockHolder &lockHolder); + void TryIncrementalLoad(FileLoadLevel workLevel, FileLoadLockHolder& lockHolder); #ifndef DACCESS_COMPILE // needs AssemblySpec public: diff --git a/src/runtime/src/coreclr/vm/assemblyspec.hpp b/src/runtime/src/coreclr/vm/assemblyspec.hpp index ea94c25dbbf..49446800fe7 100644 --- a/src/runtime/src/coreclr/vm/assemblyspec.hpp +++ b/src/runtime/src/coreclr/vm/assemblyspec.hpp @@ -27,7 +27,8 @@ enum FileLoadLevel // Note that semantics here are description is the LAST step done, not what is // currently being done. - FILE_LOAD_CREATE, + FILE_LOAD_CREATE, // List entry + FileLoadLock created, no Assembly/DomainAssembly yet + FILE_LOAD_ALLOCATE, // DomainAssembly & Assembly object allocated and associated with the lock FILE_LOAD_BEGIN, FILE_LOAD_BEFORE_TYPE_LOAD, FILE_LOAD_EAGER_FIXUPS, diff --git a/src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj b/src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj index 64282f8de95..f834af089e1 100644 --- a/src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj +++ b/src/runtime/src/installer/pkg/sfx/installers/dotnet-host.proj @@ -13,10 +13,7 @@ Dotnet_CLI_SharedHost HostSrc - - afterInstallInitialize + afterInstallExecute false true sharedhost @@ -30,7 +27,7 @@ - + diff --git a/src/runtime/src/installer/pkg/sfx/installers/host.wxs b/src/runtime/src/installer/pkg/sfx/installers/host.wxs index fe074135150..2f53e3ba54c 100644 --- a/src/runtime/src/installer/pkg/sfx/installers/host.wxs +++ b/src/runtime/src/installer/pkg/sfx/installers/host.wxs @@ -39,20 +39,22 @@ - + + - + + @@ -60,6 +62,7 @@ + @@ -72,7 +75,6 @@ - @@ -86,43 +88,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/runtime/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/runtime/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs index 80b648dfd95..879b41e23c3 100644 --- a/src/runtime/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs +++ b/src/runtime/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs @@ -612,7 +612,17 @@ private void GeneratePropMetadataInitFunc(SourceWriter writer, string propInitMe PropertyGenerationSpec property = properties[i]; string propertyName = property.NameSpecifiedInSourceCode; string declaringTypeFQN = property.DeclaringType.FullyQualifiedName; - string propertyTypeFQN = property.PropertyType.FullyQualifiedName; + + // If the property is ignored and its type is not used anywhere else in the type graph, + // emit a JsonPropertyInfo of type 'object' to avoid unnecessarily referencing the type. + // STJ requires that all ignored properties be included so that it can perform + // necessary run-time validations using configuration not known at compile time + // such as the property naming policy and case sensitivity. + bool isIgnoredPropertyOfUnusedType = + property.DefaultIgnoreCondition is JsonIgnoreCondition.Always && + !_typeIndex.ContainsKey(property.PropertyType); + + string propertyTypeFQN = isIgnoredPropertyOfUnusedType ? "object" : property.PropertyType.FullyQualifiedName; string getterValue = property switch { @@ -653,9 +663,12 @@ private void GeneratePropMetadataInitFunc(SourceWriter writer, string propInitMe : $"({JsonConverterTypeRef}<{propertyTypeFQN}>){ExpandConverterMethodName}(typeof({propertyTypeFQN}), new {converterFQN}(), {OptionsLocalVariableName})"; } - string attributeProviderFactoryExpr = property.IsProperty - ? $"typeof({property.DeclaringType.FullyQualifiedName}).GetProperty({FormatStringLiteral(property.MemberName)}, {InstanceMemberBindingFlagsVariableName}, null, typeof({property.PropertyType.FullyQualifiedName}), {EmptyTypeArray}, null)" - : $"typeof({property.DeclaringType.FullyQualifiedName}).GetField({FormatStringLiteral(property.MemberName)}, {InstanceMemberBindingFlagsVariableName})"; + string attributeProviderFactoryExpr = property switch + { + _ when isIgnoredPropertyOfUnusedType => "null", + { IsProperty: true } => $"typeof({property.DeclaringType.FullyQualifiedName}).GetProperty({FormatStringLiteral(property.MemberName)}, {InstanceMemberBindingFlagsVariableName}, null, typeof({propertyTypeFQN}), {EmptyTypeArray}, null)", + _ => $"typeof({property.DeclaringType.FullyQualifiedName}).GetField({FormatStringLiteral(property.MemberName)}, {InstanceMemberBindingFlagsVariableName})", + }; writer.WriteLine($$""" var {{InfoVarName}}{{i}} = new {{JsonPropertyInfoValuesTypeRef}}<{{propertyTypeFQN}}> diff --git a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index c058cf81a3d..e30fafc95d2 100644 --- a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -1122,6 +1122,7 @@ public void NumberHandlingHonoredOnPoco() [InlineData(MemberTypes.Field, nameof(PocoWithMixedVisibilityMembers.FieldWithCustomName), "customField")] [InlineData(MemberTypes.Property, nameof(PocoWithMixedVisibilityMembers.BaseProperty))] [InlineData(MemberTypes.Property, nameof(PocoWithMixedVisibilityMembers.ShadowProperty))] + [InlineData(MemberTypes.Property, nameof(PocoWithMixedVisibilityMembers.ExperimentalProperty))] public void JsonPropertyInfo_PopulatesAttributeProvider(MemberTypes memberType, string propertyName, string? jsonPropertyName = null) { if (DefaultContext.JsonSourceGenerationMode is JsonSourceGenerationMode.Serialization) @@ -1134,7 +1135,17 @@ public void JsonPropertyInfo_PopulatesAttributeProvider(MemberTypes memberType, JsonPropertyInfo prop = typeInfo.Properties.FirstOrDefault(prop => prop.Name == name); Assert.NotNull(prop); - MemberInfo memberInfo = Assert.IsAssignableFrom(prop.AttributeProvider); + MemberInfo memberInfo; + if (prop.AttributeProvider is null) + { + Assert.Equal(typeof(object), prop.PropertyType); + memberInfo = typeof(PocoWithMixedVisibilityMembers).GetMember(propertyName, memberType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Single(); + Type actualPropertyType = memberInfo is PropertyInfo pInfo ? pInfo.PropertyType : ((FieldInfo)memberInfo).FieldType; + Assert.False(typeInfo.Options.TryGetTypeInfo(actualPropertyType, out _)); + return; + } + + memberInfo = Assert.IsAssignableFrom(prop.AttributeProvider); string? actualJsonPropertyName = memberInfo.GetCustomAttribute()?.Name; Assert.True(memberInfo.DeclaringType.IsAssignableFrom(typeInfo.Type)); diff --git a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs index b6f2be92175..5f542807d1f 100644 --- a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs +++ b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization; using Microsoft.Extensions.Primitives; @@ -332,8 +333,18 @@ public class PocoWithMixedVisibilityMembers : PocoWithMixedVisibilityMembersBase public string FieldWithCustomName; public new int ShadowProperty { get; set; } + +#pragma warning disable EXP0001 + [JsonIgnore] + public ExperimentalClass ExperimentalProperty { get; set; } +#pragma warning restore EXP0001 } +#if NET + [Experimental("EXP0001")] +#endif + public class ExperimentalClass; + public sealed class ClassWithConflictingIgnoredProperties { [JsonIgnore] diff --git a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs index 37f6c74e172..9db17588627 100644 --- a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs +++ b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/CompilationHelper.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Xunit; +using Xunit.Abstractions; namespace System.Text.Json.SourceGeneration.UnitTests { @@ -133,7 +134,10 @@ public static CSharpGeneratorDriver CreateJsonSourceGeneratorDriver(Compilation #endif } - public static JsonSourceGeneratorResult RunJsonSourceGenerator(Compilation compilation, bool disableDiagnosticValidation = false) + public static JsonSourceGeneratorResult RunJsonSourceGenerator( + Compilation compilation, + bool disableDiagnosticValidation = false, + ITestOutputHelper? logger = null) { var generatedSpecs = ImmutableArray.Empty; var generator = new JsonSourceGenerator @@ -144,6 +148,19 @@ public static JsonSourceGeneratorResult RunJsonSourceGenerator(Compilation compi CSharpGeneratorDriver driver = CreateJsonSourceGeneratorDriver(compilation, generator); driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outCompilation, out ImmutableArray diagnostics); + if (logger is not null) + { + foreach (Diagnostic diagnostic in outCompilation.GetDiagnostics().Concat(diagnostics)) + { + logger.WriteLine(diagnostic.ToString()); + } + + foreach (var tree in outCompilation.SyntaxTrees) + { + LogGeneratedCode(tree, logger); + } + } + if (!disableDiagnosticValidation) { outCompilation.GetDiagnostics().AssertMaxSeverity(DiagnosticSeverity.Info); @@ -831,6 +848,59 @@ internal static void AssertMaxSeverity(this IEnumerable diagnostics, { Assert.DoesNotContain(diagnostics, diagnostic => diagnostic.Severity > maxSeverity); } + + private static void LogGeneratedCode(SyntaxTree tree, ITestOutputHelper logger) + { + logger.WriteLine(FileSeparator); + logger.WriteLine($"{tree.FilePath} content:"); + logger.WriteLine(FileSeparator); + using NumberedSourceFileWriter lineWriter = new(logger); + tree.GetRoot().WriteTo(lineWriter); + lineWriter.WriteLine(string.Empty); + } + + private static readonly string FileSeparator = new string('=', 140); + + private sealed class NumberedSourceFileWriter : TextWriter + { + private readonly ITestOutputHelper _logger; + private readonly StringBuilder _lineBuilder = new StringBuilder(); + private int _lineNumber; + + internal NumberedSourceFileWriter(ITestOutputHelper logger) + { + _logger = logger; + } + + public override Encoding Encoding => Encoding.Unicode; + + public override void WriteLine(string? value) + { + _logger.WriteLine($"{++_lineNumber,6}: {_lineBuilder}{value}"); + _lineBuilder.Clear(); + } + + public override void Write(string? value) + { + if (value is null) + { + return; + } + + if (value.EndsWith("\r\n", StringComparison.Ordinal)) + { + WriteLine(value.Substring(0, value.Length - 2)); + } + else if (value.EndsWith("\n", StringComparison.Ordinal)) + { + WriteLine(value.Substring(0, value.Length - 1)); + } + else + { + _lineBuilder.Append(value); + } + } + } } public record struct DiagnosticData( diff --git a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs index 185881ccbcd..9faf0472700 100644 --- a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs +++ b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Xunit; +using Xunit.Abstractions; namespace System.Text.Json.SourceGeneration.UnitTests { @@ -12,7 +13,7 @@ namespace System.Text.Json.SourceGeneration.UnitTests [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/71962", ~RuntimeConfiguration.Release)] [SkipOnMono("https://github.com/dotnet/runtime/issues/92467")] [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotX86Process))] // https://github.com/dotnet/runtime/issues/71962 - public class GeneratorTests + public class GeneratorTests(ITestOutputHelper logger) { [Fact] public void TypeDiscoveryPrimitivePOCO() @@ -54,7 +55,7 @@ public void UsePrivates() """; Compilation compilation = CompilationHelper.CreateCompilation(source); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(5, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::HelloWorld.MyType"); @@ -109,7 +110,7 @@ public void UsePrivates() Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(6, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::HelloWorld.MyType"); @@ -169,7 +170,7 @@ public void UsePrivates() Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(6, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::HelloWorld.MyType"); @@ -271,7 +272,7 @@ public class WeatherForecastWithPOCOs MetadataReference[] additionalReferences = { MetadataReference.CreateFromImage(referencedImage) }; Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] @@ -312,7 +313,7 @@ public void UsePrivates() Compilation compilation = CompilationHelper.CreateCompilation(source); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(5, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::MyType"); @@ -362,7 +363,7 @@ public record AppRecord(int Id) Compilation compilation = CompilationHelper.CreateCompilation(source); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(3, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::HelloWorld.AppRecord"); @@ -396,7 +397,7 @@ internal partial class JsonContext : JsonSerializerContext MetadataReference[] additionalReferences = { MetadataReference.CreateFromImage(referencedImage) }; Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(3, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::ReferencedAssembly.LibRecord"); @@ -435,7 +436,7 @@ internal record AppRecord : LibRecord Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(3, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::HelloWorld.AppRecord"); @@ -486,7 +487,7 @@ public class MyType MetadataReference[] additionalReferences = { MetadataReference.CreateFromImage(referencedImage) }; Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); // Should find the generated type. Assert.Equal(2, result.AllGeneratedTypes.Count()); @@ -495,7 +496,7 @@ public class MyType } [Fact] - public static void NoWarningsDueToObsoleteMembers() + public void NoWarningsDueToObsoleteMembers() { string source = """ using System; @@ -518,11 +519,11 @@ public class ClassWithObsolete """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] - public static void NoErrorsWhenUsingReservedCSharpKeywords() + public void NoErrorsWhenUsingReservedCSharpKeywords() { string source = """ using System.Text.Json.Serialization; @@ -541,7 +542,7 @@ public class ClassWithPropertiesAndFieldsThatAreReservedKeywords """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] @@ -564,7 +565,7 @@ internal partial class JsonContext : JsonSerializerContext Compilation compilation = CompilationHelper.CreateCompilation(source); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); // Should find the generated type. Assert.Equal(3, result.AllGeneratedTypes.Count()); @@ -574,7 +575,7 @@ internal partial class JsonContext : JsonSerializerContext } [Fact] - public static void NoErrorsWhenUsingTypesWithMultipleEqualsOperators() + public void NoErrorsWhenUsingTypesWithMultipleEqualsOperators() { // Regression test for https://github.com/dotnet/runtime/issues/103515 string source = """ @@ -604,11 +605,11 @@ internal partial class JsonSourceGenerationContext : JsonSerializerContext """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] - public static void NoErrorsWhenUsingIgnoredReservedCSharpKeywords() + public void NoErrorsWhenUsingIgnoredReservedCSharpKeywords() { string source = """ using System.Text.Json.Serialization; @@ -627,7 +628,7 @@ public class ClassWithPropertyNameThatIsAReservedKeyword """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] @@ -678,7 +679,7 @@ public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSeri Compilation compilation = CompilationHelper.CreateCompilation(source, additionalReferences); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(3, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::Test.Sample"); @@ -724,7 +725,7 @@ public class NestedGenericClass Compilation compilation = CompilationHelper.CreateCompilation(source); - JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation); + JsonSourceGeneratorResult result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); Assert.Equal(5, result.AllGeneratedTypes.Count()); result.AssertContainsType("global::System.Collections.Generic.Dictionary"); @@ -771,7 +772,7 @@ public class MyClass """; Compilation compilation = CompilationHelper.CreateCompilation(source, parseOptions: CompilationHelper.CreateParseOptions(languageVersion)); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] @@ -802,7 +803,7 @@ internal partial class JsonContext : JsonSerializerContext """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } #if ROSLYN4_4_OR_GREATER && NET @@ -828,7 +829,7 @@ public partial class MyContext : JsonSerializerContext """; Compilation compilation = CompilationHelper.CreateCompilation(source, parseOptions: CompilationHelper.CreateParseOptions(LanguageVersion.CSharp11)); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } #endif @@ -856,7 +857,7 @@ internal partial class ModelContext : JsonSerializerContext """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } [Fact] @@ -881,7 +882,88 @@ public partial class MyContext : JsonSerializerContext """; Compilation compilation = CompilationHelper.CreateCompilation(source); - CompilationHelper.RunJsonSourceGenerator(compilation); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); } + +#if ROSLYN4_4_OR_GREATER && NET + [Fact] + public void PropertyWithExperimentalType_JsonIgnore_CompilesSuccessfully() + { + string source = """ + using System.Diagnostics.CodeAnalysis; + using System.Text.Json.Serialization; + + [Experimental("EXP001")] + public class ExperimentalType + { + public int Value { get; set; } + } + + public class MyPoco + { + [JsonIgnore] + #pragma warning disable EXP001 + public ExperimentalType ExpType { get; set; } + #pragma warning restore EXP001 + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); + } + + [Fact] + public void PocoWithExperimentalProperty_JsonIgnore_CompilesSuccessfully() + { + string source = """ + using System.Diagnostics.CodeAnalysis; + using System.Text.Json.Serialization; + + public class MyPoco + { + [Experimental("EXP001"), JsonIgnore] + public int ExperimentalProperty { get; set; } + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger); + } + + [Fact] + public void PocoWithExperimentalProperty_NoJsonIgnore_EmitsDiagnostic() + { + string source = """ + using System.Diagnostics.CodeAnalysis; + using System.Text.Json.Serialization; + + public class MyPoco + { + [Experimental("EXP001")] + public int ExperimentalProperty { get; set; } + } + + [JsonSerializable(typeof(MyPoco))] + public partial class MyContext : JsonSerializerContext + { + } + """; + + Compilation compilation = CompilationHelper.CreateCompilation(source); + var result = CompilationHelper.RunJsonSourceGenerator(compilation, logger: logger, disableDiagnosticValidation: true); + + Assert.NotEmpty(result.NewCompilation.GetDiagnostics().Where(d => d.Id == "EXP001")); + } +#endif } } diff --git a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Unit.Tests.csproj b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Unit.Tests.csproj index 7886da32200..b453e5051e1 100644 --- a/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Unit.Tests.csproj +++ b/src/runtime/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Unit.Tests.csproj @@ -1,6 +1,6 @@ - $(MicrosoftCodeAnalysisVersion_4_4) + $(MicrosoftCodeAnalysisVersion_4_8) $(DefineConstants);ROSLYN4_0_OR_GREATER;ROSLYN4_4_OR_GREATER true diff --git a/src/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs b/src/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs index c2332f2985d..98db78edc5f 100644 --- a/src/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs +++ b/src/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ComputeWasmPublishAssets.cs @@ -420,6 +420,16 @@ private void ProcessSymbolAssets( } } + private string GetItemSpecWithoutFingerprint(ITaskItem asset) + => FingerprintAssets ? asset.GetMetadata("OriginalItemSpec") : asset.ItemSpec; + + private static string GetNonFingerprintedAssetItemSpec(ITaskItem asset) + { + var fileName = Path.GetFileName(asset.GetMetadata("OriginalItemSpec")); + var assetToUpdateItemSpec = Path.Combine(Path.GetDirectoryName(asset.ItemSpec), fileName); + return assetToUpdateItemSpec; + } + private void ComputeUpdatedAssemblies( IDictionary<(string, string assemblyName), ITaskItem> satelliteAssemblies, List filesToRemove, @@ -440,14 +450,15 @@ private void ComputeUpdatedAssemblies( foreach (var kvp in assemblyAssets) { var asset = kvp.Value; - var fileName = Path.GetFileName(asset.ItemSpec); + var fileName = Path.GetFileName(GetItemSpecWithoutFingerprint(asset)); + var assetToUpdateItemSpec = FingerprintAssets ? GetNonFingerprintedAssetItemSpec(asset) : asset.ItemSpec; if (IsWebCilEnabled) fileName = Path.ChangeExtension(fileName, ".dll"); if (resolvedAssembliesToPublish.TryGetValue(fileName, out var existing)) { // We found the assembly, so it'll have to be updated. - assetsToUpdate.Add(asset.ItemSpec, asset); + assetsToUpdate.Add(assetToUpdateItemSpec, asset); filesToRemove.Add(existing); if (!string.Equals(asset.ItemSpec, existing.GetMetadata("FullPath"), StringComparison.Ordinal)) { @@ -465,11 +476,12 @@ private void ComputeUpdatedAssemblies( { var satelliteAssembly = kvp.Value; var relatedAsset = satelliteAssembly.GetMetadata("RelatedAsset"); + if (assetsToUpdate.ContainsKey(relatedAsset)) { assetsToUpdate.Add(satelliteAssembly.ItemSpec, satelliteAssembly); var culture = satelliteAssembly.GetMetadata("AssetTraitValue"); - var fileName = Path.GetFileName(satelliteAssembly.ItemSpec); + var fileName = Path.GetFileName(GetItemSpecWithoutFingerprint(satelliteAssembly)); if (IsWebCilEnabled) fileName = Path.ChangeExtension(fileName, ".dll"); @@ -517,7 +529,8 @@ private void ComputeUpdatedAssemblies( ApplyPublishProperties(newAsemblyAsset); newAssets.Add(newAsemblyAsset); - updatedAssetsMap.Add(asset.ItemSpec, newAsemblyAsset); + var assetToUpdateItemSpec = FingerprintAssets ? GetNonFingerprintedAssetItemSpec(asset) : asset.ItemSpec; + updatedAssetsMap.Add(assetToUpdateItemSpec, newAsemblyAsset); break; default: // Satellite assembliess and compressed assets diff --git a/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.cs b/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.cs new file mode 100644 index 00000000000..f590d9fee53 --- /dev/null +++ b/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Numerics; +using System.Runtime.Intrinsics; +using System.Runtime.CompilerServices; +using Xunit; + +public class Runtime_120414 +{ + [MethodImpl(MethodImplOptions.NoInlining)] + private static Vector128 DuplicateFromVec2(Vector2 s) => + Vector128.Create(Unsafe.As(ref s)).AsSingle(); + + [Fact] + public static void TestEntryPoint() + { + Vector2 testVec = new Vector2(1.0f, 0.5f); + Vector128 result = DuplicateFromVec2(testVec); + Assert.Equal(1.0f, result[0]); + Assert.Equal(0.5f, result[1]); + Assert.Equal(1.0f, result[2]); + Assert.Equal(0.5f, result[3]); + } +} diff --git a/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.csproj b/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.csproj new file mode 100644 index 00000000000..501217e4d86 --- /dev/null +++ b/src/runtime/src/tests/JIT/Regression/JitBlue/Runtime_120414/Runtime_120414.csproj @@ -0,0 +1,9 @@ + + + None + True + + + + + diff --git a/src/runtime/src/tests/profiler/unittest/moduleload.cs b/src/runtime/src/tests/profiler/unittest/moduleload.cs index 17e1312d8b0..4539597aea5 100644 --- a/src/runtime/src/tests/profiler/unittest/moduleload.cs +++ b/src/runtime/src/tests/profiler/unittest/moduleload.cs @@ -2,12 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Loader; namespace Profiler.Tests { @@ -21,13 +23,25 @@ public static int RunTest(string[] args) .DefineDynamicModule("TestModule") .DefineType("TestClass", TypeAttributes.Public) .CreateType(); - + var obj = Activator.CreateInstance(type); if (obj == null) { throw new NullReferenceException(); } + // Trigger module load in multiple threads + int threadCount = 20; + List threads = new(threadCount); + for (int i = 0; i < threadCount; i++) + threads.Add(new Thread(() => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName("unloadlibrary")))); + + foreach (var thread in threads) + thread.Start(); + + foreach (var thread in threads) + thread.Join(); + return 100; } diff --git a/src/runtime/src/tests/profiler/unittest/moduleload.csproj b/src/runtime/src/tests/profiler/unittest/moduleload.csproj index e94b734bb5a..83770db0d9b 100644 --- a/src/runtime/src/tests/profiler/unittest/moduleload.csproj +++ b/src/runtime/src/tests/profiler/unittest/moduleload.csproj @@ -15,6 +15,7 @@ + diff --git a/src/sdk/NuGet.config b/src/sdk/NuGet.config index 09870da2888..4303832450f 100644 --- a/src/sdk/NuGet.config +++ b/src/sdk/NuGet.config @@ -3,6 +3,8 @@ + + diff --git a/src/sdk/eng/Version.Details.props b/src/sdk/eng/Version.Details.props index 9fa33977310..4189c1aaab8 100644 --- a/src/sdk/eng/Version.Details.props +++ b/src/sdk/eng/Version.Details.props @@ -6,142 +6,142 @@ This file should be imported by eng/Versions.props - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-preview.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 18.0.0-preview-25467-107 - 18.0.0-preview-25467-107 - 7.0.0-preview.2.46807 - 10.0.0-beta.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 10.0.0-preview.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 2.0.0-preview.1.25467.107 - 2.2.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 14.0.100-rc2.25467.107 - 10.0.0-rc.2.25467.107 - 5.0.0-2.25467.107 - 5.0.0-2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-preview.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 18.0.0-preview-25509-106 + 18.0.0-preview-25509-106 + 7.0.0-rc.1006 + 10.0.0-beta.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 10.0.0-preview.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 2.0.0-preview.1.25509.106 + 2.2.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 14.0.100-rc2.25509.106 + 10.0.0-rtm.25509.106 + 5.0.0-2.25509.106 + 5.0.0-2.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 10.0.0-preview.7.25377.103 - 10.0.0-preview.25467.107 - 10.0.0-rc.2.25467.107 - 18.0.0-preview-25467-107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.0-beta.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 10.0.100-rc.2.25467.107 - 18.0.0-preview-25467-107 - 18.0.0-preview-25467-107 - 3.2.0-preview.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 7.0.0-preview.2.46807 - 10.0.0-rc.2.25467.107 - 2.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 - 10.0.0-rc.2.25467.107 + 10.0.0-preview.25509.106 + 10.0.0-rtm.25509.106 + 18.0.0-release-25509-106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.0-beta.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 10.0.100-rtm.25509.106 + 18.0.0-release-25509-106 + 18.0.0-release-25509-106 + 3.2.0-preview.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 7.0.0-rc.1006 + 10.0.0-rtm.25509.106 + 2.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 + 10.0.0-rtm.25509.106 2.1.0 - 1.9.0-preview.25475.1 - 3.11.0-preview.25475.1 + 2.1.0-preview.25508.5 + 4.1.0-preview.25508.5 diff --git a/src/sdk/eng/Version.Details.xml b/src/sdk/eng/Version.Details.xml index 6f095d0867f..4510cfb93ad 100644 --- a/src/sdk/eng/Version.Details.xml +++ b/src/sdk/eng/Version.Details.xml @@ -1,62 +1,62 @@ - + - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac @@ -68,166 +68,166 @@ https://github.com/dotnet/dotnet 6a953e76162f3f079405f80e28664fa51b136740 - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/microsoft/testfx - 74721fc4a96eefb3b757e3ffacaae1fdcbe7a0f8 + 9866a77221b818a70721106b9622eac95f81adec - + https://github.com/microsoft/testfx - 74721fc4a96eefb3b757e3ffacaae1fdcbe7a0f8 + 9866a77221b818a70721106b9622eac95f81adec - + https://github.com/dotnet/dotnet - e533cfad385ba4f0ec96e35ad3d485dc13581906 + f448387a0e80f2fdeaec2d2f99ace7284fe37aac diff --git a/src/sdk/eng/Versions.props b/src/sdk/eng/Versions.props index ca336803c73..134ef99b37b 100644 --- a/src/sdk/eng/Versions.props +++ b/src/sdk/eng/Versions.props @@ -38,8 +38,8 @@ 36 20 - 18 - 7 + 19 + 8 <_NET70ILLinkPackVersion>7.0.100-1.23211.1 diff --git a/src/sdk/eng/common/core-templates/job/publish-build-assets.yml b/src/sdk/eng/common/core-templates/job/publish-build-assets.yml index 348cd16376f..37dff559fc1 100644 --- a/src/sdk/eng/common/core-templates/job/publish-build-assets.yml +++ b/src/sdk/eng/common/core-templates/job/publish-build-assets.yml @@ -40,6 +40,8 @@ parameters: repositoryAlias: self + officialBuildId: '' + jobs: - job: Asset_Registry_Publish @@ -62,6 +64,11 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml + - name: OfficialBuildId + ${{ if ne(parameters.officialBuildId, '') }}: + value: ${{ parameters.officialBuildId }} + ${{ else }}: + value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) @@ -124,7 +131,7 @@ jobs: /p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests' /p:IsAssetlessBuild=${{ parameters.isAssetlessBuild }} /p:MaestroApiEndpoint=https://maestro.dot.net - /p:OfficialBuildId=$(Build.BuildNumber) + /p:OfficialBuildId=$(OfficialBuildId) condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} diff --git a/src/sdk/eng/common/core-templates/jobs/jobs.yml b/src/sdk/eng/common/core-templates/jobs/jobs.yml index b637cb6e948..01ada747665 100644 --- a/src/sdk/eng/common/core-templates/jobs/jobs.yml +++ b/src/sdk/eng/common/core-templates/jobs/jobs.yml @@ -44,6 +44,7 @@ parameters: artifacts: {} is1ESPipeline: '' repositoryAlias: self + officialBuildId: '' # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -116,3 +117,4 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} + officialBuildId: ${{ parameters.officialBuildId }} diff --git a/src/sdk/eng/common/post-build/nuget-verification.ps1 b/src/sdk/eng/common/post-build/nuget-verification.ps1 index a365194a938..ac5c69ffcac 100644 --- a/src/sdk/eng/common/post-build/nuget-verification.ps1 +++ b/src/sdk/eng/common/post-build/nuget-verification.ps1 @@ -30,7 +30,7 @@ [CmdletBinding(PositionalBinding = $false)] param( [string]$NuGetExePath, - [string]$PackageSource = "https://api.nuget.org/v3/index.json", + [string]$PackageSource = "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json", [string]$DownloadPath, [Parameter(ValueFromRemainingArguments = $true)] [string[]]$args diff --git a/src/sdk/global.json b/src/sdk/global.json index cf8f1c01329..cf7985786d3 100644 --- a/src/sdk/global.json +++ b/src/sdk/global.json @@ -21,8 +21,8 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25467.107", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25467.107", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25509.106", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25509.106", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0", "Microsoft.WixToolset.Sdk": "5.0.2-dotnet.2737382" diff --git a/src/sdk/src/BuiltInTools/DotNetDeltaApplier/PipeListener.cs b/src/sdk/src/BuiltInTools/DotNetDeltaApplier/PipeListener.cs index d596594e551..6110f0fa631 100644 --- a/src/sdk/src/BuiltInTools/DotNetDeltaApplier/PipeListener.cs +++ b/src/sdk/src/BuiltInTools/DotNetDeltaApplier/PipeListener.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.IO.Pipes; using System.Reflection; using System.Runtime.Loader; @@ -9,6 +10,17 @@ namespace Microsoft.DotNet.HotReload; internal sealed class PipeListener(string pipeName, IHotReloadAgent agent, Action log, int connectionTimeoutMS = 5000) { + /// + /// Messages to the client sent after the initial is sent + /// need to be sent while holding this lock in order to synchronize + /// 1) responses to requests received from the client (e.g. ) or + /// 2) notifications sent to the client that may be triggered at arbitrary times (e.g. ). + /// + private readonly SemaphoreSlim _messageToClientLock = new(initialCount: 1); + + // Not-null once initialized: + private NamedPipeClientStream? _pipeClient; + public Task Listen(CancellationToken cancellationToken) { // Connect to the pipe synchronously. @@ -21,23 +33,23 @@ public Task Listen(CancellationToken cancellationToken) log($"Connecting to hot-reload server via pipe {pipeName}"); - var pipeClient = new NamedPipeClientStream(serverName: ".", pipeName, PipeDirection.InOut, PipeOptions.CurrentUserOnly | PipeOptions.Asynchronous); + _pipeClient = new NamedPipeClientStream(serverName: ".", pipeName, PipeDirection.InOut, PipeOptions.CurrentUserOnly | PipeOptions.Asynchronous); try { - pipeClient.Connect(connectionTimeoutMS); + _pipeClient.Connect(connectionTimeoutMS); log("Connected."); } catch (TimeoutException) { log($"Failed to connect in {connectionTimeoutMS}ms."); - pipeClient.Dispose(); + _pipeClient.Dispose(); return Task.CompletedTask; } try { // block execution of the app until initial updates are applied: - InitializeAsync(pipeClient, cancellationToken).GetAwaiter().GetResult(); + InitializeAsync(cancellationToken).GetAwaiter().GetResult(); } catch (Exception e) { @@ -46,7 +58,7 @@ public Task Listen(CancellationToken cancellationToken) log(e.Message); } - pipeClient.Dispose(); + _pipeClient.Dispose(); agent.Dispose(); return Task.CompletedTask; @@ -56,7 +68,7 @@ public Task Listen(CancellationToken cancellationToken) { try { - await ReceiveAndApplyUpdatesAsync(pipeClient, initialUpdates: false, cancellationToken); + await ReceiveAndApplyUpdatesAsync(initialUpdates: false, cancellationToken); } catch (Exception e) when (e is not OperationCanceledException) { @@ -64,40 +76,44 @@ public Task Listen(CancellationToken cancellationToken) } finally { - pipeClient.Dispose(); + _pipeClient.Dispose(); agent.Dispose(); } }, cancellationToken); } - private async Task InitializeAsync(NamedPipeClientStream pipeClient, CancellationToken cancellationToken) + private async Task InitializeAsync(CancellationToken cancellationToken) { + Debug.Assert(_pipeClient != null); + agent.Reporter.Report("Writing capabilities: " + agent.Capabilities, AgentMessageSeverity.Verbose); var initPayload = new ClientInitializationResponse(agent.Capabilities); - await initPayload.WriteAsync(pipeClient, cancellationToken); + await initPayload.WriteAsync(_pipeClient, cancellationToken); // Apply updates made before this process was launched to avoid executing unupdated versions of the affected modules. // We should only receive ManagedCodeUpdate when when the debugger isn't attached, // otherwise the initialization should send InitialUpdatesCompleted immediately. // The debugger itself applies these updates when launching process with the debugger attached. - await ReceiveAndApplyUpdatesAsync(pipeClient, initialUpdates: true, cancellationToken); + await ReceiveAndApplyUpdatesAsync(initialUpdates: true, cancellationToken); } - private async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipeClient, bool initialUpdates, CancellationToken cancellationToken) + private async Task ReceiveAndApplyUpdatesAsync(bool initialUpdates, CancellationToken cancellationToken) { - while (pipeClient.IsConnected) + Debug.Assert(_pipeClient != null); + + while (_pipeClient.IsConnected) { - var payloadType = (RequestType)await pipeClient.ReadByteAsync(cancellationToken); + var payloadType = (RequestType)await _pipeClient.ReadByteAsync(cancellationToken); switch (payloadType) { case RequestType.ManagedCodeUpdate: - await ReadAndApplyManagedCodeUpdateAsync(pipeClient, cancellationToken); + await ReadAndApplyManagedCodeUpdateAsync(cancellationToken); break; case RequestType.StaticAssetUpdate: - await ReadAndApplyStaticAssetUpdateAsync(pipeClient, cancellationToken); + await ReadAndApplyStaticAssetUpdateAsync(cancellationToken); break; case RequestType.InitialUpdatesCompleted when initialUpdates: @@ -110,11 +126,11 @@ private async Task ReceiveAndApplyUpdatesAsync(NamedPipeClientStream pipeClient, } } - private async ValueTask ReadAndApplyManagedCodeUpdateAsync( - NamedPipeClientStream pipeClient, - CancellationToken cancellationToken) + private async ValueTask ReadAndApplyManagedCodeUpdateAsync(CancellationToken cancellationToken) { - var request = await ManagedCodeUpdateRequest.ReadAsync(pipeClient, cancellationToken); + Debug.Assert(_pipeClient != null); + + var request = await ManagedCodeUpdateRequest.ReadAsync(_pipeClient, cancellationToken); bool success; try @@ -131,15 +147,14 @@ private async ValueTask ReadAndApplyManagedCodeUpdateAsync( var logEntries = agent.Reporter.GetAndClearLogEntries(request.ResponseLoggingLevel); - var response = new UpdateResponse(logEntries, success); - await response.WriteAsync(pipeClient, cancellationToken); + await SendResponseAsync(new UpdateResponse(logEntries, success), cancellationToken); } - private async ValueTask ReadAndApplyStaticAssetUpdateAsync( - NamedPipeClientStream pipeClient, - CancellationToken cancellationToken) + private async ValueTask ReadAndApplyStaticAssetUpdateAsync(CancellationToken cancellationToken) { - var request = await StaticAssetUpdateRequest.ReadAsync(pipeClient, cancellationToken); + Debug.Assert(_pipeClient != null); + + var request = await StaticAssetUpdateRequest.ReadAsync(_pipeClient, cancellationToken); try { @@ -155,8 +170,22 @@ private async ValueTask ReadAndApplyStaticAssetUpdateAsync( // Updating static asset only invokes ContentUpdate metadata update handlers. // Failures of these handlers are reported to the log and ignored. // Therefore, this request always succeeds. - var response = new UpdateResponse(logEntries, success: true); + await SendResponseAsync(new UpdateResponse(logEntries, success: true), cancellationToken); + } - await response.WriteAsync(pipeClient, cancellationToken); + internal async ValueTask SendResponseAsync(T response, CancellationToken cancellationToken) + where T : IResponse + { + Debug.Assert(_pipeClient != null); + try + { + await _messageToClientLock.WaitAsync(cancellationToken); + await _pipeClient.WriteAsync((byte)response.Type, cancellationToken); + await response.WriteAsync(_pipeClient, cancellationToken); + } + finally + { + _messageToClientLock.Release(); + } } } diff --git a/src/sdk/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs b/src/sdk/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs index 28890852482..03c7b04a4fe 100644 --- a/src/sdk/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs +++ b/src/sdk/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs @@ -40,14 +40,49 @@ public static void Initialize() RegisterSignalHandlers(); - var agent = new HotReloadAgent(assemblyResolvingHandler: (_, args) => - { - Log($"Resolving '{args.Name}, Version={args.Version}'"); - var path = Path.Combine(processDir, args.Name + ".dll"); - return File.Exists(path) ? AssemblyLoadContext.Default.LoadFromAssemblyPath(path) : null; - }); + PipeListener? listener = null; + + var agent = new HotReloadAgent( + assemblyResolvingHandler: (_, args) => + { + Log($"Resolving '{args.Name}, Version={args.Version}'"); + var path = Path.Combine(processDir, args.Name + ".dll"); + return File.Exists(path) ? AssemblyLoadContext.Default.LoadFromAssemblyPath(path) : null; + }, + hotReloadExceptionCreateHandler: (code, message) => + { + // Continue executing the code if the debugger is attached. + // It will throw the exception and the debugger will handle it. + if (Debugger.IsAttached) + { + return; + } + + Debug.Assert(listener != null); + Log($"Runtime rude edit detected: '{message}'"); + + SendAndForgetAsync().Wait(); + + // Handle Ctrl+C to terminate gracefully: + Console.CancelKeyPress += (_, _) => Environment.Exit(0); + + // wait for the process to be terminated by the Hot Reload client (other threads might still execute): + Thread.Sleep(Timeout.Infinite); + + async Task SendAndForgetAsync() + { + try + { + await listener.SendResponseAsync(new HotReloadExceptionCreatedNotification(code, message), CancellationToken.None); + } + catch + { + // do not crash the app + } + } + }); - var listener = new PipeListener(s_namedPipeName, agent, Log); + listener = new PipeListener(s_namedPipeName, agent, Log); // fire and forget: _ = listener.Listen(CancellationToken.None); diff --git a/src/sdk/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs b/src/sdk/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs index 66766e1c74e..dfa0158c53c 100644 --- a/src/sdk/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs +++ b/src/sdk/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs @@ -12,23 +12,39 @@ namespace Microsoft.DotNet.HotReload; -internal interface IRequest +internal interface IMessage { - RequestType Type { get; } ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken); } +internal interface IRequest : IMessage +{ + RequestType Type { get; } +} + +internal interface IResponse : IMessage +{ + ResponseType Type { get; } +} + internal interface IUpdateRequest : IRequest { } -internal enum RequestType +internal enum RequestType : byte { ManagedCodeUpdate = 1, StaticAssetUpdate = 2, InitialUpdatesCompleted = 3, } +internal enum ResponseType : byte +{ + InitializationResponse = 1, + UpdateResponse = 2, + HotReloadExceptionNotification = 3, +} + internal readonly struct ManagedCodeUpdateRequest(IReadOnlyList updates, ResponseLoggingLevel responseLoggingLevel) : IUpdateRequest { private const byte Version = 4; @@ -81,8 +97,10 @@ public static async ValueTask ReadAsync(Stream stream, } } -internal readonly struct UpdateResponse(IReadOnlyCollection<(string message, AgentMessageSeverity severity)> log, bool success) +internal readonly struct UpdateResponse(IReadOnlyCollection<(string message, AgentMessageSeverity severity)> log, bool success) : IResponse { + public ResponseType Type => ResponseType.UpdateResponse; + public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken) { await stream.WriteAsync(success, cancellationToken); @@ -116,10 +134,12 @@ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationT } } -internal readonly struct ClientInitializationResponse(string capabilities) +internal readonly struct ClientInitializationResponse(string capabilities) : IResponse { private const byte Version = 0; + public ResponseType Type => ResponseType.InitializationResponse; + public string Capabilities { get; } = capabilities; public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken) @@ -141,6 +161,26 @@ public static async ValueTask ReadAsync(Stream str } } +internal readonly struct HotReloadExceptionCreatedNotification(int code, string message) : IResponse +{ + public ResponseType Type => ResponseType.HotReloadExceptionNotification; + public int Code => code; + public string Message => message; + + public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken) + { + await stream.WriteAsync(code, cancellationToken); + await stream.WriteAsync(message, cancellationToken); + } + + public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken) + { + var code = await stream.ReadInt32Async(cancellationToken); + var message = await stream.ReadStringAsync(cancellationToken); + return new HotReloadExceptionCreatedNotification(code, message); + } +} + internal readonly struct StaticAssetUpdateRequest( RuntimeStaticAssetUpdate update, ResponseLoggingLevel responseLoggingLevel) : IUpdateRequest diff --git a/src/sdk/src/BuiltInTools/HotReloadAgent.WebAssembly.Browser/WebAssemblyHotReload.cs b/src/sdk/src/BuiltInTools/HotReloadAgent.WebAssembly.Browser/WebAssemblyHotReload.cs index 2169ef3dbd9..1aad8e47f07 100644 --- a/src/sdk/src/BuiltInTools/HotReloadAgent.WebAssembly.Browser/WebAssemblyHotReload.cs +++ b/src/sdk/src/BuiltInTools/HotReloadAgent.WebAssembly.Browser/WebAssemblyHotReload.cs @@ -71,7 +71,8 @@ public static async Task InitializeAsync(string baseUri) { s_initialized = true; - var agent = new HotReloadAgent(assemblyResolvingHandler: null); + // TODO: Implement hotReloadExceptionCreateHandler: https://github.com/dotnet/sdk/issues/51056 + var agent = new HotReloadAgent(assemblyResolvingHandler: null, hotReloadExceptionCreateHandler: null); var existingAgent = Interlocked.CompareExchange(ref s_hotReloadAgent, agent, null); if (existingAgent != null) diff --git a/src/sdk/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs b/src/sdk/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs index 4114bed5e6f..f6418014407 100644 --- a/src/sdk/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs +++ b/src/sdk/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs @@ -38,11 +38,16 @@ internal sealed class HotReloadAgent : IDisposable, IHotReloadAgent private Func? _assemblyResolvingHandlerToInstall; private Func? _installedAssemblyResolvingHandler; - public HotReloadAgent(Func? assemblyResolvingHandler) + // handler to install to HotReloadException.Created: + private Action? _hotReloadExceptionCreateHandler; + + public HotReloadAgent( + Func? assemblyResolvingHandler, + Action? hotReloadExceptionCreateHandler) { _metadataUpdateHandlerInvoker = new(Reporter); _assemblyResolvingHandlerToInstall = assemblyResolvingHandler; - + _hotReloadExceptionCreateHandler = hotReloadExceptionCreateHandler; GetUpdaterMethodsAndCapabilities(out _applyUpdate, out _capabilities); AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad; @@ -148,11 +153,69 @@ public void ApplyManagedCodeUpdates(IEnumerable update cachedModuleUpdates.Add(update); } - _metadataUpdateHandlerInvoker.MetadataUpdated(GetMetadataUpdateTypes(updates)); + var updatedTypes = GetMetadataUpdateTypes(updates); + + InstallHotReloadExceptionCreatedHandler(updatedTypes); + + _metadataUpdateHandlerInvoker.MetadataUpdated(updatedTypes); Reporter.Report("Updates applied.", AgentMessageSeverity.Verbose); } + private void InstallHotReloadExceptionCreatedHandler(Type[] types) + { + if (_hotReloadExceptionCreateHandler is null) + { + // already installed or not available + return; + } + + var exceptionType = types.FirstOrDefault(static t => t.FullName == "System.Runtime.CompilerServices.HotReloadException"); + if (exceptionType == null) + { + return; + } + + var handler = Interlocked.Exchange(ref _hotReloadExceptionCreateHandler, null); + if (handler == null) + { + // already installed or not available + return; + } + + // HotReloadException has a private static field Action Created, unless emitted by previous versions of the compiler: + // See https://github.com/dotnet/roslyn/blob/06f2643e1268e4a7fcdf1221c052f9c8cce20b60/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs#L29 + var createdField = exceptionType.GetField("Created", BindingFlags.Static | BindingFlags.NonPublic); + var codeField = exceptionType.GetField("Code", BindingFlags.Public | BindingFlags.Instance); + if (createdField == null || codeField == null) + { + Reporter.Report($"Failed to install HotReloadException handler: not supported by the compiler", AgentMessageSeverity.Verbose); + return; + } + + try + { + createdField.SetValue(null, new Action(e => + { + try + { + handler(codeField.GetValue(e) is int code ? code : 0, e.Message); + } + catch + { + // do not crash the app + } + })); + } + catch (Exception e) + { + Reporter.Report($"Failed to install HotReloadException handler: {e.Message}", AgentMessageSeverity.Verbose); + return; + } + + Reporter.Report($"HotReloadException handler installed.", AgentMessageSeverity.Verbose); + } + private Type[] GetMetadataUpdateTypes(IEnumerable updates) { List? types = null; diff --git a/src/sdk/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs b/src/sdk/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs index 952ba4866de..e841af26513 100644 --- a/src/sdk/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs +++ b/src/sdk/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs @@ -110,7 +110,7 @@ private RegisteredActions GetActions() } /// - /// Invokes all registered mtadata update handlers. + /// Invokes all registered metadata update handlers. /// internal void MetadataUpdated(Type[] updatedTypes) { diff --git a/src/sdk/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs b/src/sdk/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs index 1063ba1635f..c7b325a4c34 100644 --- a/src/sdk/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs +++ b/src/sdk/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs @@ -28,6 +28,9 @@ internal sealed class DefaultHotReloadClient(ILogger logger, ILogger agentLogger private NamedPipeServerStream? _pipe; private bool _managedCodeUpdateFailedOrCancelled; + // The status of the last update response. + private TaskCompletionSource _updateStatusSource = new(); + public override void Dispose() { DisposePipe(); @@ -75,24 +78,65 @@ async Task> ConnectAsync() var capabilities = (await ClientInitializationResponse.ReadAsync(_pipe, cancellationToken)).Capabilities; Logger.Log(LogEvents.Capabilities, capabilities); + + // fire and forget: + _ = ListenForResponsesAsync(cancellationToken); + return [.. capabilities.Split(' ')]; } - catch (EndOfStreamException) + catch (Exception e) when (e is not OperationCanceledException) { - // process terminated before capabilities sent: + ReportPipeReadException(e, "capabilities", cancellationToken); return []; } - catch (Exception e) when (e is not OperationCanceledException) + } + } + + private void ReportPipeReadException(Exception e, string responseType, CancellationToken cancellationToken) + { + // Don't report a warning when cancelled or the pipe has been disposed. The process has terminated or the host is shutting down in that case. + // Best effort: There is an inherent race condition due to time between the process exiting and the cancellation token triggering. + if (e is ObjectDisposedException or EndOfStreamException || cancellationToken.IsCancellationRequested) + { + return; + } + + Logger.LogError("Failed to read {ResponseType} from the pipe: {Message}", responseType, e.Message); + } + + private async Task ListenForResponsesAsync(CancellationToken cancellationToken) + { + Debug.Assert(_pipe != null); + + try + { + while (!cancellationToken.IsCancellationRequested) { - // pipe might throw another exception when forcibly closed on process termination: - if (!cancellationToken.IsCancellationRequested) + var type = (ResponseType)await _pipe.ReadByteAsync(cancellationToken); + + switch (type) { - Logger.LogError("Failed to read capabilities: {Message}", e.Message); + case ResponseType.UpdateResponse: + // update request can't be issued again until the status is read and a new source is created: + _updateStatusSource.SetResult(await ReadUpdateResponseAsync(cancellationToken)); + break; + + case ResponseType.HotReloadExceptionNotification: + var notification = await HotReloadExceptionCreatedNotification.ReadAsync(_pipe, cancellationToken); + RuntimeRudeEditDetected(notification.Code, notification.Message); + break; + + default: + // can't continue, the pipe is in undefined state: + Logger.LogError("Unexpected response received from the agent: {ResponseType}", type); + return; } - - return []; } } + catch (Exception e) + { + ReportPipeReadException(e, "response", cancellationToken); + } } [MemberNotNull(nameof(_capabilitiesTask))] @@ -278,6 +322,13 @@ async ValueTask WriteRequestAsync(CancellationToken cancellationToken) } private async ValueTask ReceiveUpdateResponseAsync(CancellationToken cancellationToken) + { + var result = await _updateStatusSource.Task; + _updateStatusSource = new TaskCompletionSource(); + return result; + } + + private async ValueTask ReadUpdateResponseAsync(CancellationToken cancellationToken) { // Should be initialized: Debug.Assert(_pipe != null); diff --git a/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClient.cs b/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClient.cs index e5efa4db2a7..2a563419e4f 100644 --- a/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClient.cs +++ b/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClient.cs @@ -32,6 +32,11 @@ internal abstract class HotReloadClient(ILogger logger, ILogger agentLogger) : I private readonly object _pendingUpdatesGate = new(); private Task _pendingUpdates = Task.CompletedTask; + /// + /// Invoked when a rude edit is detected at runtime. + /// + public event Action? OnRuntimeRudeEdit; + // for testing internal Task PendingUpdates => _pendingUpdates; @@ -79,6 +84,9 @@ internal Task PendingUpdates /// public abstract void Dispose(); + protected void RuntimeRudeEditDetected(int errorCode, string message) + => OnRuntimeRudeEdit?.Invoke(errorCode, message); + public static void ReportLogEntry(ILogger logger, string message, AgentMessageSeverity severity) { var level = severity switch diff --git a/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClients.cs b/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClients.cs index 58676fdcf5d..fcf541045fc 100644 --- a/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClients.cs +++ b/src/sdk/src/BuiltInTools/HotReloadClient/HotReloadClients.cs @@ -37,6 +37,28 @@ public void Dispose() public AbstractBrowserRefreshServer? BrowserRefreshServer => browserRefreshServer; + /// + /// Invoked when a rude edit is detected at runtime. + /// May be invoked multiple times, by each client. + /// + public event Action OnRuntimeRudeEdit + { + add + { + foreach (var (client, _) in clients) + { + client.OnRuntimeRudeEdit += value; + } + } + remove + { + foreach (var (client, _) in clients) + { + client.OnRuntimeRudeEdit -= value; + } + } + } + /// /// All clients share the same loggers. /// @@ -90,7 +112,8 @@ public async ValueTask> GetUpdateCapabilitiesAsync(Cancel } /// Cancellation token. The cancellation should trigger on process terminatation. - public async ValueTask ApplyManagedCodeUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, CancellationToken cancellationToken) + /// True if the updates are initial updates applied automatically when a process starts. + public async ValueTask ApplyManagedCodeUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, bool isInitial, CancellationToken cancellationToken) { var anyFailure = false; @@ -135,9 +158,13 @@ public async ValueTask ApplyManagedCodeUpdatesAsync(ImmutableArray + { + // fire and forget: + _ = HandleRuntimeRudeEditAsync(runningProject, message, cancellationToken); + }; + // Notifies the agent that it can unblock the execution of the process: await clients.InitialUpdatesAppliedAsync(processCommunicationCancellationToken); @@ -215,6 +221,41 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken) } } + private async Task HandleRuntimeRudeEditAsync(RunningProject runningProject, string rudeEditMessage, CancellationToken cancellationToken) + { + var logger = runningProject.Clients.ClientLogger; + + try + { + // Always auto-restart on runtime rude edits regardless of the settings. + // Since there is no debugger attached the process would crash on an unhandled HotReloadException if + // we let it continue executing. + logger.LogWarning(rudeEditMessage); + logger.Log(MessageDescriptor.RestartingApplication); + + if (!runningProject.InitiateRestart()) + { + // Already in the process of restarting, possibly because of another runtime rude edit. + return; + } + + await runningProject.Clients.ReportCompilationErrorsInApplicationAsync([rudeEditMessage, MessageDescriptor.RestartingApplication.GetMessage()], cancellationToken); + + // Terminate the process. + await runningProject.TerminateAsync(); + + // Creates a new running project and launches it: + await runningProject.RestartOperation(cancellationToken); + } + catch (Exception e) + { + if (e is not OperationCanceledException) + { + logger.LogError("Failed to handle runtime rude edit: {Exception}", e.ToString()); + } + } + } + private ImmutableArray GetAggregateCapabilities() { var capabilities = _runningProjects @@ -329,7 +370,7 @@ await ForEachProjectAsync(projectsToUpdate, async (runningProject, cancellationT try { using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(runningProject.ProcessExitedCancellationToken, cancellationToken); - await runningProject.Clients.ApplyManagedCodeUpdatesAsync(ToManagedCodeUpdates(updates), isProcessSuspended: false, processCommunicationCancellationSource.Token); + await runningProject.Clients.ApplyManagedCodeUpdatesAsync(ToManagedCodeUpdates(updates), isProcessSuspended: false, isInitial: false, processCommunicationCancellationSource.Token); } catch (OperationCanceledException) when (runningProject.ProcessExitedCancellationToken.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { @@ -583,7 +624,7 @@ internal async ValueTask> TerminateNonRootProcess // Do not terminate root process at this time - it would signal the cancellation token we are currently using. // The process will be restarted later on. // Wait for all processes to exit to release their resources, so we can rebuild. - await Task.WhenAll(projectsToRestart.Where(p => !p.Options.IsRootProject).Select(p => p.TerminateAsync(isRestarting: true))).WaitAsync(cancellationToken); + await Task.WhenAll(projectsToRestart.Where(p => !p.Options.IsRootProject).Select(p => p.TerminateForRestartAsync())).WaitAsync(cancellationToken); return projectsToRestart; } diff --git a/src/sdk/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs b/src/sdk/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs index 6f4b8803ed9..38e313f6f74 100644 --- a/src/sdk/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs +++ b/src/sdk/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs @@ -128,7 +128,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken) rootProcessTerminationSource, onOutput: null, onExit: null, - restartOperation: new RestartOperation(_ => throw new InvalidOperationException("Root project shouldn't be restarted")), + restartOperation: new RestartOperation(_ => default), // the process will automatically restart iterationCancellationToken); if (rootRunningProject == null) @@ -531,7 +531,7 @@ async Task> CaptureChangedFilesSnapshot(ImmutableArra if (rootRunningProject != null) { - await rootRunningProject.TerminateAsync(isRestarting: false); + await rootRunningProject.TerminateAsync(); } if (runtimeProcessLauncher != null) @@ -541,7 +541,8 @@ async Task> CaptureChangedFilesSnapshot(ImmutableArra if (waitForFileChangeBeforeRestarting && !shutdownCancellationToken.IsCancellationRequested && - !forceRestartCancellationSource.IsCancellationRequested) + !forceRestartCancellationSource.IsCancellationRequested && + rootRunningProject?.IsRestarting != true) { using var shutdownOrForcedRestartSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellationToken, forceRestartCancellationSource.Token); await WaitForFileChangeBeforeRestarting(fileWatcher, evaluationResult, shutdownOrForcedRestartSource.Token); diff --git a/src/sdk/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs b/src/sdk/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs index c2662ceec1f..8f5bfe94b00 100644 --- a/src/sdk/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs +++ b/src/sdk/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs @@ -41,8 +41,9 @@ internal sealed class RunningProject( /// Set to true when the process termination is being requested so that it can be restarted within /// the Hot Reload session (i.e. without restarting the root project). /// - public bool IsRestarting { get; private set; } + public bool IsRestarting => _isRestarting != 0; + private volatile int _isRestarting; private volatile bool _isDisposed; /// @@ -81,16 +82,34 @@ public async ValueTask WaitForProcessRunningAsync(CancellationToken cancel } } - public async Task TerminateAsync(bool isRestarting) + /// + /// Terminates the process if it hasn't terminated yet. + /// + public Task TerminateAsync() { - IsRestarting = isRestarting; - if (!_isDisposed) { processTerminationSource.Cancel(); } - return await RunningProcess; + return RunningProcess; + } + + /// + /// Marks the as restarting. + /// Subsequent process termination will be treated as a restart. + /// + /// True if the project hasn't been int restarting state prior the call. + public bool InitiateRestart() + => Interlocked.Exchange(ref _isRestarting, 1) == 0; + + /// + /// Terminates the process in preparation for a restart. + /// + public Task TerminateForRestartAsync() + { + InitiateRestart(); + return TerminateAsync(); } } } diff --git a/src/sdk/src/BuiltInTools/dotnet-watch/UI/IReporter.cs b/src/sdk/src/BuiltInTools/dotnet-watch/UI/IReporter.cs index 3d60e288a97..319fcc1740b 100644 --- a/src/sdk/src/BuiltInTools/dotnet-watch/UI/IReporter.cs +++ b/src/sdk/src/BuiltInTools/dotnet-watch/UI/IReporter.cs @@ -215,6 +215,7 @@ public MessageDescriptor WithSeverityWhen(MessageSeverity severity, bool conditi public static readonly MessageDescriptor RefreshServerRunningAt = Create(LogEvents.RefreshServerRunningAt, Emoji.Default); public static readonly MessageDescriptor ConnectedToRefreshServer = Create(LogEvents.ConnectedToRefreshServer, Emoji.Default); public static readonly MessageDescriptor RestartingApplicationToApplyChanges = Create("Restarting application to apply changes ...", Emoji.Default, MessageSeverity.Output); + public static readonly MessageDescriptor RestartingApplication = Create("Restarting application ...", Emoji.Default, MessageSeverity.Output); public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = Create("Ignoring change in hidden directory '{0}': {1} '{2}'", Emoji.Watch, MessageSeverity.Verbose); public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = Create("Ignoring change in output directory: {0} '{1}'", Emoji.Watch, MessageSeverity.Verbose); public static readonly MessageDescriptor IgnoringChangeInExcludedFile = Create("Ignoring change in excluded file '{0}': {1}. Path matches {2} glob '{3}' set in '{4}'.", Emoji.Watch, MessageSeverity.Verbose); diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf index ecb19bae658..c65c0e782a6 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Výsledek analýzy následujícího kódu JSON byl null: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf index 0b82cc14850..077b39b257a 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Das Ergebnis der Analyse des folgenden JSON war „null“: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf index c76d9428883..83e578cc990 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + El resultado del análisis del siguiente JSON fue "null": {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf index 3dd088fe92f..321017f2a61 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Le résultat de l'analyse du JSON suivant était « null » : {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf index 5022ad18a8c..37c751e7c39 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Il risultato dell'analisi del file JSON seguente è 'null': {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf index e27c0eea775..58b3201670e 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + 次の JSON の解析結果は 'null' でした: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf index 28e0b7ef6ea..5367a26984e 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + 다음 JSON을 구문 분석한 결과는 'null'입니다. {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf index edc3a399520..35d4691fba5 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Wynikiem analizy następującego kodu JSON była wartość „null”: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf index 22b848dd30e..accfeb0309e 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + O resultado da análise sintática do JSON a seguir foi 'null': {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf index b41a1a5ac24..c5e680a9798 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Результат разбора следующего JSON оказался равен null: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf index 158582a214b..ba424b44aa5 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + Aşağıdaki JSON'un ayrıştırma sonucu 'null' idi: {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf index 2df9444a65e..e1820411d6c 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + 解析以下 JSON 会得到 "null": {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf index 1c17c1adc32..5030f71d78c 100644 --- a/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/sdk/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf @@ -854,7 +854,7 @@ The header is followed by the list of parameters and their errors (might be seve The result of parsing the following JSON was 'null': {0} - The result of parsing the following JSON was 'null': + 剖析下列 JSON 的結果為 'null': {0} {0} is the JSON that is being parsed. diff --git a/src/sdk/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs b/src/sdk/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs index 00e73d0db6b..265b1eafbb7 100644 --- a/src/sdk/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs +++ b/src/sdk/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs @@ -119,6 +119,8 @@ public void Initialize(IEventSource eventSource) eventSource.BuildFinished += OnBuildFinished; } + + eventSource.BuildFinished += OnBuildFinished; } catch (Exception) { @@ -133,9 +135,10 @@ private void OnBuildFinished(object sender, BuildFinishedEventArgs e) internal void SendAggregatedEventsOnBuildFinished(ITelemetry? telemetry) { + if (telemetry is null) return; if (_aggregatedEvents.TryGetValue(TaskFactoryTelemetryAggregatedEventName, out var taskFactoryData)) { - var taskFactoryProperties = ConvertToStringDictionary(taskFactoryData); + Dictionary taskFactoryProperties = ConvertToStringDictionary(taskFactoryData); TrackEvent(telemetry, $"msbuild/{TaskFactoryTelemetryAggregatedEventName}", taskFactoryProperties, toBeHashed: [], toBeMeasured: []); _aggregatedEvents.Remove(TaskFactoryTelemetryAggregatedEventName); @@ -143,7 +146,7 @@ internal void SendAggregatedEventsOnBuildFinished(ITelemetry? telemetry) if (_aggregatedEvents.TryGetValue(TasksTelemetryAggregatedEventName, out var tasksData)) { - var tasksProperties = ConvertToStringDictionary(tasksData); + Dictionary tasksProperties = ConvertToStringDictionary(tasksData); TrackEvent(telemetry, $"msbuild/{TasksTelemetryAggregatedEventName}", tasksProperties, toBeHashed: [], toBeMeasured: []); _aggregatedEvents.Remove(TasksTelemetryAggregatedEventName); @@ -163,14 +166,10 @@ internal void SendAggregatedEventsOnBuildFinished(ITelemetry? telemetry) internal void AggregateEvent(TelemetryEventArgs args) { - if (args.EventName == null || args.Properties == null) - { - return; - } - - if (!_aggregatedEvents.TryGetValue(args.EventName, out var eventData)) + if (args.EventName is null) return; + if (!_aggregatedEvents.TryGetValue(args.EventName, out Dictionary? eventData) || eventData is null) { - eventData = []; + eventData = new Dictionary(); _aggregatedEvents[args.EventName] = eventData; } diff --git a/src/sdk/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/sdk/src/Cli/dotnet/Commands/Run/RunCommand.cs index 635566bf07b..542fc76ffbc 100644 --- a/src/sdk/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/sdk/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -6,6 +6,7 @@ using System.CommandLine; using System.CommandLine.Parsing; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; @@ -499,6 +500,7 @@ static void InvokeRunArgumentsTarget(ProjectInstance project, bool noBuild, Faca } } + [DoesNotReturn] internal static void ThrowUnableToRunError(ProjectInstance project) { string targetFrameworks = project.GetPropertyValue("TargetFrameworks"); diff --git a/src/sdk/src/Cli/dotnet/Commands/Run/RunProperties.cs b/src/sdk/src/Cli/dotnet/Commands/Run/RunProperties.cs index 81e92cb8831..6972d140ac5 100644 --- a/src/sdk/src/Cli/dotnet/Commands/Run/RunProperties.cs +++ b/src/sdk/src/Cli/dotnet/Commands/Run/RunProperties.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Execution; using Microsoft.DotNet.Cli.Utils; @@ -19,9 +20,9 @@ internal RunProperties(string command, string? arguments, string? workingDirecto { } - internal static RunProperties FromProject(ProjectInstance project) + internal static bool TryFromProject(ProjectInstance project, [NotNullWhen(returnValue: true)] out RunProperties? result) { - var result = new RunProperties( + result = new RunProperties( Command: project.GetPropertyValue("RunCommand"), Arguments: project.GetPropertyValue("RunArguments"), WorkingDirectory: project.GetPropertyValue("RunWorkingDirectory"), @@ -30,6 +31,17 @@ internal static RunProperties FromProject(ProjectInstance project) TargetFrameworkVersion: project.GetPropertyValue("TargetFrameworkVersion")); if (string.IsNullOrEmpty(result.Command)) + { + result = null; + return false; + } + + return true; + } + + internal static RunProperties FromProject(ProjectInstance project) + { + if (!TryFromProject(project, out var result)) { RunCommand.ThrowUnableToRunError(project); } diff --git a/src/sdk/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs b/src/sdk/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs index 6b99feedd1a..5119a2b579d 100644 --- a/src/sdk/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs +++ b/src/sdk/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs @@ -354,7 +354,9 @@ public override int Execute() Debug.Assert(buildRequest.ProjectInstance != null); // Cache run info (to avoid re-evaluating the project instance). - cache.CurrentEntry.Run = RunProperties.FromProject(buildRequest.ProjectInstance); + cache.CurrentEntry.Run = RunProperties.TryFromProject(buildRequest.ProjectInstance, out var runProperties) + ? runProperties + : null; if (!MSBuildUtilities.ConvertStringToBool(buildRequest.ProjectInstance.GetPropertyValue(FileBasedProgramCanSkipMSBuild), defaultValue: true)) { @@ -899,7 +901,7 @@ private BuildLevel GetBuildLevel(out CacheInfo cache) { if (!NeedsToBuild(out cache)) { - Reporter.Verbose.WriteLine("No need to build, the output is up to date."); + Reporter.Verbose.WriteLine("No need to build, the output is up to date. Cache: " + ArtifactsPath); return BuildLevel.None; } @@ -1755,8 +1757,8 @@ public readonly struct ParseContext private static (string, string?)? ParseOptionalTwoParts(in ParseContext context, char separator) { - var i = context.DirectiveText.IndexOf(separator, StringComparison.Ordinal); - var firstPart = (i < 0 ? context.DirectiveText : context.DirectiveText.AsSpan(..i)).TrimEnd(); + var separatorIndex = context.DirectiveText.IndexOf(separator, StringComparison.Ordinal); + var firstPart = (separatorIndex < 0 ? context.DirectiveText : context.DirectiveText.AsSpan(..separatorIndex)).TrimEnd(); string directiveKind = context.DirectiveKind; if (firstPart.IsWhiteSpace()) @@ -1770,12 +1772,20 @@ private static (string, string?)? ParseOptionalTwoParts(in ParseContext context, return context.Diagnostics.AddError<(string, string?)?>(context.SourceFile, context.Info.Span, string.Format(CliCommandStrings.InvalidDirectiveName, directiveKind, separator)); } - var secondPart = i < 0 ? [] : context.DirectiveText.AsSpan((i + 1)..).TrimStart(); - if (i < 0 || secondPart.IsWhiteSpace()) + if (separatorIndex < 0) { return (firstPart.ToString(), null); } + var secondPart = context.DirectiveText.AsSpan((separatorIndex + 1)..).TrimStart(); + if (secondPart.IsWhiteSpace()) + { + Debug.Assert(secondPart.Length == 0, + "We have trimmed the second part, so if it's white space, it should be actually empty."); + + return (firstPart.ToString(), string.Empty); + } + return (firstPart.ToString(), secondPart.ToString()); } diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index 10a84c76b1a..aaea01228d5 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -1164,12 +1164,12 @@ Další informace najdete na https://aka.ms/dotnet-test/mtp. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Testovací příkaz .NET pro Microsoft.Testing.Platform (vyjádřen výslovný souhlas prostřednictvím souboru global.json). Podporuje jenom Microsoft.Testing.Platform a nepodporuje VSTest. Další informace najdete na https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Testovací příkaz .NET pro VSTest. Pokud chcete použít Microsoft.Testing.Platform, prostřednictvím souboru global.json vyjádřete výslovný souhlas s příkazem založeným na Microsoft.Testing.Platform. Další informace najdete na https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Nastavte odlišné názvy profilů. duration: - duration: + doba trvání: error: - error: + chyba: @@ -1298,7 +1298,7 @@ Nastavte odlišné názvy profilů. failed: - failed: + neúspěšné: @@ -2574,7 +2574,7 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Direktivy v současné době nemůžou obsahovat dvojité uvozovky ("). @@ -2699,7 +2699,7 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře. retried - retried + zkoušeno opakovaně @@ -2983,7 +2983,7 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch skipped: - skipped: + přeskočeno: @@ -3123,7 +3123,7 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch succeeded: - succeeded: + úspěšné: @@ -3589,7 +3589,7 @@ příkazu „dotnet tool list“. total: - total: + celkem: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 70630b7a74b..6f0c0b4e099 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -1164,12 +1164,12 @@ Weitere Informationen finden Sie unter https://aka.ms/dotnet-test/mtp. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET-Testbefehl für Microsoft.Testing.Platform (über die Datei „global.json“ aktiviert). Dies unterstützt ausschließlich Microsoft.Testing.Platform und nicht VSTest. Weitere Informationen finden Sie unter https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET-Testbefehl für VSTest. Um Microsoft.Testing.Platform zu verwenden, aktivieren Sie den Microsoft.Testing.Platform-basierten Befehl über global.json. Weitere Informationen finden Sie unter https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Erstellen Sie eindeutige Profilnamen. duration: - duration: + Dauer: error: - error: + Fehler: @@ -1298,7 +1298,7 @@ Erstellen Sie eindeutige Profilnamen. failed: - failed: + fehlgeschlagen: @@ -2574,7 +2574,7 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Direktiven dürfen derzeit keine doppelten Anführungszeichen (") enthalten. @@ -2699,7 +2699,7 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht. retried - retried + Wiederholung @@ -2983,7 +2983,7 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches skipped: - skipped: + übersprungen: @@ -3123,7 +3123,7 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches succeeded: - succeeded: + erfolgreich: @@ -3589,7 +3589,7 @@ und die zugehörigen Paket-IDs für installierte Tools über den Befehl total: - total: + gesamt: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index 84d3025e048..8fc261f14d5 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -1164,12 +1164,12 @@ Consulte https://aka.ms/dotnet-test/mtp para obtener más información. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Comando de prueba de .NET para Microsoft.Testing.Platform (activado mediante el archivo “global.json”). Solo es compatible con Microsoft.Testing.Platform y no con VSTest. Consulte https://aka.ms/dotnet-test para obtener más información. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Comando de prueba de .NET para VSTest. Para usar Microsoft.Testing.Platform, actívelo en el comando basado en Microsoft.Testing.Platform mediante global.json. Consulte https://aka.ms/dotnet-test para obtener más información. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Defina nombres de perfiles distintos. duration: - duration: + duración: error: - error: + error: @@ -1298,7 +1298,7 @@ Defina nombres de perfiles distintos. failed: - failed: + error: @@ -2574,7 +2574,7 @@ El valor predeterminado es publicar una aplicación dependiente del marco. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Las directivas no pueden contener comillas dobles ("), por ahora. @@ -2699,7 +2699,7 @@ El valor predeterminado es publicar una aplicación dependiente del marco. retried - retried + volver a intentarlo @@ -2983,7 +2983,7 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa skipped: - skipped: + omitido: @@ -3123,7 +3123,7 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa succeeded: - succeeded: + correcto: @@ -3589,7 +3589,7 @@ y los identificadores de los paquetes correspondientes a las herramientas instal total: - total: + total: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 3b1487a7704..3d8be0107a5 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -1164,12 +1164,12 @@ Pour découvrir plus d’informations, consultez https://aka.ms/dotnet-test/mtp. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Commande de test .NET pour Microsoft.Testing.Platform (activée via le fichier « global.json »). Cela prend uniquement en charge Microsoft.Testing.Platform et ne prend pas en charge VSTest. Pour plus d'informations, consultez https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Commande de test .NET pour VSTest. Pour utiliser Microsoft.Testing.Platform, optez pour la commande basée sur Microsoft.Testing.Platform via global.json. Pour plus d'informations, consultez https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Faites en sorte que les noms de profil soient distincts. duration: - duration: + durée : error: - error: + erreur : @@ -1298,7 +1298,7 @@ Faites en sorte que les noms de profil soient distincts. failed: - failed: + échec : @@ -2574,7 +2574,7 @@ La valeur par défaut est de publier une application dépendante du framework. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Les directives ne peuvent actuellement pas contenir de guillemets doubles ("). @@ -2699,7 +2699,7 @@ La valeur par défaut est de publier une application dépendante du framework. retried - retried + réessayé @@ -2983,7 +2983,7 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à skipped: - skipped: + ignoré : @@ -3123,7 +3123,7 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à succeeded: - succeeded: + réussie : @@ -3589,7 +3589,7 @@ et les ID de package correspondants aux outils installés, utilisez la commande total: - total: + total : diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index fffbb67c75e..ff2e8c98609 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -1164,12 +1164,12 @@ Per altre informazioni, vedere https://aka.ms/dotnet-test/mtp. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Comando di test .NET per Microsoft.Testing.Platform (attivato tramite il file 'global.json'). Supporta solo Microsoft.Testing.Platform e non VSTest. Per altre informazioni, vedere https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Comando di test .NET per VSTest. Per usare Microsoft.Testing.Platform, attivare il comando basato su Microsoft.Testing.Platform tramite global.json. Per altre informazioni, vedere https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Rendi distinti i nomi profilo. duration: - duration: + durata: error: - error: + errore: @@ -1298,7 +1298,7 @@ Rendi distinti i nomi profilo. failed: - failed: + non riuscito: @@ -2574,7 +2574,7 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Le direttive attualmente non possono contenere virgolette doppie ("). @@ -2699,7 +2699,7 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo retried - retried + ripetuto @@ -2983,7 +2983,7 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire skipped: - skipped: + ignorato: @@ -3123,7 +3123,7 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire succeeded: - succeeded: + operazione completata: @@ -3589,7 +3589,7 @@ e gli ID pacchetto corrispondenti per gli strumenti installati usando il comando total: - total: + totale: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 4a9b4564cd8..40678fb35f7 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -1164,12 +1164,12 @@ See https://aka.ms/dotnet-test/mtp for more information. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Microsoft.Testing.Platform 用の .NET テスト コマンド ('global.json' ファイルでオプトイン済み)。これは Microsoft.Testing.Platform のみをサポートしており、VSTest には対応していません。詳細については、https://aka.ms/dotnet-test をご覧ください。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + VSTest 用の .NET テスト コマンド。Microsoft.Testing.Platform を使用するには、global.json ファイルで Microsoft.Testing.Platform ベースのコマンドにオプトインしてください。詳細については、https://aka.ms/dotnet-test をご覧ください。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Make the profile names distinct. duration: - duration: + 期間: error: - error: + エラー: @@ -1298,7 +1298,7 @@ Make the profile names distinct. failed: - failed: + 失敗: @@ -2574,7 +2574,7 @@ The default is to publish a framework-dependent application. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + ディレクティブには二重引用符 (") を含めることはできません。 @@ -2699,7 +2699,7 @@ The default is to publish a framework-dependent application. retried - retried + 再試行済み @@ -2983,7 +2983,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' skipped: - skipped: + スキップ済み: @@ -3123,7 +3123,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' succeeded: - succeeded: + 成功: @@ -3589,7 +3589,7 @@ and the corresponding package Ids for installed tools using the command total: - total: + 合計: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index d7a462877fb..b2f6c81c0d2 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -1164,12 +1164,12 @@ See https://aka.ms/dotnet-test/mtp for more information. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Microsoft.Testing.Platform용 .NET 테스트 명령입니다('global.json' 파일을 통해 옵트인). 이는 Microsoft.Testing.Platform만 지원하며 VSTest를 지원하지 않습니다. 자세한 내용은 https://aka.ms/dotnet-test를 참조하세요. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + VSTest에 대한 .NET 테스트 명령입니다. Microsoft.Testing.Platform을 사용하려면 global.json을 통해 Microsoft.Testing.Platform 기반 명령을 옵트인합니다. 자세한 내용은 https://aka.ms/dotnet-test를 참조하세요. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Make the profile names distinct. duration: - duration: + 기간: error: - error: + 오류: @@ -1298,7 +1298,7 @@ Make the profile names distinct. failed: - failed: + 실패: @@ -2574,7 +2574,7 @@ The default is to publish a framework-dependent application. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + 지시문은 현재 큰따옴표(")를 포함할 수 없습니다. @@ -2699,7 +2699,7 @@ The default is to publish a framework-dependent application. retried - retried + 다시 시도됨 @@ -2983,7 +2983,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' skipped: - skipped: + 건너뜀: @@ -3123,7 +3123,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' succeeded: - succeeded: + 성공: @@ -3589,7 +3589,7 @@ and the corresponding package Ids for installed tools using the command total: - total: + 합계: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 455c06c0f7d..bc9182e525a 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -1164,12 +1164,12 @@ Aby uzyskać więcej informacji, zobacz https://aka.ms/dotnet-test/mtp. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Polecenie testowe platformy .NET dla elementu Microsoft.Testing.Platform (aktywowane przez plik „global.json”). Obsługuje tylko Microsoft.Testing.Platform i nie obsługuje VSTest. Aby uzyskać więcej informacji, zobacz https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Polecenie testowe platformy .NET dla narzędzia VSTest. Aby korzystać z platformy Microsoft.Testing.Platform, wyraź zgodę na użycie polecenia opartego na Microsoft.Testing.Platform za pośrednictwem pliku global.json. Aby uzyskać więcej informacji, zobacz https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Rozróżnij nazwy profilów. duration: - duration: + czas trwania: error: - error: + błąd: @@ -1298,7 +1298,7 @@ Rozróżnij nazwy profilów. failed: - failed: + zakończone niepowodzeniem: @@ -2574,7 +2574,7 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Dyrektywy nie mogą obecnie zawierać podwójnych cudzysłowów ("). @@ -2699,7 +2699,7 @@ Domyślnie publikowana jest aplikacja zależna od struktury. retried - retried + próbowano ponownie @@ -2983,7 +2983,7 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u skipped: - skipped: + pominięto: @@ -3123,7 +3123,7 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u succeeded: - succeeded: + zakończone powodzeniem: @@ -3589,7 +3589,7 @@ i odpowiednie identyfikatory pakietów zainstalowanych narzędzi można znaleź total: - total: + suma: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index f999a934e37..be9c08f1ce8 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -1164,12 +1164,12 @@ Consulte https://aka.ms/dotnet-test/mtp para obter mais informações. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Comando de Teste .NET para Microsoft.Testing.Platform (aceito por meio do arquivo 'global.json'). Isso dá suporte apenas a Microsoft.Testing.Platform e não dá suporte a VSTest. Para obter mais informações, confira https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Comando de Teste .NET para VSTest. Para usar a Microsoft.Testing.Platform, faça a aceitação do comando baseado em Microsoft.Testing.Platform por meio do global.json. Para obter mais informações, confira https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Diferencie os nomes dos perfis. duration: - duration: + duração: error: - error: + erro: @@ -1298,7 +1298,7 @@ Diferencie os nomes dos perfis. failed: - failed: + falhou: @@ -2574,7 +2574,7 @@ O padrão é publicar uma aplicação dependente de framework. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + No momento, as diretivas não podem conter aspas duplas ("). @@ -2699,7 +2699,7 @@ O padrão é publicar uma aplicação dependente de framework. retried - retried + repetido @@ -2983,7 +2983,7 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa skipped: - skipped: + ignorados: @@ -3123,7 +3123,7 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa succeeded: - succeeded: + concluído com êxito: @@ -3589,7 +3589,7 @@ e as Ids de pacote correspondentes para as ferramentas instaladas usando o coman total: - total: + total: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index adae2e4b3b1..694c6428205 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -1164,12 +1164,12 @@ See https://aka.ms/dotnet-test/mtp for more information. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Тестовая команда .NET для Microsoft.Testing.Platform (предоставлено согласие с помощью файла global.json). Поддерживается только Microsoft.Testing.Platform, VSTest не поддерживается. Дополнительные сведения см. на странице https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + Тестовая команда .NET для VSTest. Чтобы использовать Microsoft.Testing.Platform, согласитесь на использование команды на основе Microsoft.Testing.Platform посредством global.json. Дополнительные сведения см. на странице https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Make the profile names distinct. duration: - duration: + длительность: error: - error: + ошибка: @@ -1298,7 +1298,7 @@ Make the profile names distinct. failed: - failed: + сбой: @@ -2574,7 +2574,7 @@ The default is to publish a framework-dependent application. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + В директивах пока нельзя использовать двойные кавычки ("). @@ -2699,7 +2699,7 @@ The default is to publish a framework-dependent application. retried - retried + повторено @@ -2983,7 +2983,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' skipped: - skipped: + пропущено: @@ -3123,7 +3123,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' succeeded: - succeeded: + успешно: @@ -3590,7 +3590,7 @@ and the corresponding package Ids for installed tools using the command total: - total: + итог: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 4e19bf47de4..f5b9e9b3825 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -1164,12 +1164,12 @@ Daha fazla bilgi için https://aka.ms/dotnet-test/mtp adresine bakın. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + Microsoft.Testing.Platform için .NET Test Komutu ('global.json' dosyasıyla seçildi). Bu sadece Microsoft.Testing.Platform'u destekler, VSTest'i desteklemez. Daha fazla bilgi için bkz. https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + VSTest için .NET Test Komutu. Microsoft.Testing.Platform'u kullanmak için global.json üzerinden Microsoft.Testing.Platform tabanlı komutu seçin. Daha fazla bilgi için bkz. https://aka.ms/dotnet-test. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Lütfen profil adlarını değiştirin. duration: - duration: + süre: error: - error: + hata: @@ -1298,7 +1298,7 @@ Lütfen profil adlarını değiştirin. failed: - failed: + başarısız oldu: @@ -2574,7 +2574,7 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + Yönergeler şu anda çift tırnak (") içeremez. @@ -2699,7 +2699,7 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. retried - retried + yeniden denendi @@ -2983,7 +2983,7 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' skipped: - skipped: + atlandı: @@ -3123,7 +3123,7 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' succeeded: - succeeded: + başarılı: @@ -3589,7 +3589,7 @@ karşılık gelen paket kimliklerini bulmak için total: - total: + toplam: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index 85e43f3d508..725ed5df548 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -1164,12 +1164,12 @@ See https://aka.ms/dotnet-test/mtp for more information. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + 适用于 Microsoft.Testing.Platform 的 .NET 测试命令(已通过 "global.json" 文件选择加入)。此命令仅支持 Microsoft.Testing.Platform,不支持 VSTest。有关详细信息,请参阅 https://aka.ms/dotnet-test。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + 适用于 VSTest 的 .NET 测试命令。若要使用 Microsoft.Testing.Platform,请通过 global.json 选择加入基于 Microsoft.Testing.Platform 的命令。有关详细信息,请参阅 https://aka.ms/dotnet-test。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Make the profile names distinct. duration: - duration: + 持续时间: error: - error: + 错误: @@ -1298,7 +1298,7 @@ Make the profile names distinct. failed: - failed: + 失败: @@ -2574,7 +2574,7 @@ The default is to publish a framework-dependent application. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + 指令当前不能包含双引号(")。 @@ -2699,7 +2699,7 @@ The default is to publish a framework-dependent application. retried - retried + 已重试 @@ -2983,7 +2983,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' skipped: - skipped: + 已跳过: @@ -3123,7 +3123,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' succeeded: - succeeded: + 成功: @@ -3589,7 +3589,7 @@ and the corresponding package Ids for installed tools using the command total: - total: + 总计: diff --git a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index 7023d8771c0..db6fc4bd2dd 100644 --- a/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/sdk/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -1164,12 +1164,12 @@ See https://aka.ms/dotnet-test/mtp for more information. .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET 測試命令,適用於 Microsoft.Testing.Platform (透過 'global.json' 檔案選擇加入)。此命令僅支援 Microsoft.Testing.Platform,不支援 VSTest。如需詳細資訊,請參閱 https://aka.ms/dotnet-test。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. - .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + 適用於 VSTest 的 .NET 測試命令。若要使用 Microsoft.Testing.Platform,請透過 global.json 選擇加入以 Microsoft.Testing.Platform 為基礎的命令。如需詳細資訊,請參閱 https://aka.ms/dotnet-test。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} @@ -1238,12 +1238,12 @@ Make the profile names distinct. duration: - duration: + 期間: error: - error: + 錯誤: @@ -1298,7 +1298,7 @@ Make the profile names distinct. failed: - failed: + 失敗: @@ -2574,7 +2574,7 @@ The default is to publish a framework-dependent application. Directives currently cannot contain double quotes ("). - Directives currently cannot contain double quotes ("). + 指令目前不能包含雙引號 (")。 @@ -2699,7 +2699,7 @@ The default is to publish a framework-dependent application. retried - retried + 已重試 @@ -2983,7 +2983,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' skipped: - skipped: + 已略過: @@ -3123,7 +3123,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' succeeded: - succeeded: + 已成功: @@ -3589,7 +3589,7 @@ and the corresponding package Ids for installed tools using the command total: - total: + 總計: diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.cs.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.cs.xlf index 793563fbddd..a55342a500c 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.cs.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.cs.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Odkaz na balíček ve formě identifikátoru balíčku, jako je „{0}“, nebo identifikátor balíčku a verze oddělené znakem @, například „{0}@{1}“. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.de.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.de.xlf index 75a8ea0992b..92adccab483 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.de.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.de.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Paketverweis in Form eines Paketbezeichners wie {0} oder Paketbezeichner und -version getrennt durch „@“ wie „{0}@{1}“. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.es.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.es.xlf index e1598973152..db4dd74c144 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.es.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.es.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Referencia de paquete en forma de identificador de paquete como "{0}" o identificador de paquete y versión separados por "@", como "{0}@{1}". diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.fr.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.fr.xlf index c559d991fdb..f9b5582e430 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.fr.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.fr.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Référence de package sous la forme d’un identificateur de package tel que « {0} » ou d’un identificateur de package et d’une version séparés par « @ », comme « {0}@{1} ». diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.it.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.it.xlf index 632432b2b28..8e5fb8a55e0 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.it.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.it.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Riferimento al pacchetto sotto forma di identificatore di pacchetto, ad esempio '{0}', oppure identificatore e versione di pacchetto separati da '@', ad esempio '{0}@{1}'. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ja.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ja.xlf index 4ec60d0c7ee..2504e04d917 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ja.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ja.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + '{0}' のようなパッケージ ID の形式のパッケージ参照、または '{0}@{1}' のように '@' で区切られたパッケージ ID とバージョンです。 diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ko.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ko.xlf index c65066c2341..af4f6dc21b6 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ko.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ko.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + '{0}'과 같은 패키지 식별자 또는 '{0}@{1}'과 같이 '@'로 구분된 패키지 식별자 및 버전 형식의 패키지 참조입니다. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.pl.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.pl.xlf index 47183ec7a67..b92f633f89d 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.pl.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.pl.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Odwołanie do pakietu w formie identyfikatora pakietu, takiego jak „{0}” lub identyfikatora pakietu i wersji, rozdzielonych znakiem „@”, np. „{0}@{1}”. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf index fccf80013c6..48cd96b70fa 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Referência de pacote na forma de um identificador de pacote como '{0}' ou identificador de pacote e versão separados por '@', como '{0}@{1}'. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ru.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ru.xlf index 3814f20f1ca..774a83ee7bd 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.ru.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.ru.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + Ссылка на пакет в виде идентификатора пакета, например "{0}", или идентификатора пакета и версии, разделенных "@", например "{0}@{1}". diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.tr.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.tr.xlf index e6dc0a5df79..e3a3c95596e 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.tr.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.tr.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + '{0}' gibi bir paket tanımlayıcısı veya '{0}@{1}' gibi '@' ile ayrılmış paket tanımlayıcısı ve sürümü şeklinde paket başvurusu. diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf index 7dc1e93e52e..12a34a5c5fa 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + 包引用的格式为包标识符,如 ‘{0}’,或由 ‘@’ 分隔的包标识符和版本,如 ‘{0}@{1}’。 diff --git a/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf b/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf index bd3384fb806..7666715348c 100644 --- a/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf +++ b/src/sdk/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf @@ -784,7 +784,7 @@ setx PATH "%PATH%;{0}" Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. - Package reference in the form of a package identifier like '{0}' or package identifier and version separated by '@' like '{0}@{1}'. + 套件參考的格式為套件識別碼,例如 '{0}',或是以 '@' 分隔的套件識別碼和版本,例如 '{0}@{1}'。 diff --git a/src/sdk/src/Layout/pkg/windows/bundles/sdk/bundle.wxs b/src/sdk/src/Layout/pkg/windows/bundles/sdk/bundle.wxs index 5836502fcd6..2503b1dac02 100644 --- a/src/sdk/src/Layout/pkg/windows/bundles/sdk/bundle.wxs +++ b/src/sdk/src/Layout/pkg/windows/bundles/sdk/bundle.wxs @@ -27,7 +27,13 @@ - + + + + + + diff --git a/src/sdk/src/Layout/redist/dnx.ps1 b/src/sdk/src/Layout/redist/dnx.ps1 deleted file mode 100644 index ff880f85e2f..00000000000 --- a/src/sdk/src/Layout/redist/dnx.ps1 +++ /dev/null @@ -1,8 +0,0 @@ -# Licensed to the .NET Foundation under one or more agreements. -# The .NET Foundation licenses this file to you under the MIT license. - -# PowerShell script to launch dotnet.exe with 'dnx' and all passed arguments -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition -$dotnet = Join-Path $scriptDir 'dotnet.exe' -& $dotnet dnx @Args -exit $LASTEXITCODE diff --git a/src/sdk/src/Layout/redist/targets/GenerateInstallerLayout.targets b/src/sdk/src/Layout/redist/targets/GenerateInstallerLayout.targets index 4c7aa7749e3..069c6a2fde2 100644 --- a/src/sdk/src/Layout/redist/targets/GenerateInstallerLayout.targets +++ b/src/sdk/src/Layout/redist/targets/GenerateInstallerLayout.targets @@ -67,7 +67,6 @@ - diff --git a/src/sdk/src/Layout/redist/targets/GeneratePackagePruneData.targets b/src/sdk/src/Layout/redist/targets/GeneratePackagePruneData.targets index 157eeabd5cd..f414f6582c7 100644 --- a/src/sdk/src/Layout/redist/targets/GeneratePackagePruneData.targets +++ b/src/sdk/src/Layout/redist/targets/GeneratePackagePruneData.targets @@ -30,13 +30,12 @@ - - + - + DependsOnTargets="GetTargetingPacksForPruneData"> + diff --git a/src/sdk/src/Layout/redist/targets/OverlaySdkOnLKG.targets b/src/sdk/src/Layout/redist/targets/OverlaySdkOnLKG.targets index 19505de69d5..faf995466d4 100644 --- a/src/sdk/src/Layout/redist/targets/OverlaySdkOnLKG.targets +++ b/src/sdk/src/Layout/redist/targets/OverlaySdkOnLKG.targets @@ -34,7 +34,7 @@ + UseHardLinksIfPossible="false" /> - <_NormalizedWindowsSdkSupportedTargetPlatformVersion Include="@(WindowsSdkSupportedTargetPlatformVersion)"> - $([System.Version]::Parse('%(Identity)').Major).$([System.Version]::Parse('%(Identity)').Minor).$([System.Version]::Parse('%(Identity)').Build).0 - - + <_NormalizedWindowsSdkSupportedTargetPlatformVersion Include="@(WindowsSdkSupportedTargetPlatformVersion)"> + $([System.Text.RegularExpressions.Regex]::Replace(%(Identity), '^((\d+\.){3})1$', '${1}0')) + + diff --git a/src/sdk/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets b/src/sdk/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets index 2176f30216f..547f8ccd759 100644 --- a/src/sdk/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets +++ b/src/sdk/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets @@ -13,7 +13,6 @@ - diff --git a/src/sdk/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/EditorConfig/Dotnet/.editorconfig b/src/sdk/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/EditorConfig/Dotnet/.editorconfig index bb88786e7b5..af3d70051e2 100644 --- a/src/sdk/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/EditorConfig/Dotnet/.editorconfig +++ b/src/sdk/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/EditorConfig/Dotnet/.editorconfig @@ -9,7 +9,7 @@ indent_style = space indent_size = 2 # Xml project files -[*.{csproj,fsproj,vbproj,proj}] +[*.{csproj,fsproj,vbproj,proj,slnx}] indent_size = 2 # Xml config files diff --git a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj index b4490a1d30f..415f0f4b220 100644 --- a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj +++ b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj @@ -1,5 +1,5 @@  - + net10.0 @@ -43,7 +43,7 @@ - + diff --git a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj index a1b85e78555..6c64e1cd116 100644 --- a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj +++ b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj @@ -1,5 +1,5 @@ - + net10.0 @@ -48,7 +48,7 @@ - + diff --git a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj index b4490a1d30f..415f0f4b220 100644 --- a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj +++ b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj @@ -1,5 +1,5 @@  - + net10.0 @@ -43,7 +43,7 @@ - + diff --git a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj index 9c6ca5d3e25..2cffa453be0 100644 --- a/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj +++ b/src/sdk/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj @@ -1,5 +1,5 @@  - + net10.0 @@ -44,8 +44,8 @@ - - + + diff --git a/src/sdk/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj b/src/sdk/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj index ff9709d80d6..7bf348d2d58 100644 --- a/src/sdk/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj +++ b/src/sdk/test/TestAssets/TestProjects/TestProjectSolutionWithCodeCoverage/TestProject/TestProject.csproj @@ -1,13 +1,12 @@ - + $(CurrentTargetFramework) False - 17.12.6 - \ No newline at end of file + diff --git a/src/sdk/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#EditorConfig-file#-n#item.verified/EditorConfig-file/.editorconfig b/src/sdk/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#EditorConfig-file#-n#item.verified/EditorConfig-file/.editorconfig index 37f826f6640..fa2cc7622c2 100644 --- a/src/sdk/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#EditorConfig-file#-n#item.verified/EditorConfig-file/.editorconfig +++ b/src/sdk/test/dotnet-new.IntegrationTests/Approvals/AllCommonItemsCreate.-o#EditorConfig-file#-n#item.verified/EditorConfig-file/.editorconfig @@ -9,7 +9,7 @@ indent_style = space indent_size = 2 # Xml project files -[*.{csproj,fsproj,vbproj,proj}] +[*.{csproj,fsproj,vbproj,proj,slnx}] indent_size = 2 # Xml config files diff --git a/src/sdk/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/src/sdk/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs index 104438d0808..501ce980b20 100644 --- a/src/sdk/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs +++ b/src/sdk/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs @@ -440,6 +440,64 @@ public async Task AutoRestartOnRudeEdit(bool nonInteractive) await App.WaitForOutputLineContaining(MessageDescriptor.HotReloadSucceeded); } + [Theory] + [CombinatorialData] + public async Task AutoRestartOnRuntimeRudeEdit(bool nonInteractive) + { + var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp") + .WithSource(); + + var tfm = ToolsetInfo.CurrentTargetFramework; + var programPath = Path.Combine(testAsset.Path, "Program.cs"); + + // Changes the type of lambda without updating top-level code. + // The loop will end up calling the old version of the lambda resulting in runtime rude edit. + + File.WriteAllText(programPath, """ + using System; + using System.Threading; + + var d = C.F(); + + while (true) + { + Thread.Sleep(250); + d(1); + } + + class C + { + public static Action F() + { + return a => + { + Console.WriteLine(a.GetType()); + }; + } + } + """); + + App.Start(testAsset, nonInteractive ? ["--non-interactive"] : []); + + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + await App.WaitUntilOutputContains("System.Int32"); + App.Process.ClearOutput(); + + UpdateSourceFile(programPath, src => src.Replace("Action", "Action")); + + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + await App.WaitUntilOutputContains("System.Byte"); + + App.AssertOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] HotReloadException handler installed."); + App.AssertOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] Runtime rude edit detected:"); + + App.AssertOutputContains($"dotnet watch ⚠ [WatchHotReloadApp ({tfm})] " + + "Attempted to invoke a deleted lambda or local function implementation. " + + "This can happen when lambda or local function is deleted while the application is running."); + + App.AssertOutputContains(MessageDescriptor.RestartingApplication, $"WatchHotReloadApp ({tfm})"); + } + [Fact] public async Task AutoRestartOnRudeEditAfterRestartPrompt() { diff --git a/src/sdk/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs b/src/sdk/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs index 499c04e6883..ff2c8491194 100644 --- a/src/sdk/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs +++ b/src/sdk/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs @@ -526,7 +526,7 @@ public async Task RudeEditInProjectWithoutRunningProcess() // Terminate the process: Log($"Terminating process {runningProject.ProjectNode.GetDisplayName()} ..."); - await runningProject.TerminateAsync(isRestarting: false); + await runningProject.TerminateAsync(); // rude edit in A (changing assembly level attribute): UpdateSourceFile(serviceSourceA2, """ diff --git a/src/sdk/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs b/src/sdk/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs index aeaffc43f28..c80299e56b7 100644 --- a/src/sdk/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs +++ b/src/sdk/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs @@ -249,9 +249,8 @@ private static void AssertSubstringPresence(string expected, IEnumerable var message = new StringBuilder(); - message.AppendLine(expectedPresent - ? "Expected text found in the output:" + ? "Expected text not found in the output:" : "Text not expected to be found in the output:"); message.AppendLine(expected); diff --git a/src/sdk/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs b/src/sdk/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs index eb5678178da..12a75b9493e 100644 --- a/src/sdk/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs +++ b/src/sdk/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs @@ -214,101 +214,5 @@ public void ItIgnoresNonIntegerPropertiesDuringAggregation() fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty"); fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty2"); } - - [Fact] - public void ItAggregatesEvents() - { - var fakeTelemetry = new FakeTelemetry(); - fakeTelemetry.Enabled = true; - var logger = new MSBuildLogger(fakeTelemetry); - - var event1 = new TelemetryEventArgs - { - EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, - Properties = new Dictionary - { - { "AssemblyTaskFactoryTasksExecutedCount", "2" }, - { "RoslynCodeTaskFactoryTasksExecutedCount", "1" } - } - }; - - var event2 = new TelemetryEventArgs - { - EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, - Properties = new Dictionary - { - { "AssemblyTaskFactoryTasksExecutedCount", "3" }, - { "CustomTaskFactoryTasksExecutedCount", "2" } - } - }; - - var event3 = new TelemetryEventArgs - { - EventName = MSBuildLogger.TasksTelemetryAggregatedEventName, - Properties = new Dictionary - { - { "TasksExecutedCount", "3" }, - { "TaskHostTasksExecutedCount", "2" } - } - }; - - var event4 = new TelemetryEventArgs - { - EventName = MSBuildLogger.TasksTelemetryAggregatedEventName, - Properties = new Dictionary - { - { "TasksExecutedCount", "5" } - } - }; - - logger.AggregateEvent(event1); - logger.AggregateEvent(event2); - logger.AggregateEvent(event3); - logger.AggregateEvent(event4); - - logger.SendAggregatedEventsOnBuildFinished(fakeTelemetry); - - fakeTelemetry.LogEntries.Should().HaveCount(2); - - var taskFactoryEntry = fakeTelemetry.LogEntries.FirstOrDefault(e => e.EventName == $"msbuild/{MSBuildLogger.TaskFactoryTelemetryAggregatedEventName}"); - taskFactoryEntry.Should().NotBeNull(); - taskFactoryEntry.Properties["AssemblyTaskFactoryTasksExecutedCount"].Should().Be("5"); // 2 + 3 - taskFactoryEntry.Properties["RoslynCodeTaskFactoryTasksExecutedCount"].Should().Be("1"); // 1 + 0 - taskFactoryEntry.Properties["CustomTaskFactoryTasksExecutedCount"].Should().Be("2"); // 0 + 2 - - var tasksEntry = fakeTelemetry.LogEntries.FirstOrDefault(e => e.EventName == $"msbuild/{MSBuildLogger.TasksTelemetryAggregatedEventName}"); - tasksEntry.Should().NotBeNull(); - tasksEntry.Properties["TasksExecutedCount"].Should().Be("8"); // 3 + 5 - tasksEntry.Properties["TaskHostTasksExecutedCount"].Should().Be("2"); // 2 + 0 - } - - [Fact] - public void ItIgnoresNonIntegerPropertiesDuringAggregation() - { - var fakeTelemetry = new FakeTelemetry(); - fakeTelemetry.Enabled = true; - var logger = new MSBuildLogger(fakeTelemetry); - - var eventArgs = new TelemetryEventArgs - { - EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, - Properties = new Dictionary - { - { "AssemblyTaskFactoryTasksExecutedCount", "3" }, - { "InvalidProperty", "not-a-number" }, - { "InvalidProperty2", "1.234" }, - } - }; - - logger.AggregateEvent(eventArgs); - - logger.SendAggregatedEventsOnBuildFinished(fakeTelemetry); - - fakeTelemetry.LogEntry.Should().NotBeNull(); - fakeTelemetry.LogEntry.EventName.Should().Be($"msbuild/{MSBuildLogger.TaskFactoryTelemetryAggregatedEventName}"); - fakeTelemetry.LogEntry.Properties["AssemblyTaskFactoryTasksExecutedCount"].Should().Be("3"); - fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty"); - fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty2"); - } } } diff --git a/src/sdk/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/src/sdk/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs index 90763462aeb..72a600549d6 100644 --- a/src/sdk/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs +++ b/src/sdk/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs @@ -1154,6 +1154,50 @@ public void Directives_EmptyName( expectedWildcardPattern: RunFileTests.DirectiveError("/app/Program.cs", 1, CliCommandStrings.MissingDirectiveName, directive)); } + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Directives_EmptyValue(string value) + { + VerifyConversion( + inputCSharp: $""" + #:property TargetFramework={value} + #:property Prop1={value} + #:sdk First@{value} + #:sdk Second@{value} + #:package P1@{value} + """, + expectedProject: """ + + + + + + Exe + enable + enable + true + true + + + + + + + + + + + """, + expectedCSharp: ""); + + VerifyConversionThrows( + inputCSharp: $""" + #:project{value} + """, + expectedWildcardPattern: RunFileTests.DirectiveError("/app/Program.cs", 1, CliCommandStrings.MissingDirectiveName, "project")); + } + [Fact] public void Directives_MissingPropertyValue() { diff --git a/src/sdk/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/src/sdk/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 541788369fb..54c5c845493 100644 --- a/src/sdk/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/src/sdk/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -1613,6 +1613,230 @@ public void NoBuild_02() .And.HaveStdOut("Changed"); } + [Fact] + public void Build_Library() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "lib.cs"); + File.WriteAllText(programFile, """ + #:property OutputType=Library + class C; + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "lib.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "lib.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRun, + Path.ChangeExtension(programFile, ".csproj"), + ToolsetInfo.CurrentTargetFrameworkVersion, + "Library")); + } + + [Fact] + public void Build_Library_MultiTarget() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "lib.cs"); + File.WriteAllText(programFile, $""" + #:property OutputType=Library + #:property PublishAot=false + #:property LangVersion=preview + #:property TargetFrameworks=netstandard2.0;{ToolsetInfo.CurrentTargetFramework} + class C; + """); + + // https://github.com/dotnet/sdk/issues/51077: cannot set this via `#:property` directive. + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + + + + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "lib.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "lib.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + + new DotnetCommand(Log, "run", "lib.cs", "--framework", ToolsetInfo.CurrentTargetFramework) + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRun, + Path.ChangeExtension(programFile, ".csproj"), + ToolsetInfo.CurrentTargetFrameworkVersion, + "Library")); + } + + [Fact] + public void Build_Module() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "module.cs"); + File.WriteAllText(programFile, """ + #:property OutputType=Module + #:property ProduceReferenceAssembly=false + class C; + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "module.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "module.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRun, + Path.ChangeExtension(programFile, ".csproj"), + ToolsetInfo.CurrentTargetFrameworkVersion, + "Module")); + } + + [Fact] + public void Build_WinExe() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "winexe.cs"); + File.WriteAllText(programFile, """ + #:property OutputType=WinExe + Console.WriteLine("Hello WinExe"); + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "winexe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "winexe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut("Hello WinExe"); + } + + [Fact] + public void Build_Exe() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "exe.cs"); + File.WriteAllText(programFile, """ + #:property OutputType=Exe + Console.WriteLine("Hello Exe"); + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "exe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "exe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut("Hello Exe"); + } + + [Fact] + public void Build_Exe_MultiTarget() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "exe.cs"); + File.WriteAllText(programFile, $""" + #:property OutputType=Exe + #:property PublishAot=false + #:property LangVersion=preview + #:property TargetFrameworks=netstandard2.0;{ToolsetInfo.CurrentTargetFramework} + Console.WriteLine("Hello Exe"); + """); + + // https://github.com/dotnet/sdk/issues/51077: cannot set this via `#:property` directive. + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + + + + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "exe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "exe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRunSpecifyFramework, "--framework")); + + new DotnetCommand(Log, "run", "exe.cs", "--framework", ToolsetInfo.CurrentTargetFramework) + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut("Hello Exe"); + } + + [Fact] + public void Build_AppContainerExe() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programFile = Path.Join(testInstance.Path, "appcontainerexe.cs"); + File.WriteAllText(programFile, """ + #:property OutputType=AppContainerExe + Console.WriteLine("Hello AppContainerExe"); + """); + + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + new DotnetCommand(Log, "build", "appcontainerexe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run", "appcontainerexe.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + .And.HaveStdErr(string.Format(CliCommandStrings.RunCommandExceptionUnableToRun, + Path.ChangeExtension(programFile, ".csproj"), + ToolsetInfo.CurrentTargetFrameworkVersion, + "AppContainerExe")); + } + [Fact] public void Publish() { diff --git a/src/source-manifest.json b/src/source-manifest.json index a988ed957bc..322086599dc 100644 --- a/src/source-manifest.json +++ b/src/source-manifest.json @@ -1,16 +1,16 @@ { "repositories": [ { - "barId": 286215, + "barId": 286474, "path": "arcade", "remoteUri": "https://github.com/dotnet/arcade", - "commitSha": "7ff6478d902606d65aa33cb8cedc2730e1843fe1" + "commitSha": "ba45f22d375e21c42fdc89695451932731d94552" }, { - "barId": 286219, + "barId": 286532, "path": "aspnetcore", "remoteUri": "https://github.com/dotnet/aspnetcore", - "commitSha": "305280b3a9092b709b4449640e4d8db9a327d46f" + "commitSha": "08964b968ed6d4381b74b05cb01afa8daeae5b6d" }, { "barId": 279211, @@ -31,16 +31,16 @@ "commitSha": "c0c52874069a4dc0fe1e880014e6a5f316e9d3b8" }, { - "barId": 285923, + "barId": 286647, "path": "diagnostics", "remoteUri": "https://github.com/dotnet/diagnostics", - "commitSha": "f40e210b5da69e3c7585917137322cac01f0dffc" + "commitSha": "53c7e6f445c2d1a918707a609b0fd08c3604c659" }, { - "barId": 285648, + "barId": 286575, "path": "efcore", "remoteUri": "https://github.com/dotnet/efcore", - "commitSha": "9f21708d124ca38c7d2c94d5538b597ba20129e1" + "commitSha": "f07ec89c55b88de5b1992cbbed86ae0529f3307f" }, { "barId": 283436, @@ -67,22 +67,22 @@ "commitSha": "5514d935e3e77d90d931758cf9e2589735b905a3" }, { - "barId": 286084, + "barId": 286683, "path": "razor", "remoteUri": "https://github.com/dotnet/razor", - "commitSha": "d52cf7384867f6592a7a45f1696f8fa298441de4" + "commitSha": "4e5ddbfa9a31b06d262234f8e582575d6ceb0739" }, { - "barId": 286121, + "barId": 286456, "path": "roslyn", "remoteUri": "https://github.com/dotnet/roslyn", - "commitSha": "23d275e30097136b12a68d1bab4997148361d116" + "commitSha": "929ae2333a73eca047b9b6e2f01bf7121558308f" }, { - "barId": 286218, + "barId": 286595, "path": "runtime", "remoteUri": "https://github.com/dotnet/runtime", - "commitSha": "d321bc6abef02ed27790e8da3740d253976b9ac6" + "commitSha": "a402595199e366ae6cf697d74b9dd43081fa2fcd" }, { "barId": 277711, @@ -91,10 +91,10 @@ "commitSha": "082359066ee0064039b9b1f1f025bdd0507d06de" }, { - "barId": 286006, + "barId": 286627, "path": "sdk", "remoteUri": "https://github.com/dotnet/sdk", - "commitSha": "cf932ba08b38f8a148536f5d0e9c98d56280cc24" + "commitSha": "09b6a7d0d679cf9480932424ae025e83c49fd9f6" }, { "barId": 282656, @@ -115,10 +115,10 @@ "commitSha": "26c6c313a35f020dc9fa8c7270a012bcd75fc81b" }, { - "barId": 285272, + "barId": 286484, "path": "templating", "remoteUri": "https://github.com/dotnet/templating", - "commitSha": "4c36907d7149f0b3b59c7178acea6330540df851" + "commitSha": "89fd90b5b2016cff0ee924dca2c8bf71b4935a39" }, { "barId": 285355, diff --git a/src/templating/.editorconfig b/src/templating/.editorconfig index bc46cb200f8..178d2855e1c 100644 --- a/src/templating/.editorconfig +++ b/src/templating/.editorconfig @@ -186,10 +186,14 @@ dotnet_code_quality.ca1822.api_surface = private, internal dotnet_code_quality.ca2208.api_surface = public dotnet_diagnostic.CS1591.severity = suggestion dotnet_analyzer_diagnostic.category-Style.severity = warning +#Name can be simplified +dotnet_diagnostic.IDE0002.severity = suggestion #Use explicit type instead of var dotnet_diagnostic.IDE0008.severity = suggestion #Add missing cases to switch statement dotnet_diagnostic.IDE0010.severity = suggestion +#Simplify LINQ expression +dotnet_diagnostic.IDE0120.severity = suggestion #Use expression body for constructors dotnet_diagnostic.IDE0021.severity = suggestion #Use expression body for methods @@ -235,6 +239,9 @@ file_header_template = Licensed to the .NET Foundation under one or more agreeme # Ensure minimum API surface is respected dotnet_diagnostic.BCL0001.severity = warning +# Don't require explicit access modifiers for interface members (conflicts with IDE0040) +dotnet_diagnostic.SA1400.severity = none + # AppContext default value expected to be true dotnet_diagnostic.BCL0010.severity = warning diff --git a/src/templating/eng/Version.Details.props b/src/templating/eng/Version.Details.props index 9c9fabfbbc0..1653acc7903 100644 --- a/src/templating/eng/Version.Details.props +++ b/src/templating/eng/Version.Details.props @@ -6,8 +6,8 @@ This file should be imported by eng/Versions.props - 10.0.0-beta.25479.109 - 2.0.0-rtm.25479.109 + 10.0.0-beta.25509.106 + 2.0.0-rtm.25509.106 9.0.3 9.0.3 diff --git a/src/templating/eng/Version.Details.xml b/src/templating/eng/Version.Details.xml index a28e2082ff6..cdaccc6842f 100644 --- a/src/templating/eng/Version.Details.xml +++ b/src/templating/eng/Version.Details.xml @@ -1,16 +1,16 @@ - + - + https://github.com/dotnet/dotnet - 8aba88f6f12f3ce1dd5740575cff9442f1f9122c + f448387a0e80f2fdeaec2d2f99ace7284fe37aac - + https://github.com/dotnet/dotnet - 8aba88f6f12f3ce1dd5740575cff9442f1f9122c + f448387a0e80f2fdeaec2d2f99ace7284fe37aac diff --git a/src/templating/eng/common/core-templates/job/source-build.yml b/src/templating/eng/common/core-templates/job/source-build.yml index d805d5faeb9..947f0971eb5 100644 --- a/src/templating/eng/common/core-templates/job/source-build.yml +++ b/src/templating/eng/common/core-templates/job/source-build.yml @@ -34,6 +34,9 @@ parameters: # container and pool. platform: {} + # Optional list of directories to ignore for component governance scans. + componentGovernanceIgnoreDirectories: [] + is1ESPipeline: '' # If set to true and running on a non-public project, @@ -94,3 +97,4 @@ jobs: parameters: is1ESPipeline: ${{ parameters.is1ESPipeline }} platform: ${{ parameters.platform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} diff --git a/src/templating/eng/common/core-templates/jobs/source-build.yml b/src/templating/eng/common/core-templates/jobs/source-build.yml index d92860cba20..eb4b923a777 100644 --- a/src/templating/eng/common/core-templates/jobs/source-build.yml +++ b/src/templating/eng/common/core-templates/jobs/source-build.yml @@ -15,6 +15,9 @@ parameters: # one job runs on 'defaultManagedPlatform'. platforms: [] + # Optional list of directories to ignore for component governance scans. + componentGovernanceIgnoreDirectories: [] + is1ESPipeline: '' # If set to true and running on a non-public project, @@ -31,6 +34,7 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} enableInternalSources: ${{ parameters.enableInternalSources }} - ${{ if eq(length(parameters.platforms), 0) }}: @@ -39,4 +43,5 @@ jobs: is1ESPipeline: ${{ parameters.is1ESPipeline }} jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ parameters.defaultManagedPlatform }} + componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }} enableInternalSources: ${{ parameters.enableInternalSources }} diff --git a/src/templating/eng/common/core-templates/steps/source-build.yml b/src/templating/eng/common/core-templates/steps/source-build.yml index acf16ed3496..77321eee11f 100644 --- a/src/templating/eng/common/core-templates/steps/source-build.yml +++ b/src/templating/eng/common/core-templates/steps/source-build.yml @@ -11,6 +11,10 @@ parameters: # for details. The entire object is described in the 'job' template for simplicity, even though # the usage of the properties on this object is split between the 'job' and 'steps' templates. platform: {} + + # Optional list of directories to ignore for component governance scans. + componentGovernanceIgnoreDirectories: [] + is1ESPipeline: false steps: diff --git a/src/templating/global.json b/src/templating/global.json index 2601ff58bf9..7d699a3dfc6 100644 --- a/src/templating/global.json +++ b/src/templating/global.json @@ -13,6 +13,6 @@ "dotnet": "10.0.100-rc.1.25451.107" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25479.109" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25509.106" } } diff --git a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IBindSymbolSource.cs b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IBindSymbolSource.cs index faacc3ef1da..beeefddb72f 100644 --- a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IBindSymbolSource.cs +++ b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IBindSymbolSource.cs @@ -12,17 +12,17 @@ public interface IBindSymbolSource : IPrioritizedComponent /// /// The user friendly name of the component. /// - public string DisplayName { get; } + string DisplayName { get; } /// /// Prefix that is used in binding to reference the component. /// - public string? SourcePrefix { get; } + string? SourcePrefix { get; } /// /// If set to true, the component required exact prefix match to be used. /// - public bool RequiresPrefixMatch { get; } + bool RequiresPrefixMatch { get; } /// /// Gets the value corresponding to . @@ -31,6 +31,6 @@ public interface IBindSymbolSource : IPrioritizedComponent /// the value to retrieve (without prefix). /// cancellation token. /// - public Task GetBoundValueAsync(IEngineEnvironmentSettings settings, string bindName, CancellationToken cancellationToken); + Task GetBoundValueAsync(IEngineEnvironmentSettings settings, string bindName, CancellationToken cancellationToken); } } diff --git a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/ISdkInfoProvider.cs b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/ISdkInfoProvider.cs index 7074e49488b..ebe85225cee 100644 --- a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/ISdkInfoProvider.cs +++ b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/ISdkInfoProvider.cs @@ -13,14 +13,14 @@ public interface ISdkInfoProvider : IIdentifiedComponent /// /// /// SDK version. - public Task GetCurrentVersionAsync(CancellationToken cancellationToken); + Task GetCurrentVersionAsync(CancellationToken cancellationToken); /// /// All installed SDK installations semver version strings. /// /// /// SDK version strings. - public Task> GetInstalledVersionsAsync(CancellationToken cancellationToken); + Task> GetInstalledVersionsAsync(CancellationToken cancellationToken); /// /// Provides localized suggestion on action to be taken so that constraints requiring specified workloads can be met. @@ -31,6 +31,6 @@ public interface ISdkInfoProvider : IIdentifiedComponent /// SDK versions required by a constraint (in an 'OR' relationship). /// SDK versions installed, that can meet the constraint - instructions should be provided to switch to any of those. /// Localized string with remedy suggestion specific to current host. - public string ProvideConstraintRemedySuggestion(IReadOnlyList supportedVersions, IReadOnlyList viableInstalledVersions); + string ProvideConstraintRemedySuggestion(IReadOnlyList supportedVersions, IReadOnlyList viableInstalledVersions); } } diff --git a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IWorkloadsInfoProvider.cs b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IWorkloadsInfoProvider.cs index 8aeb8ae4317..9e512c4e163 100644 --- a/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IWorkloadsInfoProvider.cs +++ b/src/templating/src/Microsoft.TemplateEngine.Abstractions/Components/IWorkloadsInfoProvider.cs @@ -13,7 +13,7 @@ public interface IWorkloadsInfoProvider : IIdentifiedComponent /// /// /// Set of installed workloads. - public Task> GetInstalledWorkloadsAsync(CancellationToken token); + Task> GetInstalledWorkloadsAsync(CancellationToken token); /// /// Provides localized suggestion on action to be taken so that constraints requiring specified workloads can be met. @@ -23,6 +23,6 @@ public interface IWorkloadsInfoProvider : IIdentifiedComponent /// /// Workloads required by a constraint (in an 'OR' relationship). /// Localized string with remedy suggestion specific to current host. - public string ProvideConstraintRemedySuggestion(IReadOnlyList supportedWorkloads); + string ProvideConstraintRemedySuggestion(IReadOnlyList supportedWorkloads); } } diff --git a/src/templating/src/Microsoft.TemplateEngine.Abstractions/IValidationEntry.cs b/src/templating/src/Microsoft.TemplateEngine.Abstractions/IValidationEntry.cs index 30bacbccb2c..f82b0ecf6c9 100644 --- a/src/templating/src/Microsoft.TemplateEngine.Abstractions/IValidationEntry.cs +++ b/src/templating/src/Microsoft.TemplateEngine.Abstractions/IValidationEntry.cs @@ -11,7 +11,7 @@ public interface IValidationEntry /// /// Error severity. /// - public enum SeverityLevel + enum SeverityLevel { None, Info, @@ -42,7 +42,7 @@ public enum SeverityLevel /// /// Details of the error location. /// - public readonly struct ErrorLocation + readonly struct ErrorLocation { /// /// Gets the filename where the error occurred. diff --git a/src/templating/src/Microsoft.TemplateSearch.Common/Abstractions/ITemplatePackageInfo.cs b/src/templating/src/Microsoft.TemplateSearch.Common/Abstractions/ITemplatePackageInfo.cs index 2654dad250a..0b523ec8bce 100644 --- a/src/templating/src/Microsoft.TemplateSearch.Common/Abstractions/ITemplatePackageInfo.cs +++ b/src/templating/src/Microsoft.TemplateSearch.Common/Abstractions/ITemplatePackageInfo.cs @@ -11,23 +11,23 @@ public interface ITemplatePackageInfo /// /// Gets template package name. /// - public string Name { get; } + string Name { get; } /// /// Gets template package version. /// - public string? Version { get; } + string? Version { get; } /// /// Gets total number of downloads for the package. /// Optional, might be 0 in case search provider cannot provide number of downloads. /// - public long TotalDownloads { get; } + long TotalDownloads { get; } /// /// Gets the list of template package owners. /// - public IReadOnlyList Owners { get; } + IReadOnlyList Owners { get; } /// /// Gets the indication if the package is verified. @@ -35,16 +35,16 @@ public interface ITemplatePackageInfo /// /// For NuGet.org 'verified' means that package ID is under reserved namespaces, see . /// - public bool Reserved { get; } + bool Reserved { get; } /// /// Gets the NuGet package description. /// - public string? Description { get; } + string? Description { get; } /// /// Gets the URL to the package icon. /// - public string? IconUrl { get; } + string? IconUrl { get; } } } diff --git a/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs b/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs index 14a0a7bf685..8c3114394e8 100755 --- a/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs +++ b/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs @@ -81,6 +81,10 @@ public partial class LinuxInstallerTests : IDisposable [GeneratedRegex(@"\s*\([^)]*\)", RegexOptions.CultureInvariant)] private static partial Regex RemoveVersionConstraintRegex { get; } + // Remove version numbers from package names: "dotnet-runtime-10.0.0-rc.1.25480.112-x64.rpm" -> "dotnet-runtime-*-x64.rpm" + [GeneratedRegex(@"\d+\.\d+\.\d+(?:-(?:rc|rtm|preview)(?:\.\d+)*)?", RegexOptions.CultureInvariant)] + private static partial Regex RemoveVersionFromPackageNameRegex { get; } + private const string RuntimeDepsRepo = "mcr.microsoft.com/dotnet/runtime-deps"; private const string RuntimeDepsVersion = "10.0-preview"; private const string DotnetRuntimeDepsPrefix = "dotnet-runtime-deps-"; @@ -170,6 +174,18 @@ public async Task DebPackageMetadataTest(string repo, string tag) ValidatePackageMetadata($"{repo}:{tag}", PackageType.Deb); } + [ConditionalFact(typeof(LinuxInstallerTests), nameof(IncludeRpmTests))] + public void ValidateRpmPackageList() + { + ValidatePackageList(PackageType.Rpm); + } + + [ConditionalFact(typeof(LinuxInstallerTests), nameof(IncludeDebTests))] + public void ValidateDebPackageList() + { + ValidatePackageList(PackageType.Deb); + } + private async Task InitializeContextAsync(PackageType packageType, bool initializeSharedContext = true) { string packageArchitecture = @@ -712,4 +728,80 @@ private static List ParseDebControlDependencies(string contents) return results; } + + private void ValidatePackageList(PackageType packageType) + { + string extension = packageType == PackageType.Rpm ? "*.rpm" : "*.deb"; + List expectedPatterns = GetExpectedPackagePatterns(packageType).OrderBy(p => p).ToList(); + + // Find all packages of the specified type and normalize by removing version numbers + List normalizedActual = Directory.GetFiles(Config.AssetsDirectory, extension, SearchOption.AllDirectories) + .Select(path => RemoveVersionFromPackageNameRegex.Replace(Path.GetFileName(path), "*")) + .Distinct() + .OrderBy(name => name) + .ToList(); + + Assert.True( + expectedPatterns.SequenceEqual(normalizedActual), + $"Package list validation failed for {packageType}:\nExpected:\n{string.Join("\n", expectedPatterns)}\nActual:\n{string.Join("\n", normalizedActual)}" + ); + } + + private List GetExpectedPackagePatterns(PackageType packageType) + { + string extension = packageType == PackageType.Rpm ? ".rpm" : ".deb"; + string arch = Config.Architecture == Architecture.X64 ? "x64" : + (packageType == PackageType.Rpm ? "aarch64" : "arm64"); + + var patterns = new List(); + + // Base package prefixes (common to both RPM and DEB) + var basePackages = new List + { + "aspnetcore-runtime", "aspnetcore-targeting-pack", "dotnet-apphost-pack", + "dotnet-host", "dotnet-hostfxr", "dotnet-runtime", "dotnet-sdk", "dotnet-targeting-pack" + }; + + // Add runtime-deps for DEB only (RPM only has distro-specific variants) + if (packageType == PackageType.Deb) + { + basePackages.Add("dotnet-runtime-deps"); + } + + // Standard variants + foreach (string package in basePackages) + { + patterns.Add($"{package}-*-{arch}{extension}"); + } + + // New key variants + foreach (string package in basePackages) + { + patterns.Add($"{package}-*-newkey-{arch}{extension}"); + } + + if (packageType == PackageType.Rpm) + { + // Azure Linux variants + foreach (string package in basePackages) + { + patterns.Add($"{package}-*-azl-{arch}{extension}"); + } + + // Runtime deps distro variants (RPM only) + string[] distros = new[] { "azl.3", "opensuse.15", "sles.15" }; + foreach (string distro in distros) + { + patterns.Add($"dotnet-runtime-deps-*-{distro}-{arch}{extension}"); + + // `azl` deps packages do not have a -newkey- variant + if (distro != "azl.3") + { + patterns.Add($"dotnet-runtime-deps-*-{distro}-newkey-{arch}{extension}"); + } + } + } + + return patterns; + } }