diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/BuildModelFactoryTests.cs b/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/BuildModelFactoryTests.cs index 0906695c73a..7420d9e661f 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/BuildModelFactoryTests.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed.Tests/BuildModelFactoryTests.cs @@ -23,13 +23,16 @@ public class BuildModelFactoryTests #region Standard test values private const string _testAzdoRepoUri = "https://dnceng@dev.azure.com/dnceng/internal/_git/dotnet-buildtest"; + private const string _normalizedTestAzdoRepoUri = "https://dev.azure.com/dnceng/internal/_git/dotnet-buildtest"; private const string _testBuildBranch = "foobranch"; private const string _testBuildCommit = "664996a16fa9228cfd7a55d767deb31f62a65f51"; private const string _testAzdoBuildId = "89999999"; - private const string _testInitialLocation = "As they say....Location Location Location!"; + private const string _testInitialLocation = "https://dnceng.visualstudio.com/project/_apis/build/builds/id/artifacts"; + private const string _normalizedTestInitialLocation = "https://dev.azure.com/dnceng/project/_apis/build/builds/id/artifacts"; private static readonly string[] _defaultManifestBuildData = new string[] { - $"InitialAssetsLocation={_testInitialLocation}" + $"InitialAssetsLocation={_testInitialLocation}", + $"AzureDevOpsRepository={_testAzdoRepoUri}" }; #endregion @@ -165,6 +168,8 @@ public void ManifestArtifactParsingTest() package.Attributes.Should().Contain("Id", "test-package-a"); package.Attributes.Should().Contain("Version", "1.0.0"); }); + + model.Identity.Attributes.Should().Contain("AzureDevOpsRepository", _normalizedTestAzdoRepoUri); } /// @@ -290,7 +295,7 @@ public void InitialLocationInformationAttributesAreAccepted(string attributeName _taskLoggingHelper.HasLoggedErrors.Should().BeFalse(); // Check that the build model has the initial assets location - model.Identity.Attributes.Should().Contain(attributeName, _testInitialLocation); + model.Identity.Attributes.Should().Contain(attributeName, _normalizedTestInitialLocation); } #endregion diff --git a/src/Microsoft.DotNet.Build.Tasks.Feed/src/BuildModelFactory.cs b/src/Microsoft.DotNet.Build.Tasks.Feed/src/BuildModelFactory.cs index f15f8442c27..e72224383c5 100644 --- a/src/Microsoft.DotNet.Build.Tasks.Feed/src/BuildModelFactory.cs +++ b/src/Microsoft.DotNet.Build.Tasks.Feed/src/BuildModelFactory.cs @@ -8,6 +8,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Text.RegularExpressions; using System.Xml.Linq; namespace Microsoft.DotNet.Build.Tasks.Feed @@ -71,6 +73,11 @@ public BuildModelFactory( private const string AssetsVirtualDir = "assets/"; + private static readonly string AzureDevOpsHostPattern = @"dev\.azure\.com\"; + + private readonly Regex LegacyRepositoryUriPattern = new Regex( + @"^https://(?[a-zA-Z0-9]+)\.visualstudio\.com/"); + /// /// Create a build manifest for packages, blobs, and associated signing information /// @@ -204,6 +211,9 @@ private BuildModel CreateModel( { _log.LogError("Missing 'location' property from ManifestBuildData"); } + + NormalizeUrisInBuildData(attributes); + BuildModel buildModel = new BuildModel( new BuildIdentity { @@ -241,5 +251,43 @@ private bool ManifestBuildDataHasLocationInformation(IDictionary { return attributes.ContainsKey("Location") || attributes.ContainsKey("InitialAssetsLocation"); } + + private void NormalizeUrisInBuildData(IDictionary attributes) + { + foreach(var attribute in attributes.ToList()) + { + attributes[attribute.Key] = NormalizeAzureDevOpsUrl(attribute.Value); + } + } + + /// + // If repoUri includes the user in the account we remove it from URIs like + // https://dnceng@dev.azure.com/dnceng/internal/_git/repo + // If the URL host is of the form "dnceng.visualstudio.com" like + // https://dnceng.visualstudio.com/internal/_git/repo we replace it to "dev.azure.com/dnceng" + // for consistency + /// + /// The original url + /// Transformed url + private string NormalizeAzureDevOpsUrl(string repoUri) + { + if (Uri.TryCreate(repoUri, UriKind.Absolute, out Uri parsedUri)) + { + if (!string.IsNullOrEmpty(parsedUri.UserInfo)) + { + repoUri = repoUri.Replace($"{parsedUri.UserInfo}@", string.Empty); + } + + Match m = LegacyRepositoryUriPattern.Match(repoUri); + + if (m.Success) + { + string replacementUri = $"{Regex.Unescape(AzureDevOpsHostPattern)}/{m.Groups["account"].Value}"; + repoUri = repoUri.Replace(parsedUri.Host, replacementUri); + } + } + + return repoUri; + } } }