diff --git a/Directory.Build.props b/Directory.Build.props index 187d51109e3..d212ce253ae 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -32,7 +32,7 @@ true true - true + true runtime @@ -194,9 +194,9 @@ .prebuilt.xml - $(PackageReportDir)poison-catalog.xml - $(PackageReportDir)poisoned.txt - $(PackageReportDir)poison-usage.xml + $(ArtifactsLogDir)poison-catalog.xml + $(ArtifactsLogDir)poisoned.txt + $(ArtifactsLogDir)poison-usage.xml diff --git a/eng/init-poison.proj b/eng/init-poison.proj index b04716e4680..5937223b510 100644 --- a/eng/init-poison.proj +++ b/eng/init-poison.proj @@ -15,7 +15,7 @@ Outputs="$(BaseIntermediateOutputPath)PoisonPrebuiltPackages.complete"> .source-built.xml - $(PackageReportDir)poison-source-built-catalog.xml + $(ArtifactsLogDir)poison-source-built-catalog.xml diff --git a/eng/pipelines/templates/jobs/vmr-build.yml b/eng/pipelines/templates/jobs/vmr-build.yml index cdc7160e04a..729fdd5acb7 100644 --- a/eng/pipelines/templates/jobs/vmr-build.yml +++ b/eng/pipelines/templates/jobs/vmr-build.yml @@ -606,16 +606,6 @@ jobs: continueOnError: true condition: succeededOrFailed() - - ${{ if eq(parameters.buildSourceOnly, 'True') }}: - - task: CopyFiles@2 - displayName: Copy prebuilt-report to BuildLogs - inputs: - SourceFolder: '$(sourcesPath)' - Contents: artifacts/prebuilt-report/** - TargetFolder: '$(Build.ArtifactStagingDirectory)/BuildLogs' - continueOnError: true - condition: succeededOrFailed() - - ${{ if or(ne(variables['System.TeamProject'], 'internal'), eq(variables['Build.Reason'], 'PullRequest')) }}: - publish: $(Build.ArtifactStagingDirectory)/BuildLogs artifact: $(Agent.JobName)_BuildLogs_Attempt$(System.JobAttempt) diff --git a/eng/pipelines/templates/stages/vmr-verticals.yml b/eng/pipelines/templates/stages/vmr-verticals.yml index 00221eb954f..906d2172696 100644 --- a/eng/pipelines/templates/stages/vmr-verticals.yml +++ b/eng/pipelines/templates/stages/vmr-verticals.yml @@ -210,7 +210,7 @@ stages: crossRootFs: '/crossrootfs/x64' targetOS: browser targetArchitecture: wasm - extraProperties: /p:DotNetBuildRuntimeWasmEnableThreads=true + extraProperties: /p:WasmEnableThreads=true - template: ../jobs/vmr-build.yml parameters: @@ -329,7 +329,7 @@ stages: useMonoRuntime: true targetOS: osx targetArchitecture: x64 - extraProperties: /p:DotNetBuildMonoEnableLLVM=true /p:DotNetBuildMonoAOTEnableLLVM=true /p:DotNetBuildMonoBundleLLVMOptimizer=true + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: @@ -368,7 +368,7 @@ stages: useMonoRuntime: true targetOS: linux targetArchitecture: x64 - extraProperties: /p:DotNetBuildMonoEnableLLVM=true /p:DotNetBuildMonoAOTEnableLLVM=true /p:DotNetBuildMonoBundleLLVMOptimizer=true + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: @@ -458,7 +458,7 @@ stages: useMonoRuntime: true targetOS: linux targetArchitecture: arm64 - extraProperties: /p:DotNetBuildMonoEnableLLVM=true /p:DotNetBuildMonoAOTEnableLLVM=true /p:DotNetBuildMonoBundleLLVMOptimizer=true + extraProperties: /p:MonoEnableLLVM=true /p:MonoAOTEnableLLVM=true /p:MonoBundleLLVMOptimizer=true - template: ../jobs/vmr-build.yml parameters: diff --git a/prereqs/git-info/cecil.props b/prereqs/git-info/cecil.props index 09461395a29..fa78d329b2b 100644 --- a/prereqs/git-info/cecil.props +++ b/prereqs/git-info/cecil.props @@ -1,8 +1,8 @@  - b861ffe40adc95775897af7f77cdae5292ac8c35 - 20250429.1 - 0.11.5-alpha.25229.1 + 8f5ef5bf825aed91bb15039b02f7426f165b5eb8 + 20250501.1 + 0.11.5-alpha.25251.1 \ No newline at end of file diff --git a/prereqs/git-info/deployment-tools.props b/prereqs/git-info/deployment-tools.props index af957d16bbc..40dbd8c76c4 100644 --- a/prereqs/git-info/deployment-tools.props +++ b/prereqs/git-info/deployment-tools.props @@ -1,8 +1,8 @@  - 0d838d1284cb95c963ac49987dfc2fa5d93a11e7 - 20250428.1 - 9.0.0-preview.1.25228.1 + 150d38efe20041a832482eb69e0d61b5d795e167 + 20250501.1 + 9.0.0-preview.1.25251.1 \ No newline at end of file diff --git a/prereqs/git-info/efcore.props b/prereqs/git-info/efcore.props index eeac697c23a..289ac8ac001 100644 --- a/prereqs/git-info/efcore.props +++ b/prereqs/git-info/efcore.props @@ -1,8 +1,8 @@  - c481582e500624eb9b60bba55e196c2da356be9f - 20250430.1 - 10.0.0-preview.5.25230.1 + 4ff9270f21135dba86d678b8515ed2ba2e15089d + 20250501.4 + 10.0.0-preview.5.25251.4 \ No newline at end of file diff --git a/prereqs/git-info/nuget-client.props b/prereqs/git-info/nuget-client.props index 4cc0db37ab2..322994843a9 100644 --- a/prereqs/git-info/nuget-client.props +++ b/prereqs/git-info/nuget-client.props @@ -1,8 +1,8 @@  - d535886475b1eeda1813eb1947af23bd046abb25 - 20250416.1 - 6.15.0-preview.1.7 + 86c695c5bdd02ca0aa735eed42508b57f695baa7 + 6.15.0.31 + 6.15.0-preview.1.31 \ No newline at end of file diff --git a/prereqs/git-info/runtime.props b/prereqs/git-info/runtime.props index 0dc0f6da516..284dff843ae 100644 --- a/prereqs/git-info/runtime.props +++ b/prereqs/git-info/runtime.props @@ -1,8 +1,8 @@  - 6ee6d40609b14602c20e762fac07c0da544ea1b8 - 20250430.7 - 10.0.0-preview.5.25230.7 + 27604b57bd2e9c9aa46d005db7c4e387d461b5b6 + 20250501.7 + 10.0.0-preview.5.25251.7 \ No newline at end of file diff --git a/prereqs/git-info/sdk.props b/prereqs/git-info/sdk.props index c90ed8e96aa..7c58cc4ca62 100644 --- a/prereqs/git-info/sdk.props +++ b/prereqs/git-info/sdk.props @@ -1,8 +1,8 @@  - 2e6a71352a5312f8b136f6530891f4d208c2a83b - 20250424.4 - 10.0.100-preview.5.25224.4 + 7601b9cae2f49701609df537fe81ae73425109ed + 20250501.4 + 10.0.100-preview.5.25251.4 \ No newline at end of file diff --git a/prereqs/git-info/source-build-reference-packages.props b/prereqs/git-info/source-build-reference-packages.props index 5dbf9dfd8cb..01f85e6fe51 100644 --- a/prereqs/git-info/source-build-reference-packages.props +++ b/prereqs/git-info/source-build-reference-packages.props @@ -1,8 +1,8 @@  - 64b59155edd234b5352eead067a2b005410061c7 - 20250428.1 - 10.0.622801 + d1ab3b44c05f314be6885d1933d7e48fffa0cfc2 + 20250501.1 + \ No newline at end of file diff --git a/prereqs/git-info/sourcelink.props b/prereqs/git-info/sourcelink.props index 1afadc6438c..667fe10d29e 100644 --- a/prereqs/git-info/sourcelink.props +++ b/prereqs/git-info/sourcelink.props @@ -1,8 +1,8 @@  - fe14310ca1da9c2d63c377fe590dcf5f6a2d013e - 20250430.1 - 10.0.0-beta.25230.1 + 25716402f74dfeb1f4137601d013e158628d5196 + 20250501.2 + 10.0.0-beta.25251.2 \ No newline at end of file diff --git a/prereqs/git-info/templating.props b/prereqs/git-info/templating.props index 1a5db18dacf..7b443e349f8 100644 --- a/prereqs/git-info/templating.props +++ b/prereqs/git-info/templating.props @@ -1,8 +1,8 @@  - 2bb998b46268d8c38222a709ecf9fe8140df8393 - 20250430.1 - 10.0.100-preview.5.25230.1 + 03eef3b9429ba0975982ca7427274e5986ef328e + 20250501.2 + 10.0.100-preview.5.25251.2 \ No newline at end of file diff --git a/prereqs/git-info/windowsdesktop.props b/prereqs/git-info/windowsdesktop.props index cd13f69e888..0f6ce4308a4 100644 --- a/prereqs/git-info/windowsdesktop.props +++ b/prereqs/git-info/windowsdesktop.props @@ -1,8 +1,8 @@  - 70420a1fc04cf9a1efd42570a2873fa5bdb18ccb - 20250429.1 - 10.0.0-preview.5.25229.1 + 2ebb7d189eccf908c7121a5c1e5e71da0085c4dc + 20250501.1 + 10.0.0-preview.5.25251.1 \ No newline at end of file diff --git a/prereqs/git-info/winforms.props b/prereqs/git-info/winforms.props index 4c95910ff7b..af62b9f81d2 100644 --- a/prereqs/git-info/winforms.props +++ b/prereqs/git-info/winforms.props @@ -1,8 +1,8 @@  - a345d2c077aad9aad8346aef79938c26ca029857 - 20250429.4 - 10.0.0-preview.5.25229.4 + 43ffa433a31ca3a6af20cefadf2b13ba2a7e537d + 20250501.4 + 10.0.0-preview.5.25251.4 \ No newline at end of file diff --git a/repo-projects/Directory.Build.props b/repo-projects/Directory.Build.props index d9f792eca96..c17a695a8cc 100644 --- a/repo-projects/Directory.Build.props +++ b/repo-projects/Directory.Build.props @@ -19,6 +19,11 @@ https://github.com/dotnet/source-build/issues/4922 --> + + + $([System.DateTime]::Now.ToString("yyyyMMdd")).1 + + netstandard2.0 @@ -56,7 +61,6 @@ $([MSBuild]::ValueOrDefault('$(ARCADE_BOOTSTRAP_VERSION)', '$(ArcadeSdkVersion)')) $([MSBuild]::NormalizeDirectory('$(ArtifactsLogDir)', '$(RepositoryName)')) - $([MSBuild]::NormalizeDirectory('$(PackageReportDir)', '$(RepositoryName)')) Windows_x64 diff --git a/repo-projects/Directory.Build.targets b/repo-projects/Directory.Build.targets index 45eab3f2e44..39a1eb7e66e 100644 --- a/repo-projects/Directory.Build.targets +++ b/repo-projects/Directory.Build.targets @@ -303,12 +303,12 @@ - + - + - @@ -701,23 +701,15 @@ + Condition="'$(IsUtilityProject)' != 'true'"> - - - + - - - diff --git a/repo-projects/runtime.proj b/repo-projects/runtime.proj index 38f76647dc6..65057174f78 100644 --- a/repo-projects/runtime.proj +++ b/repo-projects/runtime.proj @@ -24,10 +24,10 @@ $(BuildArgs) /p:DotNetBuildAllRuntimePacks=true - $(BuildArgs) /p:DotNetBuildRuntimeWasmEnableThreads=true - $(BuildArgs) /p:DotNetBuildMonoEnableLLVM=$(DotNetBuildMonoEnableLLVM) - $(BuildArgs) /p:DotNetBuildMonoAOTEnableLLVM=$(DotNetBuildMonoAOTEnableLLVM) - $(BuildArgs) /p:DotNetBuildMonoBundleLLVMOptimizer=$(DotNetBuildMonoBundleLLVMOptimizer) + $(BuildArgs) /p:WasmEnableThreads=true + $(BuildArgs) /p:MonoEnableLLVM=$(MonoEnableLLVM) + $(BuildArgs) /p:MonoAOTEnableLLVM=$(MonoAOTEnableLLVM) + $(BuildArgs) /p:MonoBundleLLVMOptimizer=$(MonoBundleLLVMOptimizer) $(BuildArgs) $(FlagParameterPrefix)pgoinstrument $(BuildArgs) /p:UseSystemLibs=$(UseSystemLibs) diff --git a/src/arcade/eng/common/core-templates/steps/source-build.yml b/src/arcade/eng/common/core-templates/steps/source-build.yml index c6b9ef51ac6..9292c9b67a3 100644 --- a/src/arcade/eng/common/core-templates/steps/source-build.yml +++ b/src/arcade/eng/common/core-templates/steps/source-build.yml @@ -46,11 +46,6 @@ steps: buildConfig='$(_BuildConfig)' fi - officialBuildArgs= - if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then - officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)' - fi - targetRidArgs= if [ '${{ parameters.platform.targetRID }}' != '' ]; then targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' @@ -66,16 +61,6 @@ steps: baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}' fi - publishArgs= - if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then - publishArgs='--publish' - fi - - assetManifestFileName=SourceBuild_RidSpecific.xml - if [ '${{ parameters.platform.name }}' != '' ]; then - assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml - fi - portableBuildArgs= if [ '${{ parameters.platform.portableBuild }}' != '' ]; then portableBuildArgs='/p:PortableBuild=${{ parameters.platform.portableBuild }}' @@ -83,9 +68,8 @@ steps: ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ --configuration $buildConfig \ - --restore --build --pack $publishArgs -bl \ + --restore --build --pack -bl \ ${{ parameters.platform.buildArguments }} \ - $officialBuildArgs \ $internalRuntimeDownloadArgs \ $internalRestoreArgs \ $targetRidArgs \ @@ -94,7 +78,6 @@ steps: $portableBuildArgs \ /p:DotNetBuildSourceOnly=true \ /p:DotNetBuildRepo=true \ - /p:AssetManifestFileName=$assetManifestFileName displayName: Build # Upload build logs for diagnosis. diff --git a/src/cecil/eng/Version.Details.xml b/src/cecil/eng/Version.Details.xml index 4fa76d855f8..42e6c55b41a 100644 --- a/src/cecil/eng/Version.Details.xml +++ b/src/cecil/eng/Version.Details.xml @@ -1,12 +1,12 @@ - + - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 diff --git a/src/cecil/eng/common/core-templates/jobs/source-build.yml b/src/cecil/eng/common/core-templates/jobs/source-build.yml index a10ccfbee6d..df24c948ba1 100644 --- a/src/cecil/eng/common/core-templates/jobs/source-build.yml +++ b/src/cecil/eng/common/core-templates/jobs/source-build.yml @@ -14,7 +14,7 @@ parameters: # This is the default platform provided by Arcade, intended for use by a managed-only repo. defaultManagedPlatform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-amd64' # Defines the platforms on which to run build jobs. One job is created for each platform, and the # object in this array is sent to the job template as 'platform'. If no platforms are specified, diff --git a/src/cecil/eng/common/core-templates/steps/source-build.yml b/src/cecil/eng/common/core-templates/steps/source-build.yml index 8c88ccd7b08..c6b9ef51ac6 100644 --- a/src/cecil/eng/common/core-templates/steps/source-build.yml +++ b/src/cecil/eng/common/core-templates/steps/source-build.yml @@ -121,14 +121,3 @@ steps: continueOnError: true condition: succeededOrFailed() sbomEnabled: false # we don't need SBOM for logs - -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- template: /eng/common/core-templates/steps/component-governance.yml - parameters: - displayName: Component Detection (Exclude upstream cache) - is1ESPipeline: ${{ parameters.is1ESPipeline }} - componentGovernanceIgnoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' - disableComponentGovernance: ${{ eq(variables['System.TeamProject'], 'public') }} diff --git a/src/cecil/global.json b/src/cecil/global.json index 88bdc536c7f..62e0ff9c5e0 100644 --- a/src/cecil/global.json +++ b/src/cecil/global.json @@ -4,6 +4,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.0", - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25229.109" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25251.102" } } diff --git a/src/deployment-tools/eng/Version.Details.xml b/src/deployment-tools/eng/Version.Details.xml index eb2d795aca7..fa7e8568896 100644 --- a/src/deployment-tools/eng/Version.Details.xml +++ b/src/deployment-tools/eng/Version.Details.xml @@ -1,11 +1,11 @@ - + - + https://github.com/dotnet/dotnet - efbfb137a317fb339a6b7be36af7188cc508dc95 + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 diff --git a/src/deployment-tools/eng/common/core-templates/jobs/source-build.yml b/src/deployment-tools/eng/common/core-templates/jobs/source-build.yml index a10ccfbee6d..df24c948ba1 100644 --- a/src/deployment-tools/eng/common/core-templates/jobs/source-build.yml +++ b/src/deployment-tools/eng/common/core-templates/jobs/source-build.yml @@ -14,7 +14,7 @@ parameters: # This is the default platform provided by Arcade, intended for use by a managed-only repo. defaultManagedPlatform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-amd64' # Defines the platforms on which to run build jobs. One job is created for each platform, and the # object in this array is sent to the job template as 'platform'. If no platforms are specified, diff --git a/src/deployment-tools/eng/common/core-templates/steps/source-build.yml b/src/deployment-tools/eng/common/core-templates/steps/source-build.yml index 8c88ccd7b08..c6b9ef51ac6 100644 --- a/src/deployment-tools/eng/common/core-templates/steps/source-build.yml +++ b/src/deployment-tools/eng/common/core-templates/steps/source-build.yml @@ -121,14 +121,3 @@ steps: continueOnError: true condition: succeededOrFailed() sbomEnabled: false # we don't need SBOM for logs - -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- template: /eng/common/core-templates/steps/component-governance.yml - parameters: - displayName: Component Detection (Exclude upstream cache) - is1ESPipeline: ${{ parameters.is1ESPipeline }} - componentGovernanceIgnoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' - disableComponentGovernance: ${{ eq(variables['System.TeamProject'], 'public') }} diff --git a/src/deployment-tools/global.json b/src/deployment-tools/global.json index 5982d5d3906..4ae23dd54d1 100644 --- a/src/deployment-tools/global.json +++ b/src/deployment-tools/global.json @@ -8,7 +8,7 @@ "dotnet": "10.0.100-preview.3.25201.16" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25228.104", + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25251.102", "Microsoft.Build.NoTargets": "3.7.0", "Microsoft.Build.Traversal": "3.4.0" } diff --git a/src/efcore/Directory.Packages.props b/src/efcore/Directory.Packages.props index a367f152413..ff78d09a621 100644 --- a/src/efcore/Directory.Packages.props +++ b/src/efcore/Directory.Packages.props @@ -40,7 +40,7 @@ - + diff --git a/src/efcore/eng/Version.Details.xml b/src/efcore/eng/Version.Details.xml index 860be0492c1..e65d9e17619 100644 --- a/src/efcore/eng/Version.Details.xml +++ b/src/efcore/eng/Version.Details.xml @@ -1,84 +1,84 @@ - + - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 - + https://github.com/dotnet/dotnet - 78c5fa9a48d469a19ab5a61c16c955c1f370b5be + dab45dead83a6cf3e5fa9df1396c51e6a6b61c07 diff --git a/src/efcore/eng/Versions.props b/src/efcore/eng/Versions.props index 6dc3e330955..3fdb61b5ed5 100644 --- a/src/efcore/eng/Versions.props +++ b/src/efcore/eng/Versions.props @@ -16,24 +16,24 @@ False - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 - 10.0.0-preview.5.25229.109 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 + 10.0.0-preview.5.25251.102 - 10.0.0-beta.25229.109 + 10.0.0-beta.25251.102 17.13.9 @@ -42,7 +42,7 @@ 4.13.0 1.1.3-beta1.24423.1 1.1.3-beta1.24352.1 - 1.13.1 + 1.13.2 1.3.2 1.8.1 2.1.10 diff --git a/src/efcore/eng/common/core-templates/jobs/source-build.yml b/src/efcore/eng/common/core-templates/jobs/source-build.yml index a10ccfbee6d..df24c948ba1 100644 --- a/src/efcore/eng/common/core-templates/jobs/source-build.yml +++ b/src/efcore/eng/common/core-templates/jobs/source-build.yml @@ -14,7 +14,7 @@ parameters: # This is the default platform provided by Arcade, intended for use by a managed-only repo. defaultManagedPlatform: name: 'Managed' - container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream9' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream-10-amd64' # Defines the platforms on which to run build jobs. One job is created for each platform, and the # object in this array is sent to the job template as 'platform'. If no platforms are specified, diff --git a/src/efcore/eng/common/core-templates/steps/source-build.yml b/src/efcore/eng/common/core-templates/steps/source-build.yml index 8c88ccd7b08..c6b9ef51ac6 100644 --- a/src/efcore/eng/common/core-templates/steps/source-build.yml +++ b/src/efcore/eng/common/core-templates/steps/source-build.yml @@ -121,14 +121,3 @@ steps: continueOnError: true condition: succeededOrFailed() sbomEnabled: false # we don't need SBOM for logs - -# Manually inject component detection so that we can ignore the source build upstream cache, which contains -# a nupkg cache of input packages (a local feed). -# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir' -# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets -- template: /eng/common/core-templates/steps/component-governance.yml - parameters: - displayName: Component Detection (Exclude upstream cache) - is1ESPipeline: ${{ parameters.is1ESPipeline }} - componentGovernanceIgnoreDirectories: '$(Build.SourcesDirectory)/artifacts/sb/src/artifacts/obj/source-built-upstream-cache' - disableComponentGovernance: ${{ eq(variables['System.TeamProject'], 'public') }} diff --git a/src/efcore/global.json b/src/efcore/global.json index 10c39de481f..7497505d190 100644 --- a/src/efcore/global.json +++ b/src/efcore/global.json @@ -13,7 +13,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25229.109", - "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25229.109" + "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25251.102", + "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25251.102" } } diff --git a/src/nuget-client/.gdn/.gdnsuppress b/src/nuget-client/.gdn/.gdnsuppress new file mode 100644 index 00000000000..3febf51caa7 --- /dev/null +++ b/src/nuget-client/.gdn/.gdnsuppress @@ -0,0 +1,35 @@ +{ + "version": "latest", + "suppressionSets": { + "default": { + "name": "default", + "createdDate": "2025-04-14T13:30:47Z", + "lastUpdatedDate": "2025-04-14T13:30:47Z" + } + }, + "results": { + "94b262a20d21544c9527803ee2109eec43f13edee79db6408106a7bf15ffaa0b": { + "signature": "94b262a20d21544c9527803ee2109eec43f13edee79db6408106a7bf15ffaa0b", + "target": "artifacts/NuGet.VisualStudio.Implementation/bin/release/net472/Microsoft.VisualStudio.ManagedInterfaces.dll", + "memberOf": ["default"], + "tool": "BinSkim", + "ruleId": "BA2009", + "justification": "This DLL is not shipped and is not built by our team.", + "createdDate": "2025-04-15 17:38:54Z", + "expirationDate": null, + "type": null + }, + "dadf10d994d221e4d43842f89921d1eca4a2185ef8afa6c6894e5a8faf96afc6": { + "signature": "dadf10d994d221e4d43842f89921d1eca4a2185ef8afa6c6894e5a8faf96afc6", + "target": "artifacts/NuGet.VisualStudio.Implementation/bin/release/net472/Microsoft.VisualStudio.ManagedInterfaces.dll", + "memberOf": ["default"], + "tool": "BinSkim", + "ruleId": "BA2016", + "justification": "This DLL is not shipped and is not built by our team.", + "createdDate": "2025-04-15 17:38:54Z", + "expirationDate": null, + "type": null + } + } + } + \ No newline at end of file diff --git a/src/nuget-client/docs/workflow.md b/src/nuget-client/docs/workflow.md index f52c60bf147..0897656bbc1 100644 --- a/src/nuget-client/docs/workflow.md +++ b/src/nuget-client/docs/workflow.md @@ -29,6 +29,7 @@ These are guidelines to follow but unless indicated, they are not required. - Try your best not to take feedback personally, we're all working towards the same goals. - When conflict arises try to address it directly with the commentor offline, preferably synchronously. - Provide an explanation if you decide not to implement a recommendation. + - Feel free to resolve conversations for which you've made the suggested code changes. ### Merging Pull Requests diff --git a/src/nuget-client/eng/Publishing.props b/src/nuget-client/eng/Publishing.props index 21cd8b66441..28c55419b1d 100644 --- a/src/nuget-client/eng/Publishing.props +++ b/src/nuget-client/eng/Publishing.props @@ -7,4 +7,10 @@ + + + + + diff --git a/src/nuget-client/eng/dotnet-build/build.ps1 b/src/nuget-client/eng/dotnet-build/build.ps1 index b0278404450..ee845b44586 100644 --- a/src/nuget-client/eng/dotnet-build/build.ps1 +++ b/src/nuget-client/eng/dotnet-build/build.ps1 @@ -1,11 +1,12 @@ # The VMR orchestrator passes a number of stnadar param ( - [string]$Configuration, - [switch]$ci, - [switch]$bl, - [Parameter(ValueFromRemainingArguments = $true)] - [string[]]$AdditionalArgs + [string][Alias('c')]$configuration = "Release", + [string][Alias('v')]$verbosity = "minimal", + [switch]$ci, + [switch]$bl, + [Parameter(ValueFromRemainingArguments = $true)] + [string[]]$AdditionalArgs ) # This will exec a process using the console and return it's exit code. @@ -55,6 +56,8 @@ $env:NUGET_PACKAGES=$nugetPackagesRoot # MSBuild arguments # Add the dotnet tool... $dotnetArguments += $dotnetTool +# Add the verbosity flag +$dotnetArguments += "-v:$verbosity" # Then project file... $dotnetArguments += "$PSScriptRoot/dotnet-build.proj" # Then remaining arguments. diff --git a/src/nuget-client/eng/dotnet-build/build.sh b/src/nuget-client/eng/dotnet-build/build.sh index 8bb636266cc..908ebd5b70a 100755 --- a/src/nuget-client/eng/dotnet-build/build.sh +++ b/src/nuget-client/eng/dotnet-build/build.sh @@ -6,6 +6,7 @@ git config --global protocol.file.allow always source="${BASH_SOURCE[0]}" scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" configuration='Release' +verbosity='minimal' source_build=false properties='' @@ -25,6 +26,10 @@ repo_root="${repo_root}/" while [[ $# > 0 ]]; do lowerI="$(echo $1 | awk '{print tolower($0)}')" case $lowerI in + --verbosity|-v) + verbosity=$2 + shift + ;; --configuration|-c) configuration=$2 shift @@ -91,4 +96,4 @@ properties="$properties /p:Configuration=$configuration" properties="$properties /p:DotNetBuildRepo=true" properties="$properties /p:RepoRoot=$repo_root" -"$DOTNET" msbuild "$scriptroot/dotnet-build.proj" "/bl:${repo_root}artifacts/sb/log/source-inner-build.binlog" $properties $args +"$DOTNET" msbuild -v:$verbosity "$scriptroot/dotnet-build.proj" "/bl:${repo_root}artifacts/sb/log/source-inner-build.binlog" $properties $args diff --git a/src/nuget-client/eng/pipelines/official.yml b/src/nuget-client/eng/pipelines/official.yml index b23dc644672..932abcdea32 100644 --- a/src/nuget-client/eng/pipelines/official.yml +++ b/src/nuget-client/eng/pipelines/official.yml @@ -71,6 +71,8 @@ extends: enabled: true credscan: enabled: false + suppression: + suppressionFile: $(Build.SourcesDirectory)\.gdn\.gdnsuppress pool: name: AzurePipelines-EO image: 1ESPT-Windows2022 diff --git a/src/nuget-client/eng/pipelines/pull_request.yml b/src/nuget-client/eng/pipelines/pull_request.yml index 9c58cc9b796..d7343171474 100644 --- a/src/nuget-client/eng/pipelines/pull_request.yml +++ b/src/nuget-client/eng/pipelines/pull_request.yml @@ -75,6 +75,8 @@ extends: enabled: true credscan: enabled: false + suppression: + suppressionFile: $(Build.SourcesDirectory)\.gdn\.gdnsuppress pool: name: AzurePipelines-EO image: 1ESPT-Windows2022 diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/RestoreCommand.cs b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/RestoreCommand.cs index 60d0e59967d..0ae7b05d750 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/RestoreCommand.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/RestoreCommand.cs @@ -834,8 +834,7 @@ private void GetInputsFromFile(string projectFilePath, PackageRestoreInputs pack { packageRestoreInputs.RestoreV3Context.Inputs.Add(projectFilePath); } - else if (projectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase) - || projectFileName.EndsWith(".slnf", StringComparison.OrdinalIgnoreCase)) + else if (projectFileName.IsSolutionFile()) { ProcessSolutionFile(projectFilePath, packageRestoreInputs); } @@ -859,9 +858,7 @@ private void GetInputsFromDirectory(string directory, PackageRestoreInputs packa var topLevelFiles = Directory.GetFiles(directory, "*.*", SearchOption.TopDirectoryOnly); // Solution files - var solutionFiles = topLevelFiles.Where(file => - file.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) - .ToArray(); + var solutionFiles = topLevelFiles.Where(file => file.IsSolutionFile()).ToArray(); if (solutionFiles.Length > 0) { @@ -925,7 +922,7 @@ private static bool IsSolutionOrProjectFile(string fileName) lastFourCharacters = extension.Substring(length - 4); } - return (string.Equals(extension, ".sln", StringComparison.OrdinalIgnoreCase) + return (fileName.IsSolutionFile() || string.Equals(lastFourCharacters, "proj", StringComparison.OrdinalIgnoreCase)); } return false; @@ -998,6 +995,16 @@ private string GetPackageReferenceFile(string projectFile) private void ProcessSolutionFile(string solutionFileFullPath, PackageRestoreInputs restoreInputs) { + var msBuildToolset = MsBuildDirectory.Value; + if (Path.GetExtension(solutionFileFullPath).Equals(".slnx", StringComparison.OrdinalIgnoreCase) + && msBuildToolset.ParsedVersion < new Version(17, 13)) + { + throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + LocalizedResourceManager.GetString(nameof(NuGetResources.Error_UnsupportedMsBuildForSlnx)), + msBuildToolset.Version)); + } + restoreInputs.DirectoryOfSolutionFile = Path.GetDirectoryName(solutionFileFullPath); restoreInputs.NameOfSolutionFile = Path.GetFileNameWithoutExtension(solutionFileFullPath); diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/UpdateCommand.cs b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/UpdateCommand.cs index 19e09ab5bbd..8c7b449f4a0 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/UpdateCommand.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Commands/UpdateCommand.cs @@ -238,7 +238,7 @@ private string GetInputFile() return GetPackagesConfigPath(path); } - if (extension.Equals(".sln", StringComparison.OrdinalIgnoreCase)) + if (path.IsSolutionFile()) { return Path.GetFullPath(path); } diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Common/ProjectHelper.cs b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Common/ProjectHelper.cs index 6c661af6097..e0614f58740 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Common/ProjectHelper.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/Common/ProjectHelper.cs @@ -27,6 +27,10 @@ public static HashSet SupportedProjectExtensions } } + public static bool IsSolutionFile(this string filepath) => filepath.EndsWith(".sln", StringComparison.OrdinalIgnoreCase) + || filepath.EndsWith(".slnf", StringComparison.OrdinalIgnoreCase) + || filepath.EndsWith(".slnx", StringComparison.OrdinalIgnoreCase); + public static bool TryGetProjectFile(string directory, out string projectFile) { projectFile = null; @@ -70,7 +74,7 @@ public static string GetSolutionDir(string projectDirectory) private static bool SolutionFileExists(string path) { - return Directory.GetFiles(path, "*.sln").Any(); + return Directory.EnumerateFiles(path, "*.*").Any(f => f.IsSolutionFile()); } } } diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.Designer.cs b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.Designer.cs index 130818274b4..7429fed3318 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.Designer.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.Designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // Runtime Version:4.0.30319.42000 @@ -629,6 +629,15 @@ public static string Error_UnsupportedMsbuild { } } + /// + /// Looks up a localized string similar to This version of MSBuild not support the slnx format: '{0}'. Please use a MSBuild version greater or equal to 17.13 to use this feature.. + /// + public static string Error_UnsupportedMsBuildForSlnx { + get { + return ResourceManager.GetString("Error_UnsupportedMsBuildForSlnx", resourceCulture); + } + } + /// /// Looks up a localized string similar to This version of MSBuild not support Solution filter: '{0}'. Please use a MSBuild version greater or equal to 16.7 to use this feature.. /// diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.resx b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.resx index f5370aea3ed..3a659370c8b 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.resx +++ b/src/nuget-client/src/NuGet.Clients/NuGet.CommandLine/NuGetResources.resx @@ -726,6 +726,9 @@ Do not localize `SDK`, `dotnet pack`, msbuild -t:pack` and 'true'. This version of MSBuild not support Solution filter: '{0}'. Please use a MSBuild version greater or equal to 16.7 to use this feature. + + This version of MSBuild not support the slnx format: '{0}'. Please use a MSBuild version greater or equal to 17.13 to use this feature. + Invalid culture identifier in {0} environment variable. Value read is '{1}' 0 - Environment variable name, 1 - value set in environment variable diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Design/PackageItemListViewSampleData.xaml b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Design/PackageItemListViewSampleData.xaml deleted file mode 100644 index 7178fe65920..00000000000 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Design/PackageItemListViewSampleData.xaml +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/DetailControlModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/DetailControlModel.cs index 35d03d52789..df121b73364 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/DetailControlModel.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/DetailControlModel.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using System.Windows.Media.Imaging; using Microsoft.ServiceHub.Framework; +using NuGet.PackageManagement.UI.Models.Package; using NuGet.PackageManagement.UI.ViewModels; using NuGet.PackageManagement.VisualStudio; using NuGet.Packaging.Core; @@ -474,9 +475,9 @@ public string ExplainPackageDeprecationReasons(IReadOnlyCollection reaso { return Resources.Label_DeprecationReasons_Unknown; } - else if (reasons.Contains(PackageDeprecationReason.CriticalBugs, StringComparer.OrdinalIgnoreCase)) + else if (reasons.Contains(PackageDeprecationReasonConstants.CriticalBugs, StringComparer.OrdinalIgnoreCase)) { - if (reasons.Contains(PackageDeprecationReason.Legacy, StringComparer.OrdinalIgnoreCase)) + if (reasons.Contains(PackageDeprecationReasonConstants.Legacy, StringComparer.OrdinalIgnoreCase)) { return Resources.Label_DeprecationReasons_LegacyAndCriticalBugs; } @@ -485,7 +486,7 @@ public string ExplainPackageDeprecationReasons(IReadOnlyCollection reaso return Resources.Label_DeprecationReasons_CriticalBugs; } } - else if (reasons.Contains(PackageDeprecationReason.Legacy, StringComparer.OrdinalIgnoreCase)) + else if (reasons.Contains(PackageDeprecationReasonConstants.Legacy, StringComparer.OrdinalIgnoreCase)) { return Resources.Label_DeprecationReasons_Legacy; } @@ -495,12 +496,6 @@ public string ExplainPackageDeprecationReasons(IReadOnlyCollection reaso } } - private static class PackageDeprecationReason - { - public const string CriticalBugs = nameof(CriticalBugs); - public const string Legacy = nameof(Legacy); - } - private DetailedPackageMetadata _packageMetadata; public DetailedPackageMetadata PackageMetadata diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DeprecationPackageMetadataCapability.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DeprecationPackageMetadataCapability.cs new file mode 100644 index 00000000000..10c86652023 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DeprecationPackageMetadataCapability.cs @@ -0,0 +1,77 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Threading; +using System.Threading.Tasks; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class DeprecationPackageMetadataCapability : IDeprecationCapable + { + private readonly IPackageMetadataRetrievalAdapter _packageMetadataRetrievalAdapter; + private PackageDeprecationMetadataContextInfo? _deprecationMetadata; + + public DeprecationPackageMetadataCapability(IPackageMetadataRetrievalAdapter packageMetadataRetrievalAdapter) + { + _packageMetadataRetrievalAdapter = packageMetadataRetrievalAdapter ?? throw new ArgumentNullException(nameof(packageMetadataRetrievalAdapter)); + } + + public AlternatePackageMetadataContextInfo? AlternatePackage => _deprecationMetadata?.AlternatePackage; + + public bool IsDeprecated => _deprecationMetadata != null; + + public PackageDeprecationReason PackageDeprecationReasons + { + get + { + if (_deprecationMetadata?.Reasons == null || _deprecationMetadata.Reasons.Count == 0) + { + return PackageDeprecationReason.Unknown; + } + + bool hasCriticalBugs = false; + bool hasLegacy = false; + + foreach (var reason in _deprecationMetadata.Reasons) + { + if (string.Equals(reason, PackageDeprecationReasonConstants.CriticalBugs, StringComparison.OrdinalIgnoreCase)) + { + hasCriticalBugs = true; + } + else if (string.Equals(reason, PackageDeprecationReasonConstants.Legacy, StringComparison.OrdinalIgnoreCase)) + { + hasLegacy = true; + } + + if (hasCriticalBugs && hasLegacy) + { + return PackageDeprecationReason.LegacyAndCriticalBugs; + } + } + + if (hasCriticalBugs) + { + return PackageDeprecationReason.CriticalBugs; + } + + if (hasLegacy) + { + return PackageDeprecationReason.Legacy; + } + + return PackageDeprecationReason.Unknown; + } + } + + public PackageDeprecationMetadataContextInfo? DeprecationMetadata => _deprecationMetadata; + + public async Task PopulateDataAsync(CancellationToken cancellationToken) + { + _deprecationMetadata = await _packageMetadataRetrievalAdapter.GetPackageDeprecationInfoAsync(cancellationToken); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DirectlyReferencedPackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DirectlyReferencedPackageModel.cs new file mode 100644 index 00000000000..9c8cf4ecb7e --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/DirectlyReferencedPackageModel.cs @@ -0,0 +1,88 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class DirectlyReferencedPackageModel : PackageModel, IDeprecationCapable, IVulnerableCapable + { + private readonly IDeprecationCapable _deprecationCapability; + private readonly IVulnerableCapable _vulnerableCapability; + + public DirectlyReferencedPackageModel( + PackageIdentity identity, + string packagePath, + IVulnerableCapable vulnerableCapability, + IDeprecationCapable deprecationCapability, + IEmbeddedResourcesCapable embeddedResources, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + string? reportAbuseUrl, + Uri? iconUrl) + : base(identity, + embeddedResources, + title, + description, + authors, + projectUrl, + tags, + ownersList, + packageDependencyGroups, + summary, + publishedDate, + licenseMetadata, + licenseUrl, + requireLicenseAcceptance, + iconUrl) + { + ReportAbuseUrl = reportAbuseUrl; + _deprecationCapability = deprecationCapability ?? throw new ArgumentNullException(nameof(deprecationCapability)); + _vulnerableCapability = vulnerableCapability ?? throw new ArgumentNullException(nameof(vulnerableCapability)); + PackagePath = packagePath; + } + + public string PackagePath { get; } + + public string? ReportAbuseUrl { get; } + + public bool IsDeprecated => _deprecationCapability.IsDeprecated; + + public PackageDeprecationReason PackageDeprecationReasons => _deprecationCapability.PackageDeprecationReasons; + + public AlternatePackageMetadataContextInfo? AlternatePackage => _deprecationCapability.AlternatePackage; + + public IReadOnlyList? Vulnerabilities => _vulnerableCapability.Vulnerabilities; + + public bool IsVulnerable => _vulnerableCapability.IsVulnerable; + + public PackageVulnerabilitySeverity VulnerabilityMaxSeverity => _vulnerableCapability.VulnerabilityMaxSeverity; + + public PackageDeprecationMetadataContextInfo? DeprecationMetadata => _deprecationCapability.DeprecationMetadata; + + public override async Task PopulateDataAsync(CancellationToken cancellationToken) + { + await _vulnerableCapability.PopulateDataAsync(cancellationToken); + await _deprecationCapability.PopulateDataAsync(cancellationToken); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/EmbeddedResourcesCapability.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/EmbeddedResourcesCapability.cs new file mode 100644 index 00000000000..e17e0063940 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/EmbeddedResourcesCapability.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class EmbeddedResourcesCapability : IEmbeddedResourcesCapable + { + private readonly INuGetPackageFileService _nugetPackageFileService; + private readonly PackageIdentity _packageIdentity; + + public EmbeddedResourcesCapability(INuGetPackageFileService nugetPackageFileService, PackageIdentity packageIdentity, Uri? readmeUri) + { + _nugetPackageFileService = nugetPackageFileService ?? throw new ArgumentNullException(nameof(nugetPackageFileService)); + _packageIdentity = packageIdentity ?? throw new ArgumentNullException(nameof(packageIdentity)); + ReadmeUri = readmeUri; + } + + public Uri? ReadmeUri { get; } + + public ValueTask GetIconAsync(CancellationToken cancellationToken) + { + return _nugetPackageFileService.GetPackageIconAsync(_packageIdentity, cancellationToken); + } + + public ValueTask GetLicenseAsync(CancellationToken cancellationToken) + { + return _nugetPackageFileService.GetEmbeddedLicenseAsync(_packageIdentity, cancellationToken); + } + + public async ValueTask GetReadmeAsync(CancellationToken cancellationToken) + { + if (ReadmeUri != null) + { + return await _nugetPackageFileService.GetReadmeAsync(ReadmeUri, cancellationToken); + } + return null; + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IDeprecationCapable.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IDeprecationCapable.cs new file mode 100644 index 00000000000..fb714dbbb64 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IDeprecationCapable.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System.Threading; +using System.Threading.Tasks; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal interface IDeprecationCapable + { + public bool IsDeprecated { get; } + + public PackageDeprecationReason PackageDeprecationReasons { get; } + + public AlternatePackageMetadataContextInfo? AlternatePackage { get; } + + public Task PopulateDataAsync(CancellationToken cancellationToken); + + public PackageDeprecationMetadataContextInfo? DeprecationMetadata { get; } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IEmbeddedResourcesCapable.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IEmbeddedResourcesCapable.cs new file mode 100644 index 00000000000..f2670d5c076 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IEmbeddedResourcesCapable.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal interface IEmbeddedResourcesCapable + { + Uri? ReadmeUri { get; } + + ValueTask GetIconAsync(CancellationToken cancellationToken); + + ValueTask GetLicenseAsync(CancellationToken cancellationToken); + + ValueTask GetReadmeAsync(CancellationToken cancellationToken); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IPackageMetadataRetrievalAdapter.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IPackageMetadataRetrievalAdapter.cs new file mode 100644 index 00000000000..0af87d8455e --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IPackageMetadataRetrievalAdapter.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System.Threading; +using System.Threading.Tasks; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal interface IPackageMetadataRetrievalAdapter + { + public Task GetPackageMetadataAsync( + CancellationToken cancellationToken); + + public Task GetPackageDeprecationInfoAsync( + CancellationToken cancellationToken); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IVulnerableCapable.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IVulnerableCapable.cs new file mode 100644 index 00000000000..d31b1e99e8c --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/IVulnerableCapable.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal interface IVulnerableCapable + { + public IReadOnlyList? Vulnerabilities { get; } + + public bool IsVulnerable { get; } + + public PackageVulnerabilitySeverity VulnerabilityMaxSeverity { get; } + + public Task PopulateDataAsync(CancellationToken cancellationToken); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/LocalPackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/LocalPackageModel.cs new file mode 100644 index 00000000000..7c3e20d9824 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/LocalPackageModel.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging; +using NuGet.Packaging.Core; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class LocalPackageModel : PackageModel + { + public LocalPackageModel(PackageIdentity identity, + string packagePath, + IEmbeddedResourcesCapable embeddedResources, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + Uri? iconUrl) + : base(identity, embeddedResources, title, description, authors, projectUrl, tags, ownersList, packageDependencyGroups, summary, publishedDate, licenseMetadata, licenseUrl, requireLicenseAcceptance, iconUrl) + { + PackagePath = packagePath; + } + + public string PackagePath { get; } + + public override Task PopulateDataAsync(CancellationToken cancellationToken) + { + // LocalPackageModel does not need to populate any additional data. + return Task.CompletedTask; + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageDeprecationReason.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageDeprecationReason.cs new file mode 100644 index 00000000000..8baa8eb92fb --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageDeprecationReason.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +namespace NuGet.PackageManagement.UI.Models.Package +{ + public enum PackageDeprecationReason + { + Unknown = -1, + Legacy = 0, + CriticalBugs = 1, + LegacyAndCriticalBugs = 2 + } + + public static class PackageDeprecationReasonConstants + { + public const string CriticalBugs = nameof(CriticalBugs); + public const string Legacy = nameof(Legacy); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageMetadataRetrievalAdapter.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageMetadataRetrievalAdapter.cs new file mode 100644 index 00000000000..c0e50b7e561 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageMetadataRetrievalAdapter.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging.Core; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class PackageMetadataRetrievalAdapter : IPackageMetadataRetrievalAdapter + { + private readonly INuGetSearchService _nugetSearchService; + private readonly PackageIdentity _packageIdentity; + private readonly IReadOnlyCollection _packageSources; + private readonly bool _includePrerelease; + private Task<(PackageSearchMetadataContextInfo, PackageDeprecationMetadataContextInfo?)>? _packageMetadataTask; + private readonly object _lock = new(); + + public PackageMetadataRetrievalAdapter(INuGetSearchService nugetSearchService, PackageIdentity packageIdentity, IReadOnlyCollection packageSources, bool includePrerelease) + { + _nugetSearchService = nugetSearchService ?? throw new ArgumentNullException(nameof(nugetSearchService)); + _packageIdentity = packageIdentity ?? throw new ArgumentNullException(nameof(packageIdentity)); + _packageSources = packageSources ?? throw new ArgumentNullException(nameof(packageSources)); + _includePrerelease = includePrerelease; + } + + public async Task GetPackageMetadataAsync( + CancellationToken cancellationToken) + { + var packageMetadata = await FetchMetadataAsync(cancellationToken); + return packageMetadata.Item1; + } + + public async Task GetPackageDeprecationInfoAsync( + CancellationToken cancellationToken) + { + var packageMetadata = await FetchMetadataAsync(cancellationToken); + return packageMetadata.Item2; + } + + private Task<(PackageSearchMetadataContextInfo, PackageDeprecationMetadataContextInfo?)> FetchMetadataAsync( + CancellationToken cancellationToken) + { + if (_packageMetadataTask == null) + { + lock (_lock) + { + _packageMetadataTask ??= _nugetSearchService.GetPackageMetadataAsync( + _packageIdentity, + _packageSources, + _includePrerelease, + cancellationToken).AsTask(); + } + } + + return _packageMetadataTask; + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModel.cs new file mode 100644 index 00000000000..e95fb77e829 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModel.cs @@ -0,0 +1,101 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Versioning; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + public abstract class PackageModel : IEmbeddedResourcesCapable + { + private readonly IEmbeddedResourcesCapable _embeddedResources; + + internal PackageModel(PackageIdentity identity, + IEmbeddedResourcesCapable embeddedResources, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + Uri? iconUrl) + { + _embeddedResources = embeddedResources ?? throw new ArgumentNullException(nameof(embeddedResources)); + Identity = identity ?? throw new ArgumentNullException(nameof(identity)); + Title = title; + Description = description; + Authors = authors; + ProjectUrl = projectUrl; + Tags = tags; + OwnersList = ownersList; + Summary = summary; + PublishedDate = publishedDate; + LicenseMetadata = licenseMetadata; + LicenseUrl = licenseUrl; + RequireLicenseAcceptance = requireLicenseAcceptance; + IconUrl = iconUrl; + + if (packageDependencyGroups != null && packageDependencyGroups.Count > 0) + { + DependencySets = [.. packageDependencyGroups.Select(e => new PackageDependencySetMetadata(e))]; + } + } + + public PackageIdentity Identity { get; } + + public string Id => Identity.Id; + + public NuGetVersion Version => Identity.Version; + + public string? Title { get; } + + public string? Description { get; } + + public string? Authors { get; } + + public IReadOnlyList? OwnersList { get; } + + public IReadOnlyCollection? DependencySets { get; } + + public Uri? ProjectUrl { get; } + + public string[]? Tags { get; } + + public string? Summary { get; } + + public LicenseMetadata? LicenseMetadata { get; } + + public Uri? LicenseUrl { get; } + + public bool RequireLicenseAcceptance { get; } + + public DateTimeOffset? PublishedDate { get; } + + public Uri? IconUrl { get; } + + public Uri? ReadmeUri => _embeddedResources.ReadmeUri; + + public abstract Task PopulateDataAsync(CancellationToken cancellationToken); + + public ValueTask GetIconAsync(CancellationToken cancellationToken) => _embeddedResources.GetIconAsync(cancellationToken); + + public ValueTask GetLicenseAsync(CancellationToken cancellationToken) => _embeddedResources.GetLicenseAsync(cancellationToken); + + public ValueTask GetReadmeAsync(CancellationToken cancellationToken) => _embeddedResources.GetReadmeAsync(cancellationToken); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModelFactory.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModelFactory.cs new file mode 100644 index 00000000000..ac45aadf63c --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/PackageModelFactory.cs @@ -0,0 +1,195 @@ +// Updated PackageModelFactory class +using System; +using System.Collections.Generic; +using NuGet.PackageManagement.VisualStudio; +using NuGet.VisualStudio.Internal.Contracts; +using ContractItemFilter = NuGet.VisualStudio.Internal.Contracts.ItemFilter; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class PackageModelFactory + { + private readonly INuGetSearchService _searchService; + private readonly INuGetPackageFileService _packageFileService; + private readonly IPackageVulnerabilityService _packageVulnerabilityService; + private readonly bool _includePrerelease; + private IReadOnlyCollection _packageSources; + + public PackageModelFactory(INuGetSearchService searchService, INuGetPackageFileService packageFileService, IPackageVulnerabilityService packageVulnerabilityService, bool includePrerelease, IReadOnlyCollection packageSources) + { + _searchService = searchService ?? throw new ArgumentNullException(nameof(_searchService)); + _packageFileService = packageFileService ?? throw new ArgumentNullException(nameof(_packageFileService)); + _packageVulnerabilityService = packageVulnerabilityService ?? throw new ArgumentNullException(nameof(_packageVulnerabilityService)); + _includePrerelease = includePrerelease; + _packageSources = packageSources ?? throw new ArgumentNullException(nameof(_packageSources)); + } + + public PackageModel Create(PackageSearchMetadataContextInfo metadata, ContractItemFilter itemFilter) + { + if (metadata == null) + { + throw new ArgumentNullException(nameof(metadata)); + } + + EmbeddedResourcesCapability embeddedResources = new EmbeddedResourcesCapability(_packageFileService, metadata.Identity!, metadata.ReadmeUrl); + + if (metadata.TransitiveOrigins != null) + { + IVulnerableCapable vulnerableDatabaseCapability = new VulnerableDatabaseCapability(_packageVulnerabilityService, metadata.Identity!); + return CreateTransitivelyReferencedPackageModel(metadata, vulnerableDatabaseCapability, embeddedResources); + } + else + { + if (metadata.PackagePath != null) + { + PackageMetadataRetrievalAdapter packageMetadataRetrievalAdapter = new PackageMetadataRetrievalAdapter(_searchService, metadata.Identity!, _packageSources, _includePrerelease); + IVulnerableCapable vulnerableCapability = new VulnerablePackageMetadataCapability(packageMetadataRetrievalAdapter); + + if (itemFilter.Equals(ContractItemFilter.All)) + { + return CreateLocalPackageModel(metadata, vulnerableCapability, embeddedResources); + } + + IDeprecationCapable deprecationCapable = new DeprecationPackageMetadataCapability(packageMetadataRetrievalAdapter); + return CreateDirectlyReferencedPackageModel(metadata, vulnerableCapability, deprecationCapable, embeddedResources); + } + else + { + PackageMetadataRetrievalAdapter packageMetadataRetrievalAdapter = new PackageMetadataRetrievalAdapter(_searchService, metadata.Identity!, _packageSources, _includePrerelease); + IDeprecationCapable deprecationCapable = new DeprecationPackageMetadataCapability(packageMetadataRetrievalAdapter); + VulnerablePackageMetadataCapability vulnerableCapability = new VulnerablePackageMetadataCapability(packageMetadataRetrievalAdapter); + + if (metadata.IsRecommended) + { + return CreateRecommendedPackageModel(metadata, vulnerableCapability, deprecationCapable, embeddedResources); + } + + return CreateRemotePackageModel(metadata, vulnerableCapability, deprecationCapable, embeddedResources); + } + } + } + + private static LocalPackageModel CreateLocalPackageModel(PackageSearchMetadataContextInfo metadata, IVulnerableCapable vulnerableCapability, EmbeddedResourcesCapability embeddedResources) + { + return new LocalPackageModel( + metadata.Identity!, + metadata.PackagePath!, + embeddedResources, + metadata.Title, + metadata.Description, + metadata.Authors, + metadata.ProjectUrl, + metadata.Tags?.Split(','), + metadata.OwnersList, + metadata.DependencySets, + metadata.Summary, + metadata.Published, + metadata.LicenseMetadata, + metadata.LicenseUrl, + metadata.RequireLicenseAcceptance, + metadata.IconUrl); + } + + private static DirectlyReferencedPackageModel CreateDirectlyReferencedPackageModel(PackageSearchMetadataContextInfo metadata, IVulnerableCapable vulnerableCapability, IDeprecationCapable deprecationCapable, EmbeddedResourcesCapability embeddedResources) + { + return new DirectlyReferencedPackageModel( + metadata.Identity!, + metadata.PackagePath!, + vulnerableCapability, + deprecationCapable, + embeddedResources, + metadata.Title, + metadata.Description, + metadata.Authors, + metadata.ProjectUrl, + metadata.Tags?.Split(','), + metadata.OwnersList, + metadata.DependencySets, + metadata.Summary, + metadata.Published, + metadata.LicenseMetadata, + metadata.LicenseUrl, + metadata.RequireLicenseAcceptance, + metadata.ReportAbuseUrl?.ToString(), + metadata.IconUrl); + } + + private static TransitivelyReferencedPackageModel CreateTransitivelyReferencedPackageModel(PackageSearchMetadataContextInfo metadata, IVulnerableCapable vulnerableDatabaseCapability, EmbeddedResourcesCapability embeddedResources) + { + return new TransitivelyReferencedPackageModel( + metadata.Identity!, + vulnerableDatabaseCapability, + embeddedResources, + metadata.TransitiveOrigins!, + metadata.Title, + metadata.Description, + metadata.Authors, + metadata.ProjectUrl, + metadata.Tags?.Split(','), + metadata.OwnersList, + metadata.DependencySets, + metadata.Summary, + metadata.Published, + metadata.LicenseMetadata, + metadata.LicenseUrl, + metadata.RequireLicenseAcceptance, + metadata.ReportAbuseUrl?.ToString(), + metadata.IconUrl); + } + + private static RecommendedPackageModel CreateRecommendedPackageModel(PackageSearchMetadataContextInfo metadata, IVulnerableCapable vulnerableCapability, IDeprecationCapable deprecationCapable, EmbeddedResourcesCapability embeddedResources) + { + var recommenderVersion = metadata.RecommenderVersion ?? throw new ArgumentNullException(nameof(metadata.RecommenderVersion)); + + return new RecommendedPackageModel( + metadata.Identity!, + vulnerableCapability, + deprecationCapable, + embeddedResources, + recommenderVersion, + metadata.Title, + metadata.Description, + metadata.Authors, + metadata.ProjectUrl, + metadata.Tags?.Split(','), + metadata.OwnersList, + metadata.DependencySets, + metadata.Summary, + metadata.Published, + metadata.LicenseMetadata, + metadata.LicenseUrl, + metadata.RequireLicenseAcceptance, + metadata.IsListed, + metadata.PackageDetailsUrl, + metadata.DownloadCount, + metadata.ReadmeUrl, + metadata.IconUrl); + } + + private static RemotePackageModel CreateRemotePackageModel(PackageSearchMetadataContextInfo metadata, IVulnerableCapable vulnerableCapability, IDeprecationCapable deprecationCapable, EmbeddedResourcesCapability embeddedResources) + { + return new RemotePackageModel( + metadata.Identity!, + vulnerableCapability, + deprecationCapable, + embeddedResources, + metadata.Title, + metadata.Description, + metadata.Authors, + metadata.ProjectUrl, + metadata.Tags?.Split(','), + metadata.OwnersList, + metadata.DependencySets, + metadata.Summary, + metadata.Published, + metadata.LicenseMetadata, + metadata.LicenseUrl, + metadata.RequireLicenseAcceptance, + metadata.IsListed, + metadata.PackageDetailsUrl, + metadata.DownloadCount, + metadata.ReadmeUrl, + metadata.IconUrl); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RecommendedPackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RecommendedPackageModel.cs new file mode 100644 index 00000000000..36b6687a392 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RecommendedPackageModel.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using NuGet.Packaging; +using NuGet.Packaging.Core; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class RecommendedPackageModel : RemotePackageModel + { + public RecommendedPackageModel( + PackageIdentity identity, + IVulnerableCapable vulnerableCapability, + IDeprecationCapable deprecationCapability, + IEmbeddedResourcesCapable embeddedResources, + (string modelVersion, string vsixVersion) recommenderVersion, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + bool isListed, + Uri? packageDetailsUrl, + long? downloadCount, + Uri? readmeUrl, + Uri? iconUrl) + : base(identity, vulnerableCapability, deprecationCapability, embeddedResources, title, description, authors, projectUrl, tags, ownersList, packageDependencyGroups, summary, publishedDate, licenseMetadata, licenseUrl, requireLicenseAcceptance, isListed, packageDetailsUrl, downloadCount, readmeUrl, iconUrl) + { + RecommenderVersion = recommenderVersion; + } + + public (string modelVersion, string vsixVersion) RecommenderVersion { get; } + } +} + diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RemotePackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RemotePackageModel.cs new file mode 100644 index 00000000000..bec87bb87f1 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/RemotePackageModel.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class RemotePackageModel : PackageModel, IDeprecationCapable, IVulnerableCapable + { + private readonly IDeprecationCapable _deprecationCapability; + private readonly IVulnerableCapable _vulnerableCapability; + + public RemotePackageModel( + PackageIdentity identity, + IVulnerableCapable vulnerableCapability, + IDeprecationCapable deprecationCapability, + IEmbeddedResourcesCapable embeddedResources, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + bool isListed, + Uri? packageDetailsUrl, + long? downloadCount, + Uri? readmeUrl, + Uri? iconUrl) + : base(identity, embeddedResources, title, description, authors, projectUrl, tags, ownersList, packageDependencyGroups, summary, publishedDate, licenseMetadata, licenseUrl, requireLicenseAcceptance, iconUrl) + { + IsListed = isListed; + PackageDetailsUrl = packageDetailsUrl; + DownloadCount = downloadCount; + _deprecationCapability = deprecationCapability ?? throw new ArgumentNullException(nameof(deprecationCapability)); + _vulnerableCapability = vulnerableCapability ?? throw new ArgumentNullException(nameof(vulnerableCapability)); + ReadmeUrl = readmeUrl; + } + + public bool IsListed { get; } + public Uri? PackageDetailsUrl { get; } + public long? DownloadCount { get; } + public Uri? ReadmeUrl { get; } + + public bool IsDeprecated => _deprecationCapability.IsDeprecated; + + public PackageDeprecationReason PackageDeprecationReasons => _deprecationCapability.PackageDeprecationReasons; + + public AlternatePackageMetadataContextInfo? AlternatePackage => _deprecationCapability.AlternatePackage; + + public IReadOnlyList? Vulnerabilities => _vulnerableCapability.Vulnerabilities; + + public bool IsVulnerable => _vulnerableCapability.IsVulnerable; + + public PackageVulnerabilitySeverity VulnerabilityMaxSeverity => _vulnerableCapability.VulnerabilityMaxSeverity; + + public PackageDeprecationMetadataContextInfo? DeprecationMetadata => _deprecationCapability.DeprecationMetadata; + + public override async Task PopulateDataAsync(CancellationToken cancellationToken) + { + await _vulnerableCapability.PopulateDataAsync(cancellationToken); + await _deprecationCapability.PopulateDataAsync(cancellationToken); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/TransitivelyReferencedPackageModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/TransitivelyReferencedPackageModel.cs new file mode 100644 index 00000000000..9cbc507e663 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/TransitivelyReferencedPackageModel.cs @@ -0,0 +1,73 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Packaging; +using NuGet.Packaging.Core; +using NuGet.Protocol; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class TransitivelyReferencedPackageModel : PackageModel, IVulnerableCapable + { + private readonly IVulnerableCapable _vulnerableCapability; + + public TransitivelyReferencedPackageModel( + PackageIdentity identity, + IVulnerableCapable vulnerableCapability, + IEmbeddedResourcesCapable embeddedResources, + IReadOnlyCollection transitiveOrigins, + string? title, + string? description, + string? authors, + Uri? projectUrl, + string[]? tags, + IReadOnlyList? ownersList, + IReadOnlyCollection? packageDependencyGroups, + string? summary, + DateTimeOffset? publishedDate, + LicenseMetadata? licenseMetadata, + Uri? licenseUrl, + bool requireLicenseAcceptance, + string? reportAbuseUrl, + Uri? iconUrl) + : base(identity, + embeddedResources, + title, + description, + authors, + projectUrl, + tags, + ownersList, + packageDependencyGroups, + summary, + publishedDate, + licenseMetadata, + licenseUrl, + requireLicenseAcceptance, + iconUrl) + { + _vulnerableCapability = vulnerableCapability ?? throw new ArgumentNullException(nameof(vulnerableCapability)); + TransitiveOrigins = transitiveOrigins; + } + + public IReadOnlyCollection TransitiveOrigins { get; } + + public IReadOnlyList? Vulnerabilities => _vulnerableCapability.Vulnerabilities; + + public bool IsVulnerable => _vulnerableCapability.IsVulnerable; + + public PackageVulnerabilitySeverity VulnerabilityMaxSeverity => _vulnerableCapability.VulnerabilityMaxSeverity; + + public override async Task PopulateDataAsync(CancellationToken cancellationToken) + { + await _vulnerableCapability.PopulateDataAsync(cancellationToken); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableCapabilityBase.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableCapabilityBase.cs new file mode 100644 index 00000000000..432d467678e --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableCapabilityBase.cs @@ -0,0 +1,61 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NuGet.Protocol; +using NuGet.VisualStudio.Internal.Contracts; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal abstract class VulnerableCapabilityBase : IVulnerableCapable + { + private IReadOnlyList? _vulnerabilities; + + public IReadOnlyList? Vulnerabilities + { + get => _vulnerabilities; + protected set + { + List? sortedList = null; + if (value != null) + { + sortedList = [.. value]; + // Sort the list in descending order. + sortedList.Sort((b, a) => a.Severity.CompareTo(b.Severity)); + } + _vulnerabilities = sortedList; + } + } + + public bool IsVulnerable => Vulnerabilities?.Count > 0; + + public PackageVulnerabilitySeverity VulnerabilityMaxSeverity + { + get + { + if (!IsVulnerable || Vulnerabilities is null) + { + return PackageVulnerabilitySeverity.Unknown; + } + + // Vulnerabilities are ordered on set so the first element is always the highest severity + int severity = Vulnerabilities[0].Severity; + if (Enum.IsDefined(typeof(PackageVulnerabilitySeverity), severity)) + { + return (PackageVulnerabilitySeverity)severity; + } + else + { + return PackageVulnerabilitySeverity.Unknown; + } + } + } + + public abstract Task PopulateDataAsync(CancellationToken cancellationToken); + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableDatabaseCapability.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableDatabaseCapability.cs new file mode 100644 index 00000000000..d97bcc639fd --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerableDatabaseCapability.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Threading; +using System.Threading.Tasks; +using NuGet.PackageManagement.VisualStudio; +using NuGet.Packaging.Core; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class VulnerableDatabaseCapability : VulnerableCapabilityBase + { + private readonly IPackageVulnerabilityService _vulnerabilityService; + private readonly PackageIdentity _packageIdentity; + + public VulnerableDatabaseCapability(IPackageVulnerabilityService vulnerabilityService, PackageIdentity packageIdentity) + { + _vulnerabilityService = vulnerabilityService ?? throw new ArgumentNullException(nameof(vulnerabilityService)); + _packageIdentity = packageIdentity ?? throw new ArgumentNullException(nameof(packageIdentity)); + } + + public override async Task PopulateDataAsync(CancellationToken cancellationToken) + { + Vulnerabilities = await _vulnerabilityService.GetVulnerabilityInfoAsync(_packageIdentity, cancellationToken); + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerablePackageMetadataCapability.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerablePackageMetadataCapability.cs new file mode 100644 index 00000000000..ea7e96c3f31 --- /dev/null +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Models/Package/VulnerablePackageMetadataCapability.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +#nullable enable + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace NuGet.PackageManagement.UI.Models.Package +{ + internal class VulnerablePackageMetadataCapability : VulnerableCapabilityBase + { + private readonly IPackageMetadataRetrievalAdapter _packageMetadataRetrievalAdapter; + + public VulnerablePackageMetadataCapability(IPackageMetadataRetrievalAdapter packageMetadataRetrievalAdapter) + { + _packageMetadataRetrievalAdapter = packageMetadataRetrievalAdapter ?? throw new ArgumentNullException(nameof(packageMetadataRetrievalAdapter)); + } + + public async override Task PopulateDataAsync(CancellationToken cancellationToken) + { + var packageMetadata = await _packageMetadataRetrievalAdapter.GetPackageMetadataAsync(cancellationToken); + Vulnerabilities = packageMetadata.Vulnerabilities?.ToList() ?? []; + } + } +} diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/PackageItemLoader.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/PackageItemLoader.cs index e33247df808..ed087ac03cd 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/PackageItemLoader.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/PackageItemLoader.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,6 +12,7 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using NuGet.Common; +using NuGet.PackageManagement.UI.Models.Package; using NuGet.PackageManagement.UI.ViewModels; using NuGet.PackageManagement.VisualStudio; using NuGet.Protocol.Core.Types; @@ -42,6 +42,7 @@ internal class PackageItemLoader : IPackageItemLoader, IDisposable public IItemLoaderState State => _state; private IServiceBroker _serviceBroker; private INuGetPackageFileService _packageFileService; + private PackageModelFactory _packageModelFactory; public bool IsMultiSource => _packageSources.Count > 1; @@ -274,7 +275,7 @@ public IEnumerable GetCurrent() { if (packageLevel == PackageLevel.Transitive) { - UpdateTransitiveInfo(existingListItem, metadataContextInfo); + existingListItem.UpdateTransitiveInfo(metadataContextInfo); } existingListItem.UpdateInstalledPackagesVulnerabilities(metadataContextInfo.Identity); @@ -318,25 +319,16 @@ public IEnumerable GetCurrent() knownOwnerViewModels = LoadKnownOwnerViewModels(metadataContextInfo); } - var listItem = new PackageItemViewModel(_searchService, _packageVulnerabilityService) + _packageModelFactory ??= new PackageModelFactory(_searchService, _packageFileService, _packageVulnerabilityService, _includePrerelease, _packageSources); + PackageModel packageModel = _packageModelFactory.Create(metadataContextInfo, _itemFilter); + + var listItem = new PackageItemViewModel(_searchService, packageModel, _packageVulnerabilityService) { - Id = metadataContextInfo.Identity.Id, - Version = metadataContextInfo.Identity.Version, - IconUrl = metadataContextInfo.IconUrl, - Owner = metadataContextInfo.Owners, KnownOwnerViewModels = knownOwnerViewModels, - Author = metadataContextInfo.Authors, - DownloadCount = metadataContextInfo.DownloadCount, - Summary = metadataContextInfo.Summary, AllowedVersions = allowedVersions, VersionOverride = versionOverride, PrefixReserved = metadataContextInfo.PrefixReserved && !IsMultiSource, - Recommended = metadataContextInfo.IsRecommended, - RecommenderVersion = metadataContextInfo.RecommenderVersion, - Vulnerabilities = metadataContextInfo.Vulnerabilities, Sources = _packageSources, - PackagePath = metadataContextInfo.PackagePath, - PackageFileService = _packageFileService, IncludePrerelease = _includePrerelease, PackageLevel = packageLevel, AutoReferenced = autoReferenced, @@ -344,12 +336,14 @@ public IEnumerable GetCurrent() if (listItem.PackageLevel == PackageLevel.TopLevel) { - listItem.UpdatePackageStatus(_installedPackages); + listItem.UpdatePackageStatusAsync(_installedPackages) + .PostOnFailure(nameof(PackageItemLoader), nameof(GetCurrent)); } else { - UpdateTransitiveInfo(listItem, metadataContextInfo); - listItem.UpdateTransitivePackageStatus(metadataContextInfo.Identity.Version); + listItem.UpdateTransitiveInfo(metadataContextInfo); + listItem.UpdateTransitivePackageStatusAsync() + .PostOnFailure(nameof(PackageItemLoader), nameof(GetCurrent)); } listItemViewModels[packageId] = listItem; @@ -359,13 +353,6 @@ public IEnumerable GetCurrent() return listItemViewModels.Values.ToArray(); } - private static void UpdateTransitiveInfo(PackageItemViewModel listItem, PackageSearchMetadataContextInfo metadataContextInfo) - { - listItem.TransitiveInstalledVersions.Add(metadataContextInfo.Identity.Version); - listItem.TransitiveOrigins.AddRange(metadataContextInfo.TransitiveOrigins); - listItem.TransitiveToolTipMessage = string.Format(CultureInfo.CurrentCulture, Resources.PackageVersionWithTransitiveOrigins, string.Join(", ", listItem.TransitiveInstalledVersions), string.Join(", ", listItem.TransitiveOrigins)); - } - private static ImmutableList LoadKnownOwnerViewModels(PackageSearchMetadataContextInfo metadataContextInfo) { ImmutableList knownOwnerViewModels = null; diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageItemViewModel.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageItemViewModel.cs index b27af2a9170..f6004fa20bf 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageItemViewModel.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/ViewModels/PackageItemViewModel.cs @@ -18,6 +18,7 @@ using Microsoft.VisualStudio.Services.Common; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; +using NuGet.PackageManagement.UI.Models.Package; using NuGet.PackageManagement.UI.ViewModels; using NuGet.PackageManagement.VisualStudio; using NuGet.Packaging.Core; @@ -37,12 +38,18 @@ public sealed class PackageItemViewModel : INotifyPropertyChanged, ISelectableIt private readonly CancellationTokenSource _cancellationTokenSource; private readonly IPackageVulnerabilityService _vulnerabilityService; + private readonly PackageModel _packageModel; + private List _transitiveInstalledVersions; + private List _transitiveOrigins; - public PackageItemViewModel(INuGetSearchService searchService, IPackageVulnerabilityService vulnerabilityService = default) + public PackageItemViewModel(INuGetSearchService searchService, PackageModel packageModel, IPackageVulnerabilityService vulnerabilityService = default) { _cancellationTokenSource = new CancellationTokenSource(); _searchService = searchService; _vulnerabilityService = vulnerabilityService; + _packageModel = packageModel; + _transitiveInstalledVersions = []; + _transitiveOrigins = []; } // same URIs can reuse the bitmapImage that we've already used. @@ -56,9 +63,9 @@ public PackageItemViewModel(INuGetSearchService searchService, IPackageVulnerabi public event PropertyChangedEventHandler PropertyChanged; - public string Id { get; set; } + public string Id => _packageModel.Id; - public NuGetVersion Version { get; set; } + public NuGetVersion Version => _packageModel.Version; public VersionRange AllowedVersions { get; set; } @@ -70,22 +77,9 @@ public PackageItemViewModel(INuGetSearchService searchService, IPackageVulnerabi public ImmutableList KnownOwnerViewModels { get; internal set; } - public string Owner { get; internal set; } + public string Owner => string.Join(",", _packageModel.OwnersList ?? []); - private string _author; - public string Author - { - get - { - return _author; - } - set - { - _author = value; - OnPropertyChanged(nameof(Author)); - OnPropertyChanged(nameof(ByAuthor)); - } - } + public string Author => _packageModel.Authors; /// /// When a collection of is available, this property returns the @@ -119,32 +113,14 @@ private string ByOwner } } - public string ByAuthor - { - get - { - return !string.IsNullOrWhiteSpace(_author) ? string.Format(CultureInfo.CurrentCulture, Resx.Text_ByAuthor, _author) : null; - } - } + public string ByAuthor => !string.IsNullOrWhiteSpace(_packageModel.Authors) ? string.Format(CultureInfo.CurrentCulture, Resx.Text_ByAuthor, _packageModel.Authors) : null; /// /// Fallback to only when is null. /// - public string ByOwnerOrAuthor - { - get - { - return ByOwner ?? ByAuthor; - } - } + public string ByOwnerOrAuthor => ByOwner ?? ByAuthor; - public string VulnerableVersionsString - { - get - { - return string.Join(", ", VulnerableVersions.Keys); - } - } + public string VulnerableVersionsString => string.Join(", ", VulnerableVersions.Keys); private readonly Dictionary _vulnerableVersions = []; public Dictionary VulnerableVersions => _vulnerableVersions; @@ -241,34 +217,6 @@ public bool AutoReferenced } } - private List _transitiveInstalledVersions; - public List TransitiveInstalledVersions - { - get - { - if (_transitiveInstalledVersions == null) - { - _transitiveInstalledVersions = new(); - } - - return _transitiveInstalledVersions; - } - } - - private List _transitiveOrigins; - public List TransitiveOrigins - { - get - { - if (_transitiveOrigins == null) - { - _transitiveOrigins = new(); - } - - return _transitiveOrigins; - } - } - private string _installedVersionToolTip; public string InstalledVersionToolTip @@ -329,22 +277,9 @@ private bool VersionEquals(NuGetVersion v1, NuGetVersion v2) return v1.Equals(v2, VersionComparison.Default); } - private long? _downloadCount; - - public long? DownloadCount - { - get - { - return _downloadCount; - } - set - { - _downloadCount = value; - OnPropertyChanged(nameof(DownloadCount)); - } - } + public long? DownloadCount => (_packageModel as RemotePackageModel)?.DownloadCount; - public string Summary { get; set; } + public string Summary => _packageModel.Summary; private PackageStatus _status; public PackageStatus Status @@ -415,30 +350,9 @@ public bool IsUpdateAvailable } } - private bool _recommended; - public bool Recommended - { - get { return _recommended; } - set - { - if (_recommended != value) - { - _recommended = value; - OnPropertyChanged(nameof(Recommended)); - } - } - } + public bool Recommended => (_packageModel as RecommendedPackageModel) is not null; - private (string modelVersion, string vsixVersion)? _recommenderVersion; - public (string modelVersion, string vsixVersion)? RecommenderVersion - { - get { return _recommenderVersion; } - set - { - _recommenderVersion = value; - OnPropertyChanged(nameof(RecommenderVersion)); - } - } + public (string modelVersion, string vsixVersion)? RecommenderVersion => (_packageModel as RecommendedPackageModel)?.RecommenderVersion; private bool _prefixReserved; public bool PrefixReserved @@ -454,38 +368,22 @@ public bool PrefixReserved } } - private bool _isPackageDeprecated; - public bool IsPackageDeprecated - { - get { return _isPackageDeprecated; } - set - { - if (_isPackageDeprecated != value) - { - _isPackageDeprecated = value; - OnPropertyChanged(nameof(IsPackageDeprecated)); - OnPropertyChanged(nameof(IsPackageWithWarnings)); - } - } - } + public PackageDeprecationMetadataContextInfo DeprecationMetadata => (_packageModel as IDeprecationCapable)?.DeprecationMetadata; + public bool IsPackageDeprecated => (_packageModel as IDeprecationCapable)?.IsDeprecated ?? false; - public bool IsPackageVulnerable - { - get => VulnerabilityMaxSeverity > -1; - } + public bool IsPackageVulnerable => (_packageModel as IVulnerableCapable)?.IsVulnerable ?? false || VulnerableVersions.Count > 0; - private int _vulnerabilityMaxSeverity = -1; public int VulnerabilityMaxSeverity { - get { return _vulnerabilityMaxSeverity; } - set + get { - if (_vulnerabilityMaxSeverity != value) + if (VulnerableVersions.Count > 0) { - _vulnerabilityMaxSeverity = value; - OnPropertyChanged(nameof(VulnerabilityMaxSeverity)); - OnPropertyChanged(nameof(IsPackageVulnerable)); - OnPropertyChanged(nameof(IsPackageWithWarnings)); + return VulnerableVersions.Values.Max(); + } + else + { + return -1; } } } @@ -509,16 +407,7 @@ public bool IsPackageWithNetworkErrors } } - private Uri _iconUrl; - public Uri IconUrl - { - get { return _iconUrl; } - set - { - _iconUrl = value; - OnPropertyChanged(nameof(IconUrl)); - } - } + public Uri IconUrl => _packageModel.IconUrl; private IconBitmapStatus _bitmapStatus; @@ -633,21 +522,22 @@ public async Task> GetVersionsAsync( #pragma warning restore VSTHRD003 // Avoid awaiting foreign Tasks } - private PackageDeprecationMetadataContextInfo _deprecationMetadata; - public PackageDeprecationMetadataContextInfo DeprecationMetadata + public AlternatePackageMetadataContextInfo AlternatePackage => (_packageModel as IDeprecationCapable)?.AlternatePackage; + + public IEnumerable Vulnerabilities => (_packageModel as IVulnerableCapable)?.Vulnerabilities ?? []; + + public void UpdateTransitiveInfo(PackageSearchMetadataContextInfo metadataContextInfo) { - get => _deprecationMetadata; - set + if (metadataContextInfo.TransitiveOrigins == null) { - if (_deprecationMetadata != value) - { - _deprecationMetadata = value; - OnPropertyChanged(nameof(DeprecationMetadata)); - } + return; } - } - public IEnumerable Vulnerabilities { get; set; } + _transitiveInstalledVersions.Add(metadataContextInfo.Identity.Version); + _transitiveOrigins.AddRange(metadataContextInfo.TransitiveOrigins); + TransitiveToolTipMessage = string.Format(CultureInfo.CurrentCulture, Resources.PackageVersionWithTransitiveOrigins, string.Join(", ", _transitiveInstalledVersions), string.Join(", ", _transitiveOrigins)); + OnPropertyChanged(nameof(TransitiveToolTipMessage)); + } private (BitmapSource, IconBitmapStatus) GetInitialIconBitmapAndStatus() { @@ -711,7 +601,7 @@ private async Task FetchIconAsync() Assumes.NotNull(IconUrl); - using (Stream stream = await PackageFileService.GetPackageIconAsync(new PackageIdentity(Id, Version), CancellationToken.None)) + using (Stream stream = await _packageModel.GetIconAsync(CancellationToken.None)) { if (stream != null) { @@ -795,7 +685,7 @@ private static void AddToCache(string cacheKey, BitmapSource iconBitmapImage) BitmapImageCache.Set(cacheKey, iconBitmapImage, policy); } - private async System.Threading.Tasks.Task ReloadPackageVersionsAsync() + private async Task ReloadPackageVersionsAsync() { CancellationToken cancellationToken = _cancellationTokenSource.Token; try @@ -833,54 +723,31 @@ private async System.Threading.Tasks.Task ReloadPackageVersionsAsync() } } - private async Task ReloadTopLevelPackageMetadataAsync() + private async Task ReloadPackageMetadataAsync() { - CancellationToken cancellationToken = _cancellationTokenSource.Token; - try + await RunOperationAsync(async (cancellationToken) => { - var identity = new PackageIdentity(Id, Version); - - (PackageSearchMetadataContextInfo packageMetadata, PackageDeprecationMetadataContextInfo deprecationMetadata) = - await _searchService.GetPackageMetadataAsync(identity, Sources, IncludePrerelease, cancellationToken); - - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + await _packageModel.PopulateDataAsync(cancellationToken); cancellationToken.ThrowIfCancellationRequested(); - DeprecationMetadata = deprecationMetadata; - IsPackageDeprecated = deprecationMetadata != null; - - SetVulnerabilityMaxSeverity(identity.Version, packageMetadata?.Vulnerabilities?.FirstOrDefault()?.Severity ?? -1); - } - catch (OperationCanceledException) - { - // UI requested cancellation. - } - catch (TimeoutException) - { - // Our code throws a TimeoutException for HTTP timeouts - try + if (_packageModel is IDeprecationCapable deprecationCapable) { - await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - IsPackageWithNetworkErrors = true; + UpdateDeprecationInfo(deprecationCapable); } - catch (OperationCanceledException) + + if (_packageModel is IVulnerableCapable vulnerableCapable) { - // if cancellationToken cancelled before the above is scheduled on UI thread, don't log fault telemetry + UpdateVulnerabilityInfo(vulnerableCapable); } - } + }); } - private async Task ReloadTransitivePackageMetadataAsync() + private async Task RunOperationAsync(Func func) { CancellationToken cancellationToken = _cancellationTokenSource.Token; - try { - if (_vulnerabilityService != null) - { - var identity = new PackageIdentity(Id, Version); - await UpdatePackageMaxVulnerabilityAsync(identity, cancellationToken); - } + await func(cancellationToken); } catch (OperationCanceledException) { @@ -903,13 +770,15 @@ private async Task ReloadTransitivePackageMetadataAsync() private async Task UpdatePackageMaxVulnerabilityAsync(PackageIdentity packageIdentity, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - - // Use ShutdownToken to ensure the operation is canceled if it's still running when VS shuts down. - IEnumerable vulnerabilityInfoList = - await _vulnerabilityService.GetVulnerabilityInfoAsync(packageIdentity, VsShellUtilities.ShutdownToken); + await RunOperationAsync(async (cancellationToken) => + { + IVulnerableCapable vulnerabilityDatabaseCapability = new VulnerableDatabaseCapability(_vulnerabilityService, packageIdentity); + // Use ShutdownToken to ensure the operation is canceled if it's still running when VS shuts down. + await vulnerabilityDatabaseCapability.PopulateDataAsync(VsShellUtilities.ShutdownToken); + cancellationToken.ThrowIfCancellationRequested(); - SetVulnerabilityMaxSeverity(packageIdentity.Version, vulnerabilityInfoList?.FirstOrDefault()?.Severity ?? -1); + UpdateVulnerabilityInfo(vulnerabilityDatabaseCapability); + }); } private void SetVulnerabilityMaxSeverity(NuGetVersion version, int maxSeverity) @@ -918,16 +787,40 @@ private void SetVulnerabilityMaxSeverity(NuGetVersion version, int maxSeverity) { if (VulnerableVersions.TryAdd(version, maxSeverity)) { + OnPropertyChanged(nameof(VulnerabilityMaxSeverity)); OnPropertyChanged(nameof(VulnerableVersions)); OnPropertyChanged(nameof(VulnerableVersionsString)); } - VulnerabilityMaxSeverity = Math.Max(VulnerabilityMaxSeverity, maxSeverity); - OnPropertyChanged(nameof(Status)); } } + private void UpdateDeprecationInfo(IDeprecationCapable deprecationCapable) + { + if (deprecationCapable == null) + { + return; + } + + OnPropertyChanged(nameof(IsPackageDeprecated)); + OnPropertyChanged(nameof(DeprecationMetadata)); + OnPropertyChanged(nameof(AlternatePackage)); + } + + private void UpdateVulnerabilityInfo(IVulnerableCapable vulnerableCapable) + { + if (vulnerableCapable == null) + { + return; + } + + SetVulnerabilityMaxSeverity(Version, (int)vulnerableCapable.VulnerabilityMaxSeverity); + OnPropertyChanged(nameof(IsPackageVulnerable)); + OnPropertyChanged(nameof(IsPackageWithWarnings)); + OnPropertyChanged(nameof(Vulnerabilities)); + } + public void UpdateInstalledPackagesVulnerabilities(PackageIdentity packageIdentity) { CancellationToken cancellationToken = _cancellationTokenSource.Token; @@ -937,7 +830,7 @@ public void UpdateInstalledPackagesVulnerabilities(PackageIdentity packageIdenti .PostOnFailure(nameof(PackageItemViewModel), nameof(UpdatePackageMaxVulnerabilityAsync)); } - public void UpdatePackageStatus(IEnumerable installedPackages, bool clearCache = false) + public async Task UpdatePackageStatusAsync(IEnumerable installedPackages, bool clearCache = false) { // Get the maximum version installed in any target project/solution InstalledVersion = installedPackages @@ -949,27 +842,20 @@ public void UpdatePackageStatus(IEnumerable installedPack _searchService.ClearFromCache(Id, Sources, IncludePrerelease); } - NuGetUIThreadHelper.JoinableTaskFactory - .RunAsync(ReloadPackageVersionsAsync) - .PostOnFailure(nameof(PackageItemViewModel), nameof(ReloadPackageVersionsAsync)); - - NuGetUIThreadHelper.JoinableTaskFactory - .RunAsync(ReloadTopLevelPackageMetadataAsync) - .PostOnFailure(nameof(PackageItemViewModel), nameof(ReloadTopLevelPackageMetadataAsync)); + await ReloadPackageVersionsAsync(); + await ReloadPackageMetadataAsync(); OnPropertyChanged(nameof(Status)); } - public void UpdateTransitivePackageStatus(NuGetVersion installedVersion) + public async Task UpdateTransitivePackageStatusAsync() { - InstalledVersion = installedVersion ?? throw new ArgumentNullException(nameof(installedVersion)); + InstalledVersion = Version; // Transitive packages cannot be updated and can only be installed as top-level packages with their currently installed version. - LatestVersion = installedVersion; + LatestVersion = InstalledVersion; - NuGetUIThreadHelper.JoinableTaskFactory - .RunAsync(ReloadTransitivePackageMetadataAsync) - .PostOnFailure(nameof(PackageItemViewModel), nameof(ReloadTransitivePackageMetadataAsync)); + await ReloadPackageMetadataAsync(); OnPropertyChanged(nameof(Status)); } @@ -1003,8 +889,7 @@ private void OnPropertyChanged(string propertyName) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public string PackagePath { get; set; } - public INuGetPackageFileService PackageFileService { get; internal set; } + public string PackagePath => (_packageModel as LocalPackageModel)?.PackagePath; public override string ToString() { diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/InfiniteScrollList.xaml.cs b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/InfiniteScrollList.xaml.cs index 0f722ff7526..3ef8cc7801d 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/InfiniteScrollList.xaml.cs +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/InfiniteScrollList.xaml.cs @@ -604,7 +604,7 @@ private void ClearPackageList() _loadingStatusBar.ItemsLoaded = 0; } - public void UpdatePackageStatus(PackageCollectionItem[] installedPackages, bool clearCache = false) + public async Task UpdatePackageStatusAsync(PackageCollectionItem[] installedPackages, bool clearCache = false) { // in this case, we only need to update PackageStatus of // existing items in the package list @@ -612,11 +612,11 @@ public void UpdatePackageStatus(PackageCollectionItem[] installedPackages, bool { if (package.PackageLevel == PackageLevel.TopLevel) { - package.UpdatePackageStatus(installedPackages, clearCache); + await package.UpdatePackageStatusAsync(installedPackages, clearCache); } else { - package.UpdateTransitivePackageStatus(package.InstalledVersion); + await package.UpdateTransitivePackageStatusAsync(); } } } @@ -751,9 +751,7 @@ private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) var last = _scrollViewer.ViewportHeight + first; if (_scrollViewer.ViewportHeight > 0 && last >= packagesCount) { - NuGetUIThreadHelper.JoinableTaskFactory.RunAsync(() => - LoadItemsAsync(selectedPackageItem: null, token: CancellationToken.None) - ).PostOnFailure(nameof(InfiniteScrollList)); + _ = LoadItemsAsync(selectedPackageItem: null, token: CancellationToken.None); } } } diff --git a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageItemControl.xaml b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageItemControl.xaml index 2f8c4094901..50a12d4e7fe 100644 --- a/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageItemControl.xaml +++ b/src/nuget-client/src/NuGet.Clients/NuGet.PackageManagement.UI/Xamls/PackageItemControl.xaml @@ -256,17 +256,17 @@ - + - +