diff --git a/eng/Publishing.props b/eng/Publishing.props index 388deaf16..28ae49196 100644 --- a/eng/Publishing.props +++ b/eng/Publishing.props @@ -1,9 +1,4 @@ - - - - 3 - Sdk @@ -135,33 +130,7 @@ true - - - + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8c8f28170..54b752893 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -243,17 +243,17 @@ - + https://github.com/dotnet/arcade - 1c8e12b71c28f5dc6626b529b08abefdbb7ca6e8 + ace00d8719b8d1fdfd0cc05f71bb9af216338d27 - + https://github.com/dotnet/arcade - 1c8e12b71c28f5dc6626b529b08abefdbb7ca6e8 + ace00d8719b8d1fdfd0cc05f71bb9af216338d27 - + https://github.com/dotnet/arcade - 1c8e12b71c28f5dc6626b529b08abefdbb7ca6e8 + ace00d8719b8d1fdfd0cc05f71bb9af216338d27 https://github.com/dotnet/arcade-services @@ -263,14 +263,14 @@ https://github.com/dotnet/arcade-services 0e9abfee048404d9b994fc64235b42216ce68dad - + https://github.com/dotnet/arcade - 1c8e12b71c28f5dc6626b529b08abefdbb7ca6e8 + ace00d8719b8d1fdfd0cc05f71bb9af216338d27 - + https://github.com/dotnet/arcade - 1c8e12b71c28f5dc6626b529b08abefdbb7ca6e8 + ace00d8719b8d1fdfd0cc05f71bb9af216338d27 diff --git a/eng/Versions.props b/eng/Versions.props index a85bce4eb..79981d4f5 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -39,7 +39,7 @@ - 9.0.0-beta.24162.2 + 9.0.0-beta.24165.6 diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml index 5f5413556..d72e4ea6d 100644 --- a/eng/common/templates-official/job/publish-build-assets.yml +++ b/eng/common/templates-official/job/publish-build-assets.yml @@ -98,14 +98,16 @@ jobs: inputs: targetType: inline script: | - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId) - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)" - Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild) + New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force + $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt" + Add-Content -Path $filePath -Value $(BARBuildId) + Add-Content -Path $filePath -Value "$(DefaultChannels)" + Add-Content -Path $filePath -Value $(IsStableBuild) - task: 1ES.PublishBuildArtifacts@1 displayName: Publish ReleaseConfigs Artifact inputs: - PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs.txt' + PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs' PublishLocation: Container ArtifactName: ReleaseConfigs diff --git a/global.json b/global.json index 91a4c8e9a..1d8d54a51 100644 --- a/global.json +++ b/global.json @@ -11,7 +11,7 @@ "cmake": "latest" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24162.2", - "Microsoft.DotNet.CMake.Sdk": "9.0.0-beta.24162.2" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24165.6", + "Microsoft.DotNet.CMake.Sdk": "9.0.0-beta.24165.6" } } diff --git a/src/SourceBuild/patches/arcade/0001-Enable-publishing-to-build-storage.patch b/src/SourceBuild/patches/arcade/0001-Enable-publishing-to-build-storage.patch deleted file mode 100644 index cc366f799..000000000 --- a/src/SourceBuild/patches/arcade/0001-Enable-publishing-to-build-storage.patch +++ /dev/null @@ -1,1433 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Nikola Milosavljevic -Date: Thu, 14 Mar 2024 00:40:08 +0000 -Subject: [PATCH] Enable publishing to build storage - -Backport: https://github.com/dotnet/arcade/pull/14559 ---- - .../DependencyFlowOnboardingWithoutArcade.md | 8 +- - .../tools/Build.proj | 19 ++- - .../tools/Publish.proj | 40 +++++- - .../tools/SourceBuild/AfterSourceBuild.proj | 27 ++-- - .../SourceBuild/SourceBuildArcade.targets | 6 +- - .../SourceBuildArcadeBuild.targets | 3 + - .../SourceBuildArcadePublish.targets | 19 --- - .../SourceBuildArcadeTools.targets | 1 + - .../GeneralTests.cs | 5 +- - .../PublishArtifactsInManifestTests.cs | 1 + - ...ctsTests.cs => PushToBuildStorageTests.cs} | 43 +++--- - .../Microsoft.DotNet.Build.Tasks.Feed.csproj | 49 ++++++- - .../Microsoft.DotNet.Build.Tasks.Feed.targets | 2 +- - .../src/PublishArtifactsInManifestBase.cs | 19 +-- - ...vOpsArtifacts.cs => PushToBuildStorage.cs} | 117 ++++++++++++++-- - .../src/common/AzureStorageUtils.cs | 132 ++++++++++++++++++ - .../src/common/GeneralUtils.cs | 132 +----------------- - .../Directory.Build.props | 10 -- - .../lib/src/Automation/NupkgInfo.cs | 34 ++++- - .../lib/src/Automation/NupkgInfoFactory.cs | 63 ++++++++- - ...Microsoft.DotNet.VersionTools.Tasks.csproj | 5 + - .../VersionTrimmingOperationTests.cs | 3 +- - .../Microsoft.DotNet.VersionTools.Cli.csproj | 5 + - 23 files changed, 492 insertions(+), 251 deletions(-) - rename src/Microsoft.DotNet.Build.Tasks.Feed.Tests/{PushToAzureDevOpsArtifactsTests.cs => PushToBuildStorageTests.cs} (94%) - rename src/Microsoft.DotNet.Build.Tasks.Feed/src/{PushToAzureDevOpsArtifacts.cs => PushToBuildStorage.cs} (64%) - delete mode 100644 src/Microsoft.DotNet.VersionTools/Directory.Build.props - -diff --git a/Documentation/DependencyFlowOnboardingWithoutArcade.md b/Documentation/DependencyFlowOnboardingWithoutArcade.md -index a45c707e..d5cd5105 100644 ---- a/Documentation/DependencyFlowOnboardingWithoutArcade.md -+++ b/Documentation/DependencyFlowOnboardingWithoutArcade.md -@@ -4,14 +4,14 @@ - - Dependency flow is the method by which .NET repos consume other product repo's assets. In order to participate in dependency flow, a repo must produce a [manifest](#generate-a-manifest) of what assets / packages that repo produces and then publish that manifest to the Build Asset Registry (B.A.R.). This document is intended to provide guidance for repos which are not using the Arcade Sdk but still need to participate in dependency flow. The end goal is that the repo is able to produce a manifest and publish that to B.A.R. - --To do so, a repo follows almost the same process as [normal arcade publishing](DependencyFlowOnboarding.md). The repo directly uses the [`PushToAzureDevOpsArtifacts`](https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToAzureDevOpsArtifacts.cs) task in the [Microsoft.DotNet.Build.Tasks.Feed](https://github.com/dotnet/arcade/tree/master/src/Microsoft.DotNet.Build.Tasks.Feed) package (available from the dotnet-eng feed - `https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json`). This task uploads the artifacts to build storage and generates a manifest describing the build. The manifest is the used to publish the new build to the build asset registry (BAR). -+To do so, a repo follows almost the same process as [normal arcade publishing](DependencyFlowOnboarding.md). The repo directly uses the [`PushToBuildStorage`](https://github.com/dotnet/arcade/blob/master/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToBuildStorage.cs) task in the [Microsoft.DotNet.Build.Tasks.Feed](https://github.com/dotnet/arcade/tree/master/src/Microsoft.DotNet.Build.Tasks.Feed) package (available from the dotnet-eng feed - `https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json`). This task uploads the artifacts to build storage and generates a manifest describing the build. The manifest is the used to publish the new build to the build asset registry (BAR). - - ## Creating a manifest - --To create a manifest, use the PushToAzureDevOpsArtifacts, providing the feed that packages pushed to. The below target performs this task. -+To create a manifest, use the PushToBuildStorage, providing the feed that packages pushed to. The below target performs this task. - - ``` -- -+ - - - -@@ -52,7 +52,7 @@ To create a manifest, use the PushToAzureDevOpsArtifacts, providing the feed tha - - - -- -+ -+ -+ <_VmrBuild Condition="'$(DotNetBuildFromSourceFlavor)' == 'Product' or '$(DotNetBuildOrchestrator)' == 'true'">true -+ <_InnerRepoBuild Condition="'$(ArcadeInnerBuildFromSource)' == 'true' or '$(DotNetBuildPhase)' == 'InnerRepo'">true -+ <_ShouldRunPublish Condition="'$(_InnerRepoBuild)' == 'true' and '$(_VmrBuild)' == 'true'">true -+ <_ShouldRunPublish Condition="'$(_InnerRepoBuild)' != 'true' and '$(_VmrBuild)' != 'true'">true -+ -+ -+ - -+ Condition="'$(Publish)' == 'true' and '$(_ShouldRunPublish)' == 'true'"/> - - - -diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/Publish.proj b/src/Microsoft.DotNet.Arcade.Sdk/tools/Publish.proj -index 9fa35734..dc83aac9 100644 ---- a/src/Microsoft.DotNet.Arcade.Sdk/tools/Publish.proj -+++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/Publish.proj -@@ -21,6 +21,16 @@ - --> - - -+ -+ -+ -+ true -+ -+ - - - -@@ -41,9 +51,10 @@ - - - -+ - true -+ ('$(DotNetBuildSourceOnly)' != 'true' or -+ ('$(ArcadeInnerBuildFromSource)' == 'true' and '$(DotNetBuildFromSourceFlavor)' != 'Product'))">true - - $(OS) - -@@ -89,6 +100,15 @@ - - - -+ -+ -+ -+ -+ true -+ Symbols/%(Filename)%(Extension) -+ -+ -+ - - - -- -- -+ -+ -+ -+ -+ - NonShipping=true - DotNetReleaseShipping=true - -@@ -163,7 +186,7 @@ - -- -+ IsReleaseOnlyPackageVersion="$(IsReleaseOnlyPackageVersion)" -+ PushToLocalStorage="$(PushToLocalStorage)" -+ AssetsLocalStorageDir="$(SourceBuiltAssetsDir)" -+ ShippingPackagesLocalStorageDir="$(SourceBuiltShippingPackagesDir)" -+ NonShippingPackagesLocalStorageDir="$(SourceBuiltNonShippingPackagesDir)" -+ AssetManifestsLocalStorageDir="$(SourceBuiltAssetManifestsDir)" /> - - -- -+ - - - -@@ -11,22 +11,23 @@ - - - -+ -+ -+ -+ -+ -+ -+ - -- -- -- -+ PackSourceBuildIntermediateNupkgs" /> - - - -diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcade.targets b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcade.targets -index 1fcd705b..41817c3a 100644 ---- a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcade.targets -+++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcade.targets -@@ -46,6 +46,7 @@ - $([MSBuild]::NormalizeDirectory('$(CurrentRepoSourceBuildSourceDir)', 'artifacts')) - $([MSBuild]::NormalizeDirectory('$(CurrentRepoSourceBuildArtifactsDir)', 'log', '$(Configuration)')) - $([MSBuild]::NormalizeDirectory('$(CurrentRepoSourceBuildArtifactsDir)', 'packages', '$(Configuration)')) -+ $([MSBuild]::NormalizeDirectory('$(CurrentRepoSourceBuildArtifactsPackagesDir)', 'NonShipping')) - - source-build-int-nupkg-cache - -@@ -69,6 +70,7 @@ - <_DefaultNetFrameworkFilter>netstandard2.0%3bnetstandard2.1%3bnetcoreapp2.1%3bnetcoreapp3.1%3bnet5.0%3bnet6.0%3bnet7.0%3bnet8.0%3bnet9.0 - $(_DefaultNetFrameworkFilter) - $(ArtifactsDir)RepoManifest.xml -+ GetCategorizedIntermediateNupkgContents - - - -@@ -159,12 +161,12 @@ - --> - -+ DependsOnTargets="$(CreateRepoSymbolsArchiveDependsOn)"> - - $(CurrentRepoSourceBuildArtifactsDir) - - $(RepoRoot) -- $(CurrentRepoSourceBuildArtifactsPackagesDir) -+ $(CurrentRepoSourceBuildArtifactsNonShippingPackagesDir) - $([MSBuild]::EnsureTrailingSlash('$(PackageOutputPath)')) - $([MSBuild]::NormalizePath('$(SymbolsArchiveLocation)', 'symbols.lst')) - Symbols. -diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeBuild.targets b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeBuild.targets -index a4343829..0c38335c 100644 ---- a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeBuild.targets -+++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeBuild.targets -@@ -80,6 +80,9 @@ - $(InnerBuildArgs) /p:DotNetPackageVersionPropsPath="$(DotNetPackageVersionPropsPath)" - - $(InnerBuildArgs) /p:FullAssemblySigningSupported=$(FullAssemblySigningSupported) -+ -+ -+ $(InnerBuildArgs) /p:DotNetPublishUsingPipelines=true - - - -diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadePublish.targets b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadePublish.targets -index 2b023696..fa81a575 100644 ---- a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadePublish.targets -+++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadePublish.targets -@@ -3,23 +3,4 @@ - - - -- -- -- -- -- - -diff --git a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeTools.targets b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeTools.targets -index ed8e7dfe..180b2ae6 100644 ---- a/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeTools.targets -+++ b/src/Microsoft.DotNet.Arcade.Sdk/tools/SourceBuild/SourceBuildArcadeTools.targets -@@ -9,6 +9,7 @@ - - - -+ - - - -@@ -25,10 +28,11 @@ - IsImplicitlyDefined="false" - PrivateAssets="all" - ExcludeAssets="runtime" -- VersionOverride="2.0.3" /> -+ VersionOverride="2.0.3" -+ Condition="'$(DotNetBuildSourceOnly)' != 'true'" /> - - -- -+ - -@@ -41,7 +45,7 @@ - - - -- -+ - - - -@@ -49,4 +53,35 @@ - - - -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ - -diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/build/Microsoft.DotNet.Build.Tasks.Feed.targets b/src/Microsoft.DotNet.Build.Tasks.Feed/build/Microsoft.DotNet.Build.Tasks.Feed.targets -index 7110f878..c58f5a4d 100644 ---- a/src/Microsoft.DotNet.Build.Tasks.Feed/build/Microsoft.DotNet.Build.Tasks.Feed.targets -+++ b/src/Microsoft.DotNet.Build.Tasks.Feed/build/Microsoft.DotNet.Build.Tasks.Feed.targets -@@ -47,7 +47,7 @@ - <_MicrosoftDotNetBuildTasksFeedTaskDir Condition="'$(MSBuildRuntimeType)' == 'Core'">$(MSBuildThisFileDirectory)../tools/net9.0/ - - -- -+ - - - -diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs -index ccd417c0..1de07e42 100644 ---- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs -+++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PublishArtifactsInManifestBase.cs -@@ -29,6 +29,7 @@ - using Newtonsoft.Json; - using NuGet.Versioning; - using static Microsoft.DotNet.Build.Tasks.Feed.GeneralUtils; -+using static Microsoft.DotNet.Build.CloudTestTasks.AzureStorageUtils; - using MsBuildUtils = Microsoft.Build.Utilities; - - namespace Microsoft.DotNet.Build.Tasks.Feed -@@ -1376,10 +1377,10 @@ public void SplitArtifactsInCategories(BuildModel buildModel) - ) - { - // Using these callbacks we can mock up functionality when testing. -- CompareLocalPackageToFeedPackageCallBack ??= GeneralUtils.CompareLocalPackageToFeedPackage; -+ CompareLocalPackageToFeedPackageCallBack ??= CompareLocalPackageToFeedPackage; - RunProcessAndGetOutputsCallBack ??= GeneralUtils.RunProcessAndGetOutputsAsync; - ProcessExecutionResult nugetResult = null; -- var packageStatus = GeneralUtils.PackageFeedStatus.Unknown; -+ var packageStatus = PackageFeedStatus.Unknown; - - try - { -@@ -1395,7 +1396,7 @@ public void SplitArtifactsInCategories(BuildModel buildModel) - if (nugetResult.ExitCode == 0) - { - // We have just pushed this package so we know it exists and is identical to our local copy -- packageStatus = GeneralUtils.PackageFeedStatus.ExistsAndIdenticalToLocal; -+ packageStatus = PackageFeedStatus.ExistsAndIdenticalToLocal; - break; - } - -@@ -1406,12 +1407,12 @@ public void SplitArtifactsInCategories(BuildModel buildModel) - - switch (packageStatus) - { -- case GeneralUtils.PackageFeedStatus.ExistsAndIdenticalToLocal: -+ case PackageFeedStatus.ExistsAndIdenticalToLocal: - { - Log.LogMessage(MessageImportance.Normal, $"Package '{localPackageLocation}' already exists on '{feedConfig.TargetURL}' but has the same content; skipping push"); - break; - } -- case GeneralUtils.PackageFeedStatus.ExistsAndDifferent: -+ case PackageFeedStatus.ExistsAndDifferent: - { - Log.LogError($"Package '{localPackageLocation}' already exists on '{feedConfig.TargetURL}' with different content."); - break; -@@ -1425,11 +1426,11 @@ public void SplitArtifactsInCategories(BuildModel buildModel) - } - } - } -- while (packageStatus != GeneralUtils.PackageFeedStatus.ExistsAndIdenticalToLocal && // Success -- packageStatus != GeneralUtils.PackageFeedStatus.ExistsAndDifferent && // Give up: Non-retriable error -+ while (packageStatus != PackageFeedStatus.ExistsAndIdenticalToLocal && // Success -+ packageStatus != PackageFeedStatus.ExistsAndDifferent && // Give up: Non-retriable error - attemptIndex <= MaxRetryCount); // Give up: Too many retries - -- if (packageStatus != GeneralUtils.PackageFeedStatus.ExistsAndIdenticalToLocal) -+ if (packageStatus != PackageFeedStatus.ExistsAndIdenticalToLocal) - { - Log.LogError($"Failed to publish package '{id}@{version}' to '{feedConfig.TargetURL}' after {MaxRetryCount} attempts. (Final status: {packageStatus})"); - } -@@ -1443,7 +1444,7 @@ public void SplitArtifactsInCategories(BuildModel buildModel) - Log.LogError($"Unexpected exception pushing package '{id}@{version}': {e.Message}"); - } - -- if (packageStatus != GeneralUtils.PackageFeedStatus.ExistsAndIdenticalToLocal && nugetResult?.ExitCode != 0) -+ if (packageStatus != PackageFeedStatus.ExistsAndIdenticalToLocal && nugetResult?.ExitCode != 0) - { - Log.LogError($"Output from nuget.exe: {Environment.NewLine}StdOut:{Environment.NewLine}{nugetResult.StandardOut}{Environment.NewLine}StdErr:{Environment.NewLine}{nugetResult.StandardError}"); - } -diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToAzureDevOpsArtifacts.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToBuildStorage.cs -similarity index 64% -rename from src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToAzureDevOpsArtifacts.cs -rename to src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToBuildStorage.cs -index 2c1825c2..05c853c7 100644 ---- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToAzureDevOpsArtifacts.cs -+++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/PushToBuildStorage.cs -@@ -3,6 +3,7 @@ - - using Microsoft.Arcade.Common; - using Microsoft.Build.Framework; -+using Microsoft.Build.Utilities; - using Microsoft.DotNet.VersionTools.Automation; - using Microsoft.DotNet.VersionTools.BuildManifest.Model; - using Microsoft.Extensions.DependencyInjection; -@@ -14,7 +15,7 @@ - - namespace Microsoft.DotNet.Build.Tasks.Feed - { -- public class PushToAzureDevOpsArtifacts : MSBuildTaskBase -+ public class PushToBuildStorage : MSBuildTaskBase - { - [Required] - public ITaskItem[] ItemsToPush { get; set; } -@@ -57,12 +58,29 @@ public class PushToAzureDevOpsArtifacts : MSBuildTaskBase - - public bool IsReleaseOnlyPackageVersion { get; set; } - -+ public string AssetsLocalStorageDir { get; set; } -+ -+ public string ShippingPackagesLocalStorageDir { get; set; } -+ -+ public string NonShippingPackagesLocalStorageDir { get; set; } -+ -+ public string AssetManifestsLocalStorageDir { get; set; } -+ -+ public bool PushToLocalStorage { get; set; } -+ - /// - /// Which version should the build manifest be tagged with. - /// By default he latest version is used. - /// - public string PublishingVersion { get; set; } - -+ public enum ItemType -+ { -+ AssetManifest = 0, -+ PackageArtifact, -+ BlobArtifact -+ } -+ - public override void ConfigureServices(IServiceCollection collection) - { - collection.TryAddSingleton(); -@@ -83,7 +101,22 @@ public override void ConfigureServices(IServiceCollection collection) - { - try - { -- Log.LogMessage(MessageImportance.High, "Performing push to Azure DevOps artifacts storage."); -+ if (PushToLocalStorage) -+ { -+ if (string.IsNullOrEmpty(AssetsLocalStorageDir) || -+ string.IsNullOrEmpty(ShippingPackagesLocalStorageDir) || -+ string.IsNullOrEmpty(NonShippingPackagesLocalStorageDir) || -+ string.IsNullOrEmpty(AssetManifestsLocalStorageDir)) -+ { -+ throw new Exception($"AssetsLocalStorageDir, ShippingPackagesLocalStorageDir, NonShippingPackagesLocalStorageDir and AssetManifestsLocalStorageDir need to be specified if PublishToLocalStorage is set to true"); -+ } -+ -+ Log.LogMessage(MessageImportance.High, "Performing push to local artifacts storage."); -+ } -+ else -+ { -+ Log.LogMessage(MessageImportance.High, "Performing push to Azure DevOps artifacts storage."); -+ } - - if (!string.IsNullOrWhiteSpace(AssetsTemporaryDirectory)) - { -@@ -116,8 +149,7 @@ public override void ConfigureServices(IServiceCollection collection) - continue; - } - -- Log.LogMessage(MessageImportance.High, -- $"##vso[artifact.upload containerfolder=BlobArtifacts;artifactname=BlobArtifacts]{blobItem.ItemSpec}"); -+ PushToLocalStorageOrAzDO(ItemType.BlobArtifact, blobItem); - } - } - else -@@ -159,8 +191,7 @@ public override void ConfigureServices(IServiceCollection collection) - continue; - } - -- Log.LogMessage(MessageImportance.High, -- $"##vso[artifact.upload containerfolder=PackageArtifacts;artifactname=PackageArtifacts]{packagePath.ItemSpec}"); -+ PushToLocalStorageOrAzDO(ItemType.PackageArtifact, packagePath); - } - - foreach (var blobItem in blobItems) -@@ -171,8 +202,7 @@ public override void ConfigureServices(IServiceCollection collection) - continue; - } - -- Log.LogMessage(MessageImportance.High, -- $"##vso[artifact.upload containerfolder=BlobArtifacts;artifactname=BlobArtifacts]{blobItem.ItemSpec}"); -+ PushToLocalStorageOrAzDO(ItemType.BlobArtifact, blobItem); - } - - packageArtifacts = packageItems.Select(packageArtifactModelFactory.CreatePackageArtifactModel); -@@ -206,8 +236,7 @@ public override void ConfigureServices(IServiceCollection collection) - IsReleaseOnlyPackageVersion, - signingInformationModel: signingInformationModel); - -- Log.LogMessage(MessageImportance.High, -- $"##vso[artifact.upload containerfolder=AssetManifests;artifactname=AssetManifests]{AssetManifestPath}"); -+ PushToLocalStorageOrAzDO(ItemType.AssetManifest, new TaskItem(AssetManifestPath)); - } - } - catch (Exception e) -@@ -217,5 +246,73 @@ public override void ConfigureServices(IServiceCollection collection) - - return !Log.HasLoggedErrors; - } -+ -+ private void PushToLocalStorageOrAzDO(ItemType itemType, ITaskItem item) -+ { -+ string path = item.ItemSpec; -+ -+ if (PushToLocalStorage) -+ { -+ string filename = Path.GetFileName(path); -+ switch (itemType) -+ { -+ case ItemType.AssetManifest: -+ Directory.CreateDirectory(AssetManifestsLocalStorageDir); -+ File.Copy(path, Path.Combine(AssetManifestsLocalStorageDir, filename), true); -+ break; -+ -+ case ItemType.PackageArtifact: -+ if (string.Equals(item.GetMetadata("IsShipping"), "true", StringComparison.OrdinalIgnoreCase)) -+ { -+ Directory.CreateDirectory(ShippingPackagesLocalStorageDir); -+ File.Copy(path, Path.Combine(ShippingPackagesLocalStorageDir, filename), true); -+ } -+ else -+ { -+ Directory.CreateDirectory(NonShippingPackagesLocalStorageDir); -+ File.Copy(path, Path.Combine(NonShippingPackagesLocalStorageDir, filename), true); -+ } -+ break; -+ -+ case ItemType.BlobArtifact: -+ string relativeBlobPath = item.GetMetadata("RelativeBlobPath"); -+ string destinationPath = Path.Combine( -+ AssetsLocalStorageDir, -+ string.IsNullOrEmpty(relativeBlobPath) ? filename : relativeBlobPath); -+ -+ Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)); -+ File.Copy(path, destinationPath, true); -+ break; -+ -+ default: -+ throw new ArgumentOutOfRangeException(nameof(itemType)); -+ } -+ } -+ else -+ { -+ // Push to AzDO artifacts storage -+ -+ switch (itemType) -+ { -+ case ItemType.AssetManifest: -+ Log.LogMessage(MessageImportance.High, -+ $"##vso[artifact.upload containerfolder=AssetManifests;artifactname=AssetManifests]{path}"); -+ break; -+ -+ case ItemType.PackageArtifact: -+ Log.LogMessage(MessageImportance.High, -+ $"##vso[artifact.upload containerfolder=PackageArtifacts;artifactname=PackageArtifacts]{path}"); -+ break; -+ -+ case ItemType.BlobArtifact: -+ Log.LogMessage(MessageImportance.High, -+ $"##vso[artifact.upload containerfolder=BlobArtifacts;artifactname=BlobArtifacts]{path}"); -+ break; -+ -+ default: -+ throw new ArgumentOutOfRangeException(nameof(itemType)); -+ } -+ } -+ } - } - } -diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AzureStorageUtils.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AzureStorageUtils.cs -index b126fa4c..c805c6b4 100644 ---- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AzureStorageUtils.cs -+++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/AzureStorageUtils.cs -@@ -8,6 +8,8 @@ - using Azure.Storage.Blobs.Specialized; - using Azure.Storage.Sas; - using Microsoft.Arcade.Common; -+using Microsoft.Build.Framework; -+using MsBuildUtils = Microsoft.Build.Utilities; - using System; - using System.Collections.Generic; - using System.IO; -@@ -33,6 +35,17 @@ public class AzureStorageUtils - {".svg", "no-cache"} - }; - -+ /// -+ /// Enum describing the states of a given package on a feed -+ /// -+ public enum PackageFeedStatus -+ { -+ DoesNotExist, -+ ExistsAndIdenticalToLocal, -+ ExistsAndDifferent, -+ Unknown -+ } -+ - // Save the credential so we can sign SAS tokens - private readonly StorageSharedKeyCredential _credential; - -@@ -168,5 +181,124 @@ public static BlobHttpHeaders GetBlobHeadersByExtension(string filePath) - - return headers; - } -+ -+ /// -+ /// Determine whether a local package is the same as a package on an AzDO feed. -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// Open a stream to the local file and an http request to the package. There are a couple possibilities: -+ /// - The returned headers include a content MD5 header, in which case we can -+ /// hash the local file and just compare those. -+ /// - No content MD5 hash, and the streams must be compared in blocks. This is a bit trickier to do efficiently, -+ /// since we do not necessarily want to read all bytes if we can help it. Thus, we should compare in blocks. However, -+ /// the streams make no guarantee that they will return a full block each time when read operations are performed, so we -+ /// must be sure to only compare the minimum number of bytes returned. -+ /// -+ public static async Task CompareLocalPackageToFeedPackage( -+ string localPackageFullPath, -+ string packageContentUrl, -+ HttpClient client, -+ MsBuildUtils.TaskLoggingHelper log) -+ { -+ return await CompareLocalPackageToFeedPackage( -+ localPackageFullPath, -+ packageContentUrl, -+ client, -+ log, -+ GeneralUtils.CreateDefaultRetryHandler()); -+ } -+ -+ /// -+ /// Determine whether a local package is the same as a package on an AzDO feed. -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// -+ /// Open a stream to the local file and an http request to the package. There are a couple possibilities: -+ /// - The returned headers include a content MD5 header, in which case we can -+ /// hash the local file and just compare those. -+ /// - No content MD5 hash, and the streams must be compared in blocks. This is a bit trickier to do efficiently, -+ /// since we do not necessarily want to read all bytes if we can help it. Thus, we should compare in blocks. However, -+ /// the streams make no guarantee that they will return a full block each time when read operations are performed, so we -+ /// must be sure to only compare the minimum number of bytes returned. -+ /// -+ public static async Task CompareLocalPackageToFeedPackage( -+ string localPackageFullPath, -+ string packageContentUrl, -+ HttpClient client, -+ MsBuildUtils.TaskLoggingHelper log, -+ IRetryHandler retryHandler) -+ { -+ log.LogMessage($"Getting package content from {packageContentUrl} and comparing to {localPackageFullPath}"); -+ -+ PackageFeedStatus result = PackageFeedStatus.Unknown; -+ -+ bool success = await retryHandler.RunAsync(async attempt => -+ { -+ try -+ { -+ using (Stream localFileStream = File.OpenRead(localPackageFullPath)) -+ using (HttpResponseMessage response = await client.GetAsync(packageContentUrl)) -+ { -+ response.EnsureSuccessStatusCode(); -+ -+ // Check the headers for content length and md5 -+ bool md5HeaderAvailable = response.Headers.TryGetValues("Content-MD5", out var md5); -+ bool lengthHeaderAvailable = response.Headers.TryGetValues("Content-Length", out var contentLength); -+ -+ if (lengthHeaderAvailable && long.Parse(contentLength.Single()) != localFileStream.Length) -+ { -+ log.LogMessage(MessageImportance.Low, $"Package '{localPackageFullPath}' has different length than remote package '{packageContentUrl}'."); -+ result = PackageFeedStatus.ExistsAndDifferent; -+ return true; -+ } -+ -+ if (md5HeaderAvailable) -+ { -+ var localMD5 = AzureStorageUtils.CalculateMD5(localPackageFullPath); -+ if (!localMD5.Equals(md5.Single(), StringComparison.OrdinalIgnoreCase)) -+ { -+ log.LogMessage(MessageImportance.Low, $"Package '{localPackageFullPath}' has different MD5 hash than remote package '{packageContentUrl}'."); -+ } -+ -+ result = PackageFeedStatus.ExistsAndDifferent; -+ return true; -+ } -+ -+ const int BufferSize = 64 * 1024; -+ -+ // Otherwise, compare the streams -+ var remoteStream = await response.Content.ReadAsStreamAsync(); -+ var streamsMatch = await GeneralUtils.CompareStreamsAsync(localFileStream, remoteStream, BufferSize); -+ result = streamsMatch ? PackageFeedStatus.ExistsAndIdenticalToLocal : PackageFeedStatus.ExistsAndDifferent; -+ return true; -+ } -+ } -+ // String based comparison because the status code isn't exposed in HttpRequestException -+ // see here: https://github.com/dotnet/runtime/issues/23648 -+ catch (Exception e) when (e is HttpRequestException || e is TaskCanceledException) -+ { -+ if (e.Message.Contains("404 (Not Found)")) -+ { -+ result = PackageFeedStatus.DoesNotExist; -+ return true; -+ } -+ -+ // Retry this. Could be an http client timeout, 500, etc. -+ return false; -+ } -+ }); -+ -+ return result; -+ } - } - } -diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/GeneralUtils.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/GeneralUtils.cs -index ab8b35f7..a361aa45 100644 ---- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/GeneralUtils.cs -+++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/common/GeneralUtils.cs -@@ -4,7 +4,9 @@ - using Microsoft.Arcade.Common; - using Microsoft.Build.Framework; - using Microsoft.Build.Utilities; -+#if !DOTNET_BUILD_SOURCE_ONLY - using Microsoft.DotNet.Build.CloudTestTasks; -+#endif - using System; - using System.Collections.Generic; - using System.Diagnostics; -@@ -31,17 +33,6 @@ public static ExponentialRetry CreateDefaultRetryHandler() - MaxAttempts = 5 - }; - -- /// -- /// Enum describing the states of a given package on a feed -- /// -- public enum PackageFeedStatus -- { -- DoesNotExist, -- ExistsAndIdenticalToLocal, -- ExistsAndDifferent, -- Unknown -- } -- - /// - /// Compare a local stream and a remote stream for quality - /// -@@ -119,125 +110,6 @@ public static async Task CompareStreamsAsync(Stream localFileStream, Strea - while (true); - } - -- /// -- /// Determine whether a local package is the same as a package on an AzDO feed. -- /// -- /// -- /// -- /// -- /// -- /// -- /// -- /// Open a stream to the local file and an http request to the package. There are a couple possibilities: -- /// - The returned headers include a content MD5 header, in which case we can -- /// hash the local file and just compare those. -- /// - No content MD5 hash, and the streams must be compared in blocks. This is a bit trickier to do efficiently, -- /// since we do not necessarily want to read all bytes if we can help it. Thus, we should compare in blocks. However, -- /// the streams make no guarantee that they will return a full block each time when read operations are performed, so we -- /// must be sure to only compare the minimum number of bytes returned. -- /// -- public static async Task CompareLocalPackageToFeedPackage( -- string localPackageFullPath, -- string packageContentUrl, -- HttpClient client, -- TaskLoggingHelper log) -- { -- return await CompareLocalPackageToFeedPackage( -- localPackageFullPath, -- packageContentUrl, -- client, -- log, -- CreateDefaultRetryHandler()); -- } -- -- /// -- /// Determine whether a local package is the same as a package on an AzDO feed. -- /// -- /// -- /// -- /// -- /// -- /// -- /// -- /// -- /// Open a stream to the local file and an http request to the package. There are a couple possibilities: -- /// - The returned headers include a content MD5 header, in which case we can -- /// hash the local file and just compare those. -- /// - No content MD5 hash, and the streams must be compared in blocks. This is a bit trickier to do efficiently, -- /// since we do not necessarily want to read all bytes if we can help it. Thus, we should compare in blocks. However, -- /// the streams make no guarantee that they will return a full block each time when read operations are performed, so we -- /// must be sure to only compare the minimum number of bytes returned. -- /// -- public static async Task CompareLocalPackageToFeedPackage( -- string localPackageFullPath, -- string packageContentUrl, -- HttpClient client, -- TaskLoggingHelper log, -- IRetryHandler retryHandler) -- { -- log.LogMessage($"Getting package content from {packageContentUrl} and comparing to {localPackageFullPath}"); -- -- PackageFeedStatus result = PackageFeedStatus.Unknown; -- -- bool success = await retryHandler.RunAsync(async attempt => -- { -- try -- { -- using (Stream localFileStream = File.OpenRead(localPackageFullPath)) -- using (HttpResponseMessage response = await client.GetAsync(packageContentUrl)) -- { -- response.EnsureSuccessStatusCode(); -- -- // Check the headers for content length and md5 -- bool md5HeaderAvailable = response.Headers.TryGetValues("Content-MD5", out var md5); -- bool lengthHeaderAvailable = response.Headers.TryGetValues("Content-Length", out var contentLength); -- -- if (lengthHeaderAvailable && long.Parse(contentLength.Single()) != localFileStream.Length) -- { -- log.LogMessage(MessageImportance.Low, $"Package '{localPackageFullPath}' has different length than remote package '{packageContentUrl}'."); -- result = PackageFeedStatus.ExistsAndDifferent; -- return true; -- } -- -- if (md5HeaderAvailable) -- { -- var localMD5 = AzureStorageUtils.CalculateMD5(localPackageFullPath); -- if (!localMD5.Equals(md5.Single(), StringComparison.OrdinalIgnoreCase)) -- { -- log.LogMessage(MessageImportance.Low, $"Package '{localPackageFullPath}' has different MD5 hash than remote package '{packageContentUrl}'."); -- } -- -- result = PackageFeedStatus.ExistsAndDifferent; -- return true; -- } -- -- const int BufferSize = 64 * 1024; -- -- // Otherwise, compare the streams -- var remoteStream = await response.Content.ReadAsStreamAsync(); -- var streamsMatch = await GeneralUtils.CompareStreamsAsync(localFileStream, remoteStream, BufferSize); -- result = streamsMatch ? PackageFeedStatus.ExistsAndIdenticalToLocal : PackageFeedStatus.ExistsAndDifferent; -- return true; -- } -- } -- // String based comparison because the status code isn't exposed in HttpRequestException -- // see here: https://github.com/dotnet/runtime/issues/23648 -- catch (Exception e) when (e is HttpRequestException || e is TaskCanceledException) -- { -- if (e.Message.Contains("404 (Not Found)")) -- { -- result = PackageFeedStatus.DoesNotExist; -- return true; -- } -- -- // Retry this. Could be an http client timeout, 500, etc. -- return false; -- } -- }); -- -- return result; -- } -- - /// - /// Determine whether the feed is public or private. - /// -diff --git a/src/Microsoft.DotNet.VersionTools/Directory.Build.props b/src/Microsoft.DotNet.VersionTools/Directory.Build.props -deleted file mode 100644 -index 2dc829c3..00000000 ---- a/src/Microsoft.DotNet.VersionTools/Directory.Build.props -+++ /dev/null -@@ -1,10 +0,0 @@ -- -- -- -- -- -- -- true -- -- -- -diff --git a/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfo.cs b/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfo.cs -index c3646c58..4e4aed0a 100644 ---- a/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfo.cs -+++ b/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfo.cs -@@ -1,7 +1,7 @@ - // Licensed to the .NET Foundation under one or more agreements. - // The .NET Foundation licenses this file to you under the MIT license. - --using NuGet.Packaging.Core; -+using System; - - namespace Microsoft.DotNet.VersionTools.Automation - { -@@ -10,14 +10,40 @@ public class NupkgInfo - public NupkgInfo(PackageIdentity identity) - { - Id = identity.Id; -- Version = identity.Version.ToString(); -- Prerelease = identity.Version.Release; -+ Version = identity.Version; - } - - public string Id { get; } - public string Version { get; } -- public string Prerelease { get; } -+ public string Prerelease { get { throw new NotImplementedException();} } - - public static bool IsSymbolPackagePath(string path) => path.EndsWith(".symbols.nupkg"); - } -+ -+ public class PackageIdentity -+ { -+ private readonly string _id; -+ private readonly string _version; -+ -+ public PackageIdentity(string id, string version) -+ { -+ if (id == null) -+ { -+ throw new ArgumentNullException(nameof(id)); -+ } -+ -+ _id = id; -+ _version = version; -+ } -+ -+ public string Id -+ { -+ get { return _id; } -+ } -+ -+ public string Version -+ { -+ get { return _version; } -+ } -+ } - } -diff --git a/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfoFactory.cs b/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfoFactory.cs -index ddf8dd99..eab28d72 100644 ---- a/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfoFactory.cs -+++ b/src/Microsoft.DotNet.VersionTools/lib/src/Automation/NupkgInfoFactory.cs -@@ -1,8 +1,12 @@ - // Licensed to the .NET Foundation under one or more agreements. - // The .NET Foundation licenses this file to you under the MIT license. - --using NuGet.Packaging; --using NuGet.Packaging.Core; -+using System; -+using System.Globalization; -+using System.IO; -+using System.IO.Compression; -+using System.Linq; -+using System.Xml.Linq; - - namespace Microsoft.DotNet.VersionTools.Automation - { -@@ -22,10 +26,59 @@ public NupkgInfoFactory(IPackageArchiveReaderFactory packageArchiveReaderFactory - - public NupkgInfo CreateNupkgInfo(string path) - { -- using PackageArchiveReader archiveReader = _packageArchiveReaderFactory.CreatePackageArchiveReader(path); -- PackageIdentity identity = archiveReader.GetIdentity(); -+ if (path == null) -+ { -+ throw new ArgumentNullException(nameof(path)); -+ } - -- return new NupkgInfo(identity); -+ Stream stream = null; -+ Stream ZipReadStream = null; -+ ZipArchive _zipArchive; -+ try -+ { -+ stream = File.OpenRead(path); -+ ZipReadStream = stream; -+ _zipArchive = new ZipArchive(stream, ZipArchiveMode.Read); -+ string nuspecFile = Path.GetTempFileName(); -+ foreach (ZipArchiveEntry entry in _zipArchive.Entries) -+ { -+ if (entry.Name.EndsWith(".nuspec")) -+ { -+ Directory.CreateDirectory(Path.GetDirectoryName(nuspecFile)); -+ entry.ExtractToFile(nuspecFile, true); -+ } -+ } -+ -+ if (!File.Exists(nuspecFile)) -+ { -+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "Did not extract nuspec file from package: {0}", path)); -+ } -+ -+ PackageIdentity identity = GetIdentity(nuspecFile); -+ return new NupkgInfo(identity); -+ } -+ catch (Exception ex) -+ { -+ stream?.Dispose(); -+ throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture, "Invalid package", path), ex); -+ } -+ } -+ -+ private static PackageIdentity GetIdentity(string nuspecFile) -+ { -+ if (nuspecFile == null) -+ { -+ throw new ArgumentNullException(nameof(nuspecFile)); -+ } -+ -+ XDocument doc = XDocument.Load(nuspecFile, LoadOptions.PreserveWhitespace); -+ XElement metadataElement = GetSingleElement(doc.Root, "metadata"); -+ return new PackageIdentity(GetSingleElement(metadataElement, "id").Value, GetSingleElement(metadataElement, "version").Value); -+ } -+ -+ private static XElement GetSingleElement(XElement el, string name) -+ { -+ return el.Descendants().First(c => c.Name.LocalName.ToString() == name); - } - } - } -diff --git a/src/Microsoft.DotNet.VersionTools/tasks/Microsoft.DotNet.VersionTools.Tasks.csproj b/src/Microsoft.DotNet.VersionTools/tasks/Microsoft.DotNet.VersionTools.Tasks.csproj -index 2013e9da..72277ea5 100644 ---- a/src/Microsoft.DotNet.VersionTools/tasks/Microsoft.DotNet.VersionTools.Tasks.csproj -+++ b/src/Microsoft.DotNet.VersionTools/tasks/Microsoft.DotNet.VersionTools.Tasks.csproj -@@ -7,6 +7,11 @@ - MSBuildSdk - - -+ -+ -+ true -+ -+ - - - -diff --git a/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli.Tests/VersionTrimmingOperationTests.cs b/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli.Tests/VersionTrimmingOperationTests.cs -index 7a1b83c1..fb1be051 100644 ---- a/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli.Tests/VersionTrimmingOperationTests.cs -+++ b/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli.Tests/VersionTrimmingOperationTests.cs -@@ -4,7 +4,6 @@ - using FluentAssertions; - using Microsoft.DotNet.VersionTools.Automation; - using Moq; --using NuGet.Packaging.Core; - using NuGet.Versioning; - using System.IO; - using System; -@@ -22,7 +21,7 @@ public void TestRemoveVersionFromFileNames() - { - var nupkgInfoFactory = new Mock(); - nupkgInfoFactory.Setup(m => m.CreateNupkgInfo(It.IsAny())) -- .Returns(new NupkgInfo(new PackageIdentity("id", new NuGetVersion("8.0.0-dev")))); -+ .Returns(new NupkgInfo(new PackageIdentity("id", "8.0.0-dev"))); - - var fileProxy = new Mock(); - fileProxy.Setup(m => m.Move(It.IsAny(), It.IsAny())).Verifiable(); -diff --git a/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli/Microsoft.DotNet.VersionTools.Cli.csproj b/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli/Microsoft.DotNet.VersionTools.Cli.csproj -index 3f28b805..a2f9360e 100644 ---- a/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli/Microsoft.DotNet.VersionTools.Cli.csproj -+++ b/src/Microsoft.DotNet.VersionTools/tools/Microsoft.DotNet.VersionTools.Cli/Microsoft.DotNet.VersionTools.Cli.csproj -@@ -9,6 +9,11 @@ - This package provides a CLI interface to the Microsoft.DotNet.VersionTools library. - - -+ -+ -+ true -+ -+ - - -