From ac86bfa0302c9fad15f4ebdb3c1daba64fcba5fb Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 21 Feb 2019 16:02:19 +0100 Subject: [PATCH 1/9] Add support for Bitbucket Enterprise --- .../GetSourceLinkUrl.cs | 81 ++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs index 9a6e2031..6cd5cdf8 100644 --- a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs +++ b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs @@ -16,7 +16,84 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask protected override string HostsItemGroupName => "SourceLinkBitbucketGitHost"; protected override string ProviderDisplayName => "Bitbucket.Git"; - protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, ITaskItem hostItem) - => UriUtilities.Combine(UriUtilities.Combine(contentUri.ToString(), relativeUrl), "raw/" + revisionId + "/*"); + private const string IsEnterpriseEditionMetadataName = "EnterpriseEdition"; + private const string VersionMetadataName = "Version"; + private const string VersionWithNewUrlFormat = "4.7"; + + protected override bool SupportsImplicitHost => false; + + protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, + ITaskItem hostItem) + { + + var isEnterpriseEditionFlagAvailable = + bool.TryParse(hostItem.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition); + + if (isEnterpriseEditionFlagAvailable && isEnterpriseEdition) + { + return BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); + } + else + { + return BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId); + } + } + + private string BuildSourceLinkUrlForEnterpriseEdition(Uri contentUri, string relativeUrl, string revisionId, + ITaskItem hostItem) + { + var bitbucketEnterpriseVersion = GetBitbucketEnterpriseVersion(hostItem); + + var splits = relativeUrl.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + var relativeUrlForBitbucketEnterprise = + GetRelativeUrlForBitbucketEnterprise(splits[0], splits[1], revisionId, bitbucketEnterpriseVersion); + + var result = UriUtilities.Combine(contentUri.ToString(), relativeUrlForBitbucketEnterprise); + + return result; + } + + private Version GetBitbucketEnterpriseVersion(ITaskItem hostItem) + { + var bitbucketEnterpriseVersionAsString = hostItem.GetMetadata(VersionMetadataName); + Version bitbucketEnterpriseVersion; + if (!string.IsNullOrEmpty(bitbucketEnterpriseVersionAsString)) + { + if (!Version.TryParse(bitbucketEnterpriseVersionAsString, out bitbucketEnterpriseVersion)) + { + Log.LogError(CommonResources.ItemOfItemGroupMustSpecifyMetadata, hostItem.ItemSpec, + HostsItemGroupName, VersionMetadataName); + + return null; + } + } + else + { + bitbucketEnterpriseVersion = Version.Parse(VersionWithNewUrlFormat); + } + + return bitbucketEnterpriseVersion; + } + + private static string BuildSourceLinkUrlForCloudEdition(Uri contentUri, string relativeUrl, string revisionId) + { + return UriUtilities.Combine(UriUtilities.Combine(contentUri.ToString(), relativeUrl), + "raw/" + revisionId + "/*"); + } + + private static string GetRelativeUrlForBitbucketEnterprise(string projectName, string repositoryName, string commitId, Version bitbucketVersion) + { + string relativeUrl; + if (bitbucketVersion >= Version.Parse(VersionWithNewUrlFormat)) + { + relativeUrl = $"projects/{projectName}/repos/{repositoryName}/raw/*?at={commitId}"; + } + else + { + relativeUrl = $"projects/{projectName}/repos/{repositoryName}/browse/*?at={commitId}&raw"; + } + + return relativeUrl; + } } } From 87100ebaa9239b29e7ebf8cd731c8193ef45980b Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 21 Feb 2019 16:03:00 +0100 Subject: [PATCH 2/9] Update unit tests for Bitbucket to cover Enterprise edition --- .../GetSourceLinkUrlTests.cs | 138 +++++++++++++++++- 1 file changed, 134 insertions(+), 4 deletions(-) diff --git a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs index b0bc0eff..097f323c 100644 --- a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs +++ b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs @@ -8,6 +8,11 @@ namespace Microsoft.SourceLink.Bitbucket.Git.UnitTests { public class GetSourceLinkUrlTests { + private const string ExpectedUrlForCloudEdition = + "https://domain.com/x/y/a/b/raw/0123456789abcdefABCDEF000000000000000000/*"; + private const string ExpectedUrlForEnterpriseEditionOldVersion = "https://bitbucket.domain.com/projects/a/repos/b/browse/*?at=0123456789abcdefABCDEF000000000000000000&raw"; + private const string ExpectedUrlForEnterpriseEditionNewVersion = "https://bitbucket.domain.com/projects/a/repos/b/raw/*?at=0123456789abcdefABCDEF000000000000000000"; + [Fact] public void EmptyHosts() { @@ -32,24 +37,149 @@ public void EmptyHosts() [InlineData("", "/")] [InlineData("/", "")] [InlineData("/", "/")] - public void BuildSourceLinkUrl(string s1, string s2) + public void BuildSourceLinkUrl_BitbucketCloud(string s1, string s2) { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "false"); var engine = new MockEngine(); - var task = new GetSourceLinkUrl() { BuildEngine = engine, SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://subdomain.mybitbucket.org:100/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), Hosts = new[] { - new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y" + s2)), + new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y" + s2), isEnterpriseEditionSetting), + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForCloudEdition, task.SourceLinkUrl); + Assert.True(result); + } + + [Fact] + public void BuildSourceLinkUrl_EmptyMetadata_UseCloudEditionAsDefault() + { + var engine = new MockEngine(); + var task = new GetSourceLinkUrl + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://subdomain.mybitbucket.org:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y")) + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForCloudEdition, task.SourceLinkUrl); + Assert.True(result); + } + + [Fact] + public void BuildSourceLinkUrl_MetadataWithEnterpriseEditionButWithoutVersion_UseNewVersionAsDefauld() + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com"), isEnterpriseEditionSetting), + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForEnterpriseEditionNewVersion, task.SourceLinkUrl); + Assert.True(result); + } + + [Theory] + [InlineData("", "", "4.4")] + [InlineData("", "/", "4.4")] + [InlineData("/", "", "4.4")] + [InlineData("/", "/", "4.4")] + [InlineData("", "", "4.6")] + [InlineData("", "/", "4.6")] + [InlineData("/", "", "4.6")] + [InlineData("/", "/", "4.6")] + public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersion(string s1, string s2, string bitbucketVersion) + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var version = KVP("Version", bitbucketVersion); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com" + s2), isEnterpriseEditionSetting, version), } }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); - AssertEx.AreEqual("https://domain.com/x/y/a/b/raw/0123456789abcdefABCDEF000000000000000000/*", task.SourceLinkUrl); + AssertEx.AreEqual(ExpectedUrlForEnterpriseEditionOldVersion, task.SourceLinkUrl); Assert.True(result); } + + [Theory] + [InlineData("", "", "")] + [InlineData("", "", "4.7")] + [InlineData("", "/", "4.7")] + [InlineData("/", "", "4.7")] + [InlineData("/", "/", "4.7")] + [InlineData("", "", "5.6")] + [InlineData("", "/", "5.6")] + [InlineData("/", "", "5.6")] + [InlineData("/", "/", "5.6")] + public void BuildSourceLinkUrl_BitbucketEnterpriseNewVersion(string s1, string s2, string bitbucketVersion) + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var version = KVP("Version", bitbucketVersion); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com" + s2), isEnterpriseEditionSetting, version), + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForEnterpriseEditionNewVersion, task.SourceLinkUrl); + Assert.True(result); + } + + [Fact] + public void BuildSourceLinkUrl_IncorrectVersionForEnterpriseEdition_ERROR() + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var version = KVP("Version", "incorrect_version"); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com"), isEnterpriseEditionSetting, version), + } + }; + + bool result = task.Execute(); + + AssertEx.AssertEqualToleratingWhitespaceDifferences( + "ERROR : " + string.Format(CommonResources.ItemOfItemGroupMustSpecifyMetadata, "domain.com", "SourceLinkBitbucketGitHost", "Version"), engine.Log); + Assert.False(result); + } } } From c79589aa5819277961a91e70e81f052dac882c7f Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 21 Feb 2019 16:03:30 +0100 Subject: [PATCH 3/9] Update Bitbucket integration tests to cover Enterprise edition --- .../Microsoft.SourceLink.Bitbucket.Git.props | 10 - .../Microsoft.SourceLink.Bitbucket.Git.props | 8 - .../BitbucketGitTests.cs | 236 +++++++++++++++++- 3 files changed, 233 insertions(+), 21 deletions(-) delete mode 100644 src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props delete mode 100644 src/SourceLink.Bitbucket.Git/buildCrossTargeting/Microsoft.SourceLink.Bitbucket.Git.props diff --git a/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props b/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props deleted file mode 100644 index f502d5ff..00000000 --- a/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props +++ /dev/null @@ -1,10 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - diff --git a/src/SourceLink.Bitbucket.Git/buildCrossTargeting/Microsoft.SourceLink.Bitbucket.Git.props b/src/SourceLink.Bitbucket.Git/buildCrossTargeting/Microsoft.SourceLink.Bitbucket.Git.props deleted file mode 100644 index 3be77360..00000000 --- a/src/SourceLink.Bitbucket.Git/buildCrossTargeting/Microsoft.SourceLink.Bitbucket.Git.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - diff --git a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs index b1f85036..6ba1f6af 100644 --- a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs +++ b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs @@ -13,7 +13,7 @@ public BitBucketGitTests() } [ConditionalFact(typeof(DotNetSdkAvailable))] - public void FullValidation_Https() + public void FullValidation_CloudHttps() { // Test non-ascii characters and escapes in the URL. // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. @@ -28,6 +28,9 @@ public void FullValidation_Https() true + + + ", customTargets: "", targets: new[] @@ -67,7 +70,120 @@ public void FullValidation_Https() } [ConditionalFact(typeof(DotNetSdkAvailable))] - public void FullValidation_Ssh() + public void FullValidation_EnterpriseNewHttps() + { + // Test non-ascii characters and escapes in the URL. + // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. + var repoUrl = "https://bitbucket.domain.com/test-org/test-%72epo\u1234%24%2572%2F"; + var repoName = "test-repo\u1234%24%2572%2F"; + + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); + var commitSha = repo.Head.Tip.Sha; + + VerifyValues( + customProps: @" + + true + + + + +", + customTargets: "", + targets: new[] + { + "Build", "Pack" + }, + expressions: new[] + { + "@(SourceRoot)", + "@(SourceRoot->'%(SourceLinkUrl)')", + "$(SourceLink)", + "$(PrivateRepositoryUrl)", + "$(RepositoryUrl)" + }, + expectedResults: new[] + { + ProjectSourceRoot, + $"https://bitbucket.domain.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}", + s_relativeSourceLinkJsonPath, + $"https://bitbucket.domain.com/test-org/{repoName}", + $"https://bitbucket.domain.com/test-org/{repoName}" + }); + + AssertEx.AreEqual( + $@"{{""documents"":{{""{ProjectSourceRoot.Replace(@"\", @"\\")}*"":""https://bitbucket.domain.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}""}}}}", + File.ReadAllText(Path.Combine(ProjectDir.Path, s_relativeSourceLinkJsonPath))); + + TestUtilities.ValidateAssemblyInformationalVersion( + Path.Combine(ProjectDir.Path, s_relativeOutputFilePath), + "1.0.0+" + commitSha); + + TestUtilities.ValidateNuSpecRepository( + Path.Combine(ProjectDir.Path, s_relativePackagePath), + type: "git", + commit: commitSha, + url: $"https://bitbucket.domain.com/test-org/{repoName}"); + } + [ConditionalFact(typeof(DotNetSdkAvailable))] + public void FullValidation_EnterpriseOldHttps() + { + // Test non-ascii characters and escapes in the URL. + // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. + var repoUrl = "https://bitbucket.domain.com/test-org/test-%72epo\u1234%24%2572%2F"; + var repoName = "test-repo\u1234%24%2572%2F"; + + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); + var commitSha = repo.Head.Tip.Sha; + + VerifyValues( + customProps: @" + + true + + + + +", + customTargets: "", + targets: new[] + { + "Build", "Pack" + }, + expressions: new[] + { + "@(SourceRoot)", + "@(SourceRoot->'%(SourceLinkUrl)')", + "$(SourceLink)", + "$(PrivateRepositoryUrl)", + "$(RepositoryUrl)" + }, + expectedResults: new[] + { + ProjectSourceRoot, + $"https://bitbucket.domain.com/projects/test-org/repos/{repoName}/browse/*?at={commitSha}&raw", + s_relativeSourceLinkJsonPath, + $"https://bitbucket.domain.com/test-org/{repoName}", + $"https://bitbucket.domain.com/test-org/{repoName}" + }); + + AssertEx.AreEqual( + $@"{{""documents"":{{""{ProjectSourceRoot.Replace(@"\", @"\\")}*"":""https://bitbucket.domain.com/projects/test-org/repos/{repoName}/browse/*?at={commitSha}&raw""}}}}", + File.ReadAllText(Path.Combine(ProjectDir.Path, s_relativeSourceLinkJsonPath))); + + TestUtilities.ValidateAssemblyInformationalVersion( + Path.Combine(ProjectDir.Path, s_relativeOutputFilePath), + "1.0.0+" + commitSha); + + TestUtilities.ValidateNuSpecRepository( + Path.Combine(ProjectDir.Path, s_relativePackagePath), + type: "git", + commit: commitSha, + url: $"https://bitbucket.domain.com/test-org/{repoName}"); + } + + [ConditionalFact(typeof(DotNetSdkAvailable))] + public void FullValidation_CloudSsh() { // Test non-ascii characters and escapes in the URL. // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. @@ -83,7 +199,7 @@ public void FullValidation_Ssh() true - + ", customTargets: "", @@ -122,5 +238,119 @@ public void FullValidation_Ssh() commit: commitSha, url: $"https://噸.com/test-org/{repoName}"); } + + [ConditionalFact(typeof(DotNetSdkAvailable))] + public void FullValidation_EnterpriseOldSsh() + { + // Test non-ascii characters and escapes in the URL. + // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. + var repoUrl = "test-user@噸.com:test-org/test-%72epo\u1234%24%2572%2F"; + var repoName = "test-repo\u1234%24%2572%2F"; + + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); + var commitSha = repo.Head.Tip.Sha; + + VerifyValues( + customProps: @" + + true + + + + +", + customTargets: "", + targets: new[] + { + "Build", "Pack" + }, + expressions: new[] + { + "@(SourceRoot)", + "@(SourceRoot->'%(SourceLinkUrl)')", + "$(SourceLink)", + "$(PrivateRepositoryUrl)", + "$(RepositoryUrl)" + }, + expectedResults: new[] + { + ProjectSourceRoot, + $"https://噸.com/projects/test-org/repos/{repoName}/browse/*?at={commitSha}&raw", + s_relativeSourceLinkJsonPath, + $"https://噸.com/test-org/{repoName}", + $"https://噸.com/test-org/{repoName}" + }); + + AssertEx.AreEqual( + $@"{{""documents"":{{""{ProjectSourceRoot.Replace(@"\", @"\\")}*"":""https://噸.com/projects/test-org/repos/{repoName}/browse/*?at={commitSha}&raw""}}}}", + File.ReadAllText(Path.Combine(ProjectDir.Path, s_relativeSourceLinkJsonPath))); + + TestUtilities.ValidateAssemblyInformationalVersion( + Path.Combine(ProjectDir.Path, s_relativeOutputFilePath), + "1.0.0+" + commitSha); + + TestUtilities.ValidateNuSpecRepository( + Path.Combine(ProjectDir.Path, s_relativePackagePath), + type: "git", + commit: commitSha, + url: $"https://噸.com/test-org/{repoName}"); + } + + [ConditionalFact(typeof(DotNetSdkAvailable))] + public void FullValidation_EnterpriseNewSsh() + { + // Test non-ascii characters and escapes in the URL. + // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. + var repoUrl = "test-user@噸.com:test-org/test-%72epo\u1234%24%2572%2F"; + var repoName = "test-repo\u1234%24%2572%2F"; + + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); + var commitSha = repo.Head.Tip.Sha; + + VerifyValues( + customProps: @" + + true + + + + +", + customTargets: "", + targets: new[] + { + "Build", "Pack" + }, + expressions: new[] + { + "@(SourceRoot)", + "@(SourceRoot->'%(SourceLinkUrl)')", + "$(SourceLink)", + "$(PrivateRepositoryUrl)", + "$(RepositoryUrl)" + }, + expectedResults: new[] + { + ProjectSourceRoot, + $"https://噸.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}", + s_relativeSourceLinkJsonPath, + $"https://噸.com/test-org/{repoName}", + $"https://噸.com/test-org/{repoName}" + }); + + AssertEx.AreEqual( + $@"{{""documents"":{{""{ProjectSourceRoot.Replace(@"\", @"\\")}*"":""https://噸.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}""}}}}", + File.ReadAllText(Path.Combine(ProjectDir.Path, s_relativeSourceLinkJsonPath))); + + TestUtilities.ValidateAssemblyInformationalVersion( + Path.Combine(ProjectDir.Path, s_relativeOutputFilePath), + "1.0.0+" + commitSha); + + TestUtilities.ValidateNuSpecRepository( + Path.Combine(ProjectDir.Path, s_relativePackagePath), + type: "git", + commit: commitSha, + url: $"https://噸.com/test-org/{repoName}"); + } } } From d26c71d9940b3790a1196e391f4500273231e1ec Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 21 Feb 2019 16:08:57 +0100 Subject: [PATCH 4/9] Update Readme.md with info about Bitbucket host configuration --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 693f0e9f..5cce2d1e 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,13 @@ For projects hosted by [GitLab](https://gitlab.com) reference [Microsoft.SourceL ### Bitbucket.org -For projects hosted on [Bitbucket.org](https://bitbucket.org) in git repositories reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package: +For projects hosted on [Bitbucket.org](https://bitbucket.org) in git repositories reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package +and add Bitbucket host configuration. Including flag whether it is Enterprise Edtition or Cloud. For Enterprise Edition provide its version and change value in include attribute to your Bitbucket server name: ```xml + ``` From f6e20895ca3f9c20ab990580dc5ef4205421c280 Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Wed, 20 Mar 2019 11:12:41 +0100 Subject: [PATCH 5/9] Update BuildSourceLinkUrl for Bitbucket to support Enterprise edition with ssh uri. --- src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs index 6cd5cdf8..c42ea5e1 100644 --- a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs +++ b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs @@ -25,8 +25,7 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, ITaskItem hostItem) { - - var isEnterpriseEditionFlagAvailable = + var isEnterpriseEditionFlagAvailable = bool.TryParse(hostItem.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition); if (isEnterpriseEditionFlagAvailable && isEnterpriseEdition) @@ -45,8 +44,13 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask var bitbucketEnterpriseVersion = GetBitbucketEnterpriseVersion(hostItem); var splits = relativeUrl.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + var isSshRepoUri = !(splits.Length == 3 && splits[0] == "scm"); + var projectName = isSshRepoUri ? splits[0] : splits[1]; + var repositoryName = isSshRepoUri ? splits[1] : splits[2]; + var relativeUrlForBitbucketEnterprise = - GetRelativeUrlForBitbucketEnterprise(splits[0], splits[1], revisionId, bitbucketEnterpriseVersion); + GetRelativeUrlForBitbucketEnterprise(projectName, repositoryName, revisionId, + bitbucketEnterpriseVersion); var result = UriUtilities.Combine(contentUri.ToString(), relativeUrlForBitbucketEnterprise); From cb54756dea75ad039b9eff49dcb6ce313061b4a8 Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Wed, 20 Mar 2019 11:13:13 +0100 Subject: [PATCH 6/9] Update unit and integration tests for BitBucket Enterprise support of ssh repository uri. --- .../GetSourceLinkUrlTests.cs | 65 ++++++++++++++++++- .../BitbucketGitTests.cs | 34 +++++----- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs index 097f323c..182ae45c 100644 --- a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs +++ b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs @@ -107,7 +107,7 @@ public void BuildSourceLinkUrl_MetadataWithEnterpriseEditionButWithoutVersion_Us [InlineData("", "/", "4.6")] [InlineData("/", "", "4.6")] [InlineData("/", "/", "4.6")] - public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersion(string s1, string s2, string bitbucketVersion) + public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersionSsh(string s1, string s2, string bitbucketVersion) { var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); var version = KVP("Version", bitbucketVersion); @@ -128,6 +128,36 @@ public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersion(string s1, string s Assert.True(result); } + [Theory] + [InlineData("", "", "4.4")] + [InlineData("", "/", "4.4")] + [InlineData("/", "", "4.4")] + [InlineData("/", "/", "4.4")] + [InlineData("", "", "4.6")] + [InlineData("", "/", "4.6")] + [InlineData("/", "", "4.6")] + [InlineData("/", "/", "4.6")] + public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersionHttps(string s1, string s2, string bitbucketVersion) + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var version = KVP("Version", bitbucketVersion); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/scm/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com" + s2), isEnterpriseEditionSetting, version), + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForEnterpriseEditionOldVersion, task.SourceLinkUrl); + Assert.True(result); + } + [Theory] [InlineData("", "", "")] [InlineData("", "", "4.7")] @@ -138,7 +168,7 @@ public void BuildSourceLinkUrl_BitbucketEnterpriseOldVersion(string s1, string s [InlineData("", "/", "5.6")] [InlineData("/", "", "5.6")] [InlineData("/", "/", "5.6")] - public void BuildSourceLinkUrl_BitbucketEnterpriseNewVersion(string s1, string s2, string bitbucketVersion) + public void BuildSourceLinkUrl_BitbucketEnterpriseNewVersionSsh(string s1, string s2, string bitbucketVersion) { var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); var version = KVP("Version", bitbucketVersion); @@ -159,6 +189,37 @@ public void BuildSourceLinkUrl_BitbucketEnterpriseNewVersion(string s1, string s Assert.True(result); } + [Theory] + [InlineData("", "", "")] + [InlineData("", "", "4.7")] + [InlineData("", "/", "4.7")] + [InlineData("/", "", "4.7")] + [InlineData("/", "/", "4.7")] + [InlineData("", "", "5.6")] + [InlineData("", "/", "5.6")] + [InlineData("/", "", "5.6")] + [InlineData("/", "/", "5.6")] + public void BuildSourceLinkUrl_BitbucketEnterpriseNewVersionHttps(string s1, string s2, string bitbucketVersion) + { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "true"); + var version = KVP("Version", bitbucketVersion); + var engine = new MockEngine(); + var task = new GetSourceLinkUrl() + { + BuildEngine = engine, + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.domain.com:100/scm/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + Hosts = new[] + { + new MockItem("domain.com", KVP("ContentUrl", "https://bitbucket.domain.com" + s2), isEnterpriseEditionSetting, version), + } + }; + + bool result = task.Execute(); + AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); + AssertEx.AreEqual(ExpectedUrlForEnterpriseEditionNewVersion, task.SourceLinkUrl); + Assert.True(result); + } + [Fact] public void BuildSourceLinkUrl_IncorrectVersionForEnterpriseEdition_ERROR() { diff --git a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs index 6ba1f6af..a7ae049c 100644 --- a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs +++ b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs @@ -74,10 +74,11 @@ public void FullValidation_EnterpriseNewHttps() { // Test non-ascii characters and escapes in the URL. // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. - var repoUrl = "https://bitbucket.domain.com/test-org/test-%72epo\u1234%24%2572%2F"; + var repoUrl = "https://bitbucket.domain.com/scm/test-org/test-%72epo\u1234%24%2572%2F"; var repoName = "test-repo\u1234%24%2572%2F"; - var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] {ProjectFileName}, + repoUrl); var commitSha = repo.Head.Tip.Sha; VerifyValues( @@ -107,8 +108,8 @@ public void FullValidation_EnterpriseNewHttps() ProjectSourceRoot, $"https://bitbucket.domain.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}", s_relativeSourceLinkJsonPath, - $"https://bitbucket.domain.com/test-org/{repoName}", - $"https://bitbucket.domain.com/test-org/{repoName}" + $"https://bitbucket.domain.com/scm/test-org/{repoName}", + $"https://bitbucket.domain.com/scm/test-org/{repoName}" }); AssertEx.AreEqual( @@ -123,14 +124,15 @@ public void FullValidation_EnterpriseNewHttps() Path.Combine(ProjectDir.Path, s_relativePackagePath), type: "git", commit: commitSha, - url: $"https://bitbucket.domain.com/test-org/{repoName}"); + url: $"https://bitbucket.domain.com/scm/test-org/{repoName}"); } + [ConditionalFact(typeof(DotNetSdkAvailable))] public void FullValidation_EnterpriseOldHttps() { // Test non-ascii characters and escapes in the URL. // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. - var repoUrl = "https://bitbucket.domain.com/test-org/test-%72epo\u1234%24%2572%2F"; + var repoUrl = "https://bitbucket.domain.com/scm/test-org/test-%72epo\u1234%24%2572%2F"; var repoName = "test-repo\u1234%24%2572%2F"; var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, repoUrl); @@ -138,13 +140,13 @@ public void FullValidation_EnterpriseOldHttps() VerifyValues( customProps: @" - - true - - - - -", + + true + + + + + ", customTargets: "", targets: new[] { @@ -163,8 +165,8 @@ public void FullValidation_EnterpriseOldHttps() ProjectSourceRoot, $"https://bitbucket.domain.com/projects/test-org/repos/{repoName}/browse/*?at={commitSha}&raw", s_relativeSourceLinkJsonPath, - $"https://bitbucket.domain.com/test-org/{repoName}", - $"https://bitbucket.domain.com/test-org/{repoName}" + $"https://bitbucket.domain.com/scm/test-org/{repoName}", + $"https://bitbucket.domain.com/scm/test-org/{repoName}" }); AssertEx.AreEqual( @@ -179,7 +181,7 @@ public void FullValidation_EnterpriseOldHttps() Path.Combine(ProjectDir.Path, s_relativePackagePath), type: "git", commit: commitSha, - url: $"https://bitbucket.domain.com/test-org/{repoName}"); + url: $"https://bitbucket.domain.com/scm/test-org/{repoName}"); } [ConditionalFact(typeof(DotNetSdkAvailable))] From 3203cf18c9b6833c5eaaec0d1f39c12440a76f4e Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 9 May 2019 12:02:20 +0200 Subject: [PATCH 7/9] Code review refactoring for Bitbucket Enterprise Adding SourceLinkBitbucketGitHost is not necessary for cloud scenario For self-hosting scenario adding SourceLinkBitbucketGitHost with proper host is required EnterpriseEdition and Version settings are optional with default values. --- README.md | 15 ++++- .../GetSourceLinkUrlTests.cs | 30 ++++----- .../GetSourceLinkUrl.cs | 16 ++++- .../BitbucketGitTests.cs | 64 +++++++++++++++++-- 4 files changed, 101 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 1f8b852a..3f193410 100644 --- a/README.md +++ b/README.md @@ -92,16 +92,25 @@ For projects hosted by [GitLab](https://gitlab.com) reference [Microsoft.SourceL ### Bitbucket.org -For projects hosted on [Bitbucket.org](https://bitbucket.org) in git repositories reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package -and add Bitbucket host configuration. Including flag whether it is Enterprise Edtition or Cloud. For Enterprise Edition provide its version and change value in include attribute to your Bitbucket server name: +For projects hosted on [Bitbucket.org](https://bitbucket.org) in git repositories reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package: ```xml - ``` +For self-hosted Bitbucket projects reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package and add Bitbucket host configuration. +Additionally there are two optional attributes with default values: +EnterpriseEdition="true" - flag whether it is Enterprise Edtition or Cloud Edition +Version="4.7" - for Enterprise Edition provides its version + +```xml + + + + +``` ### Multiple providers, repositories with submodules If your repository contains submodules hosted by other git providers reference packages of all these providers. For example, projects in a repository hosted by Azure DevOps that links a GitHub repository via a submodule should reference both [Microsoft.SourceLink.Vsts.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Vsts.Git) and [Microsoft.SourceLink.GitHub](https://www.nuget.org/packages/Microsoft.SourceLink.GitHub) packages. [Additional configuration](https://github.com/dotnet/sourcelink/blob/master/docs/README.md#configuring-projects-with-multiple-sourcelink-providers) might be needed if multiple Source Link packages are used in the project. diff --git a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs index 182ae45c..ff04e550 100644 --- a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs +++ b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs @@ -32,22 +32,17 @@ public void EmptyHosts() Assert.False(result); } - [Theory] - [InlineData("", "")] - [InlineData("", "/")] - [InlineData("/", "")] - [InlineData("/", "/")] - public void BuildSourceLinkUrl_BitbucketCloud(string s1, string s2) + [Fact] + public void BuildSourceLinkUrl_bitbucketorgIsHost_UseCloudEditionAsDefault() { - var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "false"); var engine = new MockEngine(); - var task = new GetSourceLinkUrl() + var task = new GetSourceLinkUrl { BuildEngine = engine, - SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://subdomain.mybitbucket.org:100/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.org:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), Hosts = new[] { - new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y" + s2), isEnterpriseEditionSetting), + new MockItem("bitbucket.org", KVP("ContentUrl", "https://domain.com/x/y")) } }; @@ -57,17 +52,22 @@ public void BuildSourceLinkUrl_BitbucketCloud(string s1, string s2) Assert.True(result); } - [Fact] - public void BuildSourceLinkUrl_EmptyMetadata_UseCloudEditionAsDefault() + [Theory] + [InlineData("", "")] + [InlineData("", "/")] + [InlineData("/", "")] + [InlineData("/", "/")] + public void BuildSourceLinkUrl_BitbucketCloud(string s1, string s2) { + var isEnterpriseEditionSetting = KVP("EnterpriseEdition", "false"); var engine = new MockEngine(); - var task = new GetSourceLinkUrl + var task = new GetSourceLinkUrl() { BuildEngine = engine, - SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://subdomain.mybitbucket.org:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), + SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://subdomain.mybitbucket.org:100/a/b" + s1), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), Hosts = new[] { - new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y")) + new MockItem("mybitbucket.org", KVP("ContentUrl", "https://domain.com/x/y" + s2), isEnterpriseEditionSetting), } }; diff --git a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs index c42ea5e1..6eb429d7 100644 --- a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs +++ b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs @@ -16,6 +16,7 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask protected override string HostsItemGroupName => "SourceLinkBitbucketGitHost"; protected override string ProviderDisplayName => "Bitbucket.Git"; + private const string BitbucketCloudHostingUrl = "bitbucket.org"; private const string IsEnterpriseEditionMetadataName = "EnterpriseEdition"; private const string VersionMetadataName = "Version"; private const string VersionWithNewUrlFormat = "4.7"; @@ -28,7 +29,19 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask var isEnterpriseEditionFlagAvailable = bool.TryParse(hostItem.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition); - if (isEnterpriseEditionFlagAvailable && isEnterpriseEdition) + if (isEnterpriseEditionFlagAvailable) + { + if (isEnterpriseEdition) + { + return BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); + } + else + { + return BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId); + } + } + + if (!BitbucketCloudHostingUrl.Equals(gitUri.GetHost(), StringComparison.OrdinalIgnoreCase)) { return BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); } @@ -36,6 +49,7 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask { return BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId); } + } private string BuildSourceLinkUrlForEnterpriseEdition(Uri contentUri, string relativeUrl, string revisionId, diff --git a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs index a7ae049c..70000639 100644 --- a/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs +++ b/src/SourceLink.Git.IntegrationTests/BitbucketGitTests.cs @@ -27,11 +27,7 @@ public void FullValidation_CloudHttps() customProps: @" true - - - - -", +", customTargets: "", targets: new[] { @@ -127,6 +123,64 @@ public void FullValidation_EnterpriseNewHttps() url: $"https://bitbucket.domain.com/scm/test-org/{repoName}"); } + [ConditionalFact(typeof(DotNetSdkAvailable))] + public void FullValidation_EnterpriseNewHttpsWithDefaultFlags() + { + // Test non-ascii characters and escapes in the URL. + // Escaped URI reserved characters should remain escaped, non-reserved characters unescaped in the results. + var repoUrl = "https://bitbucket.domain.com/scm/test-org/test-%72epo\u1234%24%2572%2F"; + var repoName = "test-repo\u1234%24%2572%2F"; + + var repo = GitUtilities.CreateGitRepositoryWithSingleCommit(ProjectDir.Path, new[] { ProjectFileName }, + repoUrl); + var commitSha = repo.Head.Tip.Sha; + + VerifyValues( + customProps: @" + + true + + + + +", + customTargets: "", + targets: new[] + { + "Build", "Pack" + }, + expressions: new[] + { + "@(SourceRoot)", + "@(SourceRoot->'%(SourceLinkUrl)')", + "$(SourceLink)", + "$(PrivateRepositoryUrl)", + "$(RepositoryUrl)" + }, + expectedResults: new[] + { + ProjectSourceRoot, + $"https://bitbucket.domain.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}", + s_relativeSourceLinkJsonPath, + $"https://bitbucket.domain.com/scm/test-org/{repoName}", + $"https://bitbucket.domain.com/scm/test-org/{repoName}" + }); + + AssertEx.AreEqual( + $@"{{""documents"":{{""{ProjectSourceRoot.Replace(@"\", @"\\")}*"":""https://bitbucket.domain.com/projects/test-org/repos/{repoName}/raw/*?at={commitSha}""}}}}", + File.ReadAllText(Path.Combine(ProjectDir.Path, s_relativeSourceLinkJsonPath))); + + TestUtilities.ValidateAssemblyInformationalVersion( + Path.Combine(ProjectDir.Path, s_relativeOutputFilePath), + "1.0.0+" + commitSha); + + TestUtilities.ValidateNuSpecRepository( + Path.Combine(ProjectDir.Path, s_relativePackagePath), + type: "git", + commit: commitSha, + url: $"https://bitbucket.domain.com/scm/test-org/{repoName}"); + } + [ConditionalFact(typeof(DotNetSdkAvailable))] public void FullValidation_EnterpriseOldHttps() { From 2b52fc2f32dd82ada5b8f11cfe415c4354e66d04 Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 9 May 2019 12:57:06 +0200 Subject: [PATCH 8/9] Fix unnecessary line break. --- src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs index 6eb429d7..6cb72f79 100644 --- a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs +++ b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs @@ -23,8 +23,7 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask protected override bool SupportsImplicitHost => false; - protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, - ITaskItem hostItem) + protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, ITaskItem hostItem) { var isEnterpriseEditionFlagAvailable = bool.TryParse(hostItem.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition); From 8593b6563972d3d00ce96ea98bee3090f193cec7 Mon Sep 17 00:00:00 2001 From: Adam Sajdak Date: Thu, 16 May 2019 12:38:18 +0200 Subject: [PATCH 9/9] Support for Bitbucket Enterprise Add EnterpriseEdition="false" in defaulp props file so Bitbucket.org is recognized as cloud version By default EnterpriseEdition is true so any other bitbucket repository is recognized as enterprise version by default --- README.md | 10 +++--- .../GetSourceLinkUrlTests.cs | 20 ----------- .../GetSourceLinkUrl.cs | 33 +++---------------- .../Microsoft.SourceLink.Bitbucket.Git.props | 2 +- 4 files changed, 12 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 3f193410..e1d1bc14 100644 --- a/README.md +++ b/README.md @@ -101,16 +101,18 @@ For projects hosted on [Bitbucket.org](https://bitbucket.org) in git repositorie ``` For self-hosted Bitbucket projects reference [Microsoft.SourceLink.Bitbucket.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Bitbucket.Git) package and add Bitbucket host configuration. -Additionally there are two optional attributes with default values: -EnterpriseEdition="true" - flag whether it is Enterprise Edtition or Cloud Edition -Version="4.7" - for Enterprise Edition provides its version +Additional configuration is available when SourceLinkBitbucketGitHost is added to csproj: + +- EnterpriseEdition - flag whether it is Enterprise Edition or Cloud Edition, by default it is true. +- Version="4.7" - for Enterprise Edition provides its version. URL format for accessing files is different for Bitbucket in version < 4.7, please add Bitbucket version if it is the case ```xml - + ``` + ### Multiple providers, repositories with submodules If your repository contains submodules hosted by other git providers reference packages of all these providers. For example, projects in a repository hosted by Azure DevOps that links a GitHub repository via a submodule should reference both [Microsoft.SourceLink.Vsts.Git](https://www.nuget.org/packages/Microsoft.SourceLink.Vsts.Git) and [Microsoft.SourceLink.GitHub](https://www.nuget.org/packages/Microsoft.SourceLink.GitHub) packages. [Additional configuration](https://github.com/dotnet/sourcelink/blob/master/docs/README.md#configuring-projects-with-multiple-sourcelink-providers) might be needed if multiple Source Link packages are used in the project. diff --git a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs index ff04e550..b0c6e170 100644 --- a/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs +++ b/src/SourceLink.Bitbucket.Git.UnitTests/GetSourceLinkUrlTests.cs @@ -32,26 +32,6 @@ public void EmptyHosts() Assert.False(result); } - [Fact] - public void BuildSourceLinkUrl_bitbucketorgIsHost_UseCloudEditionAsDefault() - { - var engine = new MockEngine(); - var task = new GetSourceLinkUrl - { - BuildEngine = engine, - SourceRoot = new MockItem("/src/", KVP("RepositoryUrl", "http://bitbucket.org:100/a/b"), KVP("SourceControl", "git"), KVP("RevisionId", "0123456789abcdefABCDEF000000000000000000")), - Hosts = new[] - { - new MockItem("bitbucket.org", KVP("ContentUrl", "https://domain.com/x/y")) - } - }; - - bool result = task.Execute(); - AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); - AssertEx.AreEqual(ExpectedUrlForCloudEdition, task.SourceLinkUrl); - Assert.True(result); - } - [Theory] [InlineData("", "")] [InlineData("", "/")] diff --git a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs index 6cb72f79..7870674c 100644 --- a/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs +++ b/src/SourceLink.Bitbucket.Git/GetSourceLinkUrl.cs @@ -16,39 +16,16 @@ public sealed class GetSourceLinkUrl : GetSourceLinkUrlGitTask protected override string HostsItemGroupName => "SourceLinkBitbucketGitHost"; protected override string ProviderDisplayName => "Bitbucket.Git"; - private const string BitbucketCloudHostingUrl = "bitbucket.org"; private const string IsEnterpriseEditionMetadataName = "EnterpriseEdition"; private const string VersionMetadataName = "Version"; private const string VersionWithNewUrlFormat = "4.7"; - protected override bool SupportsImplicitHost => false; - protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string relativeUrl, string revisionId, ITaskItem hostItem) { - var isEnterpriseEditionFlagAvailable = - bool.TryParse(hostItem.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition); - - if (isEnterpriseEditionFlagAvailable) - { - if (isEnterpriseEdition) - { - return BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); - } - else - { - return BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId); - } - } - - if (!BitbucketCloudHostingUrl.Equals(gitUri.GetHost(), StringComparison.OrdinalIgnoreCase)) - { - return BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); - } - else - { - return BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId); - } - + return + bool.TryParse(hostItem?.GetMetadata(IsEnterpriseEditionMetadataName), out var isEnterpriseEdition) && !isEnterpriseEdition + ? BuildSourceLinkUrlForCloudEdition(contentUri, relativeUrl, revisionId) + : BuildSourceLinkUrlForEnterpriseEdition(contentUri, relativeUrl, revisionId, hostItem); } private string BuildSourceLinkUrlForEnterpriseEdition(Uri contentUri, string relativeUrl, string revisionId, @@ -72,7 +49,7 @@ protected override string BuildSourceLinkUrl(Uri contentUri, Uri gitUri, string private Version GetBitbucketEnterpriseVersion(ITaskItem hostItem) { - var bitbucketEnterpriseVersionAsString = hostItem.GetMetadata(VersionMetadataName); + var bitbucketEnterpriseVersionAsString = hostItem?.GetMetadata(VersionMetadataName); Version bitbucketEnterpriseVersion; if (!string.IsNullOrEmpty(bitbucketEnterpriseVersionAsString)) { diff --git a/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props b/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props index 08469bed..8710d6aa 100644 --- a/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props +++ b/src/SourceLink.Bitbucket.Git/build/Microsoft.SourceLink.Bitbucket.Git.props @@ -1,6 +1,6 @@ - +