From 7b7489e8113f6f3ec5a802390da8cc9b8ec29d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Crespo=20Garc=C3=ADa?= Date: Mon, 23 Jun 2025 13:34:06 +0200 Subject: [PATCH 1/7] Add support for Bitbucket API tokens --- .../scm/BitbucketRepositoryProvider.groovy | 27 ++++++++++++++----- .../groovy/nextflow/scm/ProviderConfig.groovy | 1 + .../BitbucketRepositoryProviderTest.groovy | 8 +++--- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy index dcc9f7579b..18bd41a17e 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy @@ -39,23 +39,36 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { this.config = config ?: new ProviderConfig('bitbucket') } - /** {@inheritDoc} */ + @Override + protected String[] getAuth() { + if (config.password) { + return super.getAuth() + } + + if (config.token) { + return new String[] { "Authorization", "Bearer ${config.token}" } + } + + return EMPTY_ARRAY + } + + /** {@inheritDoc} */ @Override String getName() { "BitBucket" } @Override String getEndpointUrl() { - "${config.endpoint}/api/2.0/repositories/${project}" + "${config.endpoint}/2.0/repositories/${project}" } @Override String getContentUrl( String path ) { final ref = revision ? getRefForRevision(revision) : getMainBranch() - return "${config.endpoint}/api/2.0/repositories/$project/src/$ref/$path" + return "${config.endpoint}/2.0/repositories/$project/src/$ref/$path" } private String getMainBranchUrl() { - "${config.endpoint}/api/2.0/repositories/$project" + "${config.endpoint}/2.0/repositories/$project" } String getMainBranch() { @@ -87,7 +100,7 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { } private String getRefForRevision0(String revision, String type){ - final resp = invokeAndParseResponse("${config.endpoint}/api/2.0/repositories/$project/refs/$type/$revision") + final resp = invokeAndParseResponse("${config.endpoint}/2.0/repositories/$project/refs/$type/$revision") return resp?.target?.hash } @@ -117,7 +130,7 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override List getTags() { final result = new ArrayList() - final url = "$config.endpoint/api/2.0/repositories/$project/refs/tags" + final url = "$config.endpoint/2.0/repositories/$project/refs/tags" final mapper = { Map entry -> result.add( new TagInfo(entry.name, entry.target?.hash) ) } invokeAndResponseWithPaging(url, mapper) return result @@ -131,7 +144,7 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override List getBranches() { final result = new ArrayList() - final url = "$config.endpoint/api/2.0/repositories/$project/refs/branches" + final url = "$config.endpoint/2.0/repositories/$project/refs/branches" final mapper = { Map entry -> result.add( new BranchInfo(entry.name, entry.target?.hash) ) } invokeAndResponseWithPaging(url, mapper) return result diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/ProviderConfig.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/ProviderConfig.groovy index 28064f9aaa..0dd3db854a 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/ProviderConfig.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/ProviderConfig.groovy @@ -84,6 +84,7 @@ class ProviderConfig { case 'bitbucket': attr.platform = name if( !attr.server ) attr.server = 'https://bitbucket.org' + if( !attr.endpoint ) attr.endpoint = 'https://api.bitbucket.org' break case 'azurerepos': diff --git a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy index 7dc933f793..da5641c0b8 100644 --- a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy @@ -36,7 +36,7 @@ class BitbucketRepositoryProviderTest extends Specification { when: def url = new BitbucketRepositoryProvider('pditommaso/tutorial',config).getCloneUrl() then: - url == "https://${config.user}@bitbucket.org/pditommaso/tutorial.git".toString() + url ==~ /https:\/\/\w+@bitbucket.org\/pditommaso\/tutorial.git/ } @@ -100,17 +100,17 @@ class BitbucketRepositoryProviderTest extends Specification { expect: new BitbucketRepositoryProvider('pditommaso/tutorial', config) .setRevision('test-branch') - .getContentUrl('main.nf') == 'https://bitbucket.org/api/2.0/repositories/pditommaso/tutorial/src/test-branch/main.nf' + .getContentUrl('main.nf') == 'https://api.bitbucket.org/2.0/repositories/pditommaso/tutorial/src/test-branch/main.nf' and: new BitbucketRepositoryProvider('pditommaso/tutorial', config) .setRevision('feature/with-slash') - .getContentUrl('main.nf') == 'https://bitbucket.org/api/2.0/repositories/pditommaso/tutorial/src/a6b825b22d46758cdeb496ae6cf26aef839ace52/main.nf' + .getContentUrl('main.nf') == 'https://api.bitbucket.org/2.0/repositories/pditommaso/tutorial/src/a6b825b22d46758cdeb496ae6cf26aef839ace52/main.nf' and: new BitbucketRepositoryProvider('pditommaso/tutorial', config) .setRevision('test/tag/v2') - .getContentUrl('main.nf') == 'https://bitbucket.org/api/2.0/repositories/pditommaso/tutorial/src/8f849beceb2ea479ef836809ca33d3daeeed25f9/main.nf' + .getContentUrl('main.nf') == 'https://api.bitbucket.org/2.0/repositories/pditommaso/tutorial/src/8f849beceb2ea479ef836809ca33d3daeeed25f9/main.nf' } From fc8329d34979d00da1730656d10ef68768164339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Crespo=20Garc=C3=ADa?= Date: Mon, 23 Jun 2025 13:50:21 +0200 Subject: [PATCH 2/7] Fix tests --- .../src/test/groovy/nextflow/scm/ProviderConfigTest.groovy | 2 +- .../src/test/groovy/nextflow/scm/RepositoryProviderTest.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/scm/ProviderConfigTest.groovy b/modules/nextflow/src/test/groovy/nextflow/scm/ProviderConfigTest.groovy index 74fef90799..a7429c98bb 100644 --- a/modules/nextflow/src/test/groovy/nextflow/scm/ProviderConfigTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/scm/ProviderConfigTest.groovy @@ -95,7 +95,7 @@ class ProviderConfigTest extends Specification { then: config.name == 'bitbucket' config.server == 'https://bitbucket.org' - config.endpoint == 'https://bitbucket.org' + config.endpoint == 'https://api.bitbucket.org' config.platform == 'bitbucket' config.domain == 'bitbucket.org' } diff --git a/modules/nextflow/src/test/groovy/nextflow/scm/RepositoryProviderTest.groovy b/modules/nextflow/src/test/groovy/nextflow/scm/RepositoryProviderTest.groovy index d233669842..90533e3dc3 100644 --- a/modules/nextflow/src/test/groovy/nextflow/scm/RepositoryProviderTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/scm/RepositoryProviderTest.groovy @@ -48,7 +48,7 @@ class RepositoryProviderTest extends Specification { provider = RepositoryFactory.newRepositoryProvider(new ProviderConfig('bitbucket'),'project/z') then: provider instanceof BitbucketRepositoryProvider - provider.endpointUrl == 'https://bitbucket.org/api/2.0/repositories/project/z' + provider.endpointUrl == 'https://api.bitbucket.org/2.0/repositories/project/z' when: provider = RepositoryFactory.newRepositoryProvider(new ProviderConfig('local', [path:'/user/data']),'local/w') From 2ec614cb495d1865055108b65ecaeb3825f62577 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Fri, 27 Jun 2025 10:05:16 +0200 Subject: [PATCH 3/7] Update return type [ci fast] Signed-off-by: Paolo Di Tommaso --- .../main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy index 18bd41a17e..3353a2ae69 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy @@ -49,7 +49,7 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { return new String[] { "Authorization", "Bearer ${config.token}" } } - return EMPTY_ARRAY + return null } /** {@inheritDoc} */ From d1448271cae11836299004eaa27c549c3178c456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Crespo=20Garc=C3=ADa?= Date: Fri, 27 Jun 2025 10:17:42 +0200 Subject: [PATCH 4/7] Refactor condition --- .../main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy index 3353a2ae69..31fb9cd6e6 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy @@ -41,7 +41,7 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override protected String[] getAuth() { - if (config.password) { + if (config.user && config.password) { return super.getAuth() } From 622c378f1cd8a76524ac9839dc4deb0ce4032508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Crespo=20Garc=C3=ADa?= Date: Fri, 27 Jun 2025 13:03:36 +0200 Subject: [PATCH 5/7] Refactor getAuth method logic and override hasCredentials method --- .../nextflow/scm/BitbucketRepositoryProvider.groovy | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy index 31fb9cd6e6..7fbf03c5e7 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy @@ -41,15 +41,20 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override protected String[] getAuth() { - if (config.user && config.password) { - return super.getAuth() + if (config.token) { + return new String[] { "Authorization", "Bearer ${config.token}" } } + return super.getAuth() + } + + @Override + boolean hasCredentials() { if (config.token) { - return new String[] { "Authorization", "Bearer ${config.token}" } + return true } - return null + return super.hasCredentials() } /** {@inheritDoc} */ From 62fb99fc415a7ae7ea320ad0718ae64218891756 Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 28 Jun 2025 16:22:14 +0200 Subject: [PATCH 6/7] Minor changes + tests [ci fast] Signed-off-by: Paolo Di Tommaso --- .../scm/BitbucketRepositoryProvider.groovy | 17 ++++------ .../BitbucketRepositoryProviderTest.groovy | 34 +++++++++++++++++-- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy index 7fbf03c5e7..f7e468cdd4 100644 --- a/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy +++ b/modules/nextflow/src/main/groovy/nextflow/scm/BitbucketRepositoryProvider.groovy @@ -41,20 +41,16 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override protected String[] getAuth() { - if (config.token) { - return new String[] { "Authorization", "Bearer ${config.token}" } - } - - return super.getAuth() + return config.token + ? new String[] { "Authorization", "Bearer ${config.token}" } + : super.getAuth() } @Override boolean hasCredentials() { - if (config.token) { - return true - } - - return super.hasCredentials() + return config.token + ? true + : super.hasCredentials() } /** {@inheritDoc} */ @@ -177,7 +173,6 @@ final class BitbucketRepositoryProvider extends RepositoryProvider { @Override byte[] readBytes(String path) { - def url = getContentUrl(path) invoke(url)?.getBytes() } diff --git a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy index da5641c0b8..d082d3977a 100644 --- a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy @@ -21,6 +21,7 @@ import spock.lang.IgnoreIf import spock.lang.Requires import spock.lang.Specification import spock.lang.Timeout +import spock.lang.Unroll @Timeout(30) @IgnoreIf({System.getenv('NXF_SMOKE')}) @@ -45,10 +46,8 @@ class BitbucketRepositoryProviderTest extends Specification { new BitbucketRepositoryProvider('pditommaso/tutorial').getRepositoryUrl() == "https://bitbucket.org/pditommaso/tutorial" } - @Requires( { System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') } ) def testReadContent() { - given: def token = System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') def config = new ProviderConfig('bitbucket').setAuth(token) @@ -59,7 +58,6 @@ class BitbucketRepositoryProviderTest extends Specification { then: result.trim().startsWith('#!/usr/bin/env nextflow') - } @Requires( { System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') } ) @@ -160,6 +158,36 @@ class BitbucketRepositoryProviderTest extends Specification { then: !data.contains('world') data.contains('mundo') + } + + @Unroll + def 'should validate hasCredentials' () { + given: + def provider = new BitbucketRepositoryProvider('pditommaso/tutorial', CONFIG) + + expect: + provider.hasCredentials() == EXPECTED + + where: + EXPECTED | CONFIG + false | new ProviderConfig('bitbucket') + false | new ProviderConfig('bitbucket').setUser('foo') + true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar') + true | new ProviderConfig('bitbucket').setToken('xyz') + } + + @Unroll + def 'should validate getAuth' () { + given: + def provider = new BitbucketRepositoryProvider('pditommaso/tutorial', CONFIG) + + expect: + provider.getAuth() == EXPECTED as String[] + where: + EXPECTED | CONFIG + null | new ProviderConfig('bitbucket') + ["Authorization", "Bearer xyz"] | new ProviderConfig('bitbucket').setToken('xyz') + ["Authorization", "Basic ${"foo:bar".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar') } } From 6b7544590bbe65592aa5cdd87cabe0eec1747c2b Mon Sep 17 00:00:00 2001 From: Paolo Di Tommaso Date: Sat, 28 Jun 2025 16:30:07 +0200 Subject: [PATCH 7/7] Update Bitbucket test [ci fast] Signed-off-by: Paolo Di Tommaso --- .../nextflow/scm/BitbucketRepositoryProviderTest.groovy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy index d082d3977a..e63f5cdf11 100644 --- a/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy +++ b/modules/nextflow/src/test/groovy/nextflow/scm/BitbucketRepositoryProviderTest.groovy @@ -29,7 +29,6 @@ class BitbucketRepositoryProviderTest extends Specification { @Requires( { System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') } ) def testBitbucketCloneURL() { - given: def token = System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') def config = new ProviderConfig('bitbucket').setAuth(token) @@ -53,11 +52,11 @@ class BitbucketRepositoryProviderTest extends Specification { def config = new ProviderConfig('bitbucket').setAuth(token) when: - def repo = new BitbucketRepositoryProvider('pditommaso/tutorial', config) + def repo = new BitbucketRepositoryProvider('pditommaso/secret', config) def result = repo.readText('main.nf') then: - result.trim().startsWith('#!/usr/bin/env nextflow') + result.trim() == "println 'Hello from Bitbucket'" } @Requires( { System.getenv('NXF_BITBUCKET_ACCESS_TOKEN') } )