Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions docs/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The following configuration properties are supported for each provider configura
: User password required to access private repositories on the SCM server.

`providers.<provider>.token`
: *Required only for private Gitlab servers*
: *Required only for private Gitlab servers and supported for BitBucket*
: Private API access token.

`providers.<provider>.platform`
Expand All @@ -51,19 +51,23 @@ The following configuration properties are supported for each provider configura

### BitBucket

Create a `bitbucket` entry in the [SCM configuration file](#git-configuration) specifying your user name and app password, as shown below:
Create a `bitbucket` entry in the [SCM configuration file](#git-configuration) specifying your user name and either an API token or app password, as shown below:

```groovy
providers {
bitbucket {
user = 'me'
password = 'my-secret'
token = 'my-api-token'
}
}
```

:::{versionadded} 25.07.0-edge
API tokens are supported for BitBucket authentication. When both `token` and `password` are provided, the API token takes priority over the app password.
:::

:::{note}
App passwords are substitute passwords for a user account which you can use for scripts and integrating tools in order to avoid putting your real password into configuration files. Learn more at [this link](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/).
API tokens are substitute passwords for a user account which you can use for scripts and integrating tools in order to avoid putting your real password into configuration files. Learn more about [app passwords](https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/) and [API tokens](https://support.atlassian.com/bitbucket-cloud/docs/using-api-tokens/).
:::

### BitBucket Server
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package nextflow.scm
import groovy.transform.Memoized
import groovy.util.logging.Slf4j
import nextflow.exception.AbortOperationException
import org.eclipse.jgit.transport.CredentialsProvider
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider

/**
* Implements a repository provider for the BitBucket service
*
Expand All @@ -41,19 +44,28 @@ final class BitbucketRepositoryProvider extends RepositoryProvider {

@Override
protected String[] getAuth() {
return config.token
? new String[] { "Authorization", "Bearer ${config.token}" }
: super.getAuth()
if (!hasCredentials()) {
return null
}

String secret = getToken() ?: getPassword()
String authString = "${getUser()}:${secret}".bytes.encodeBase64().toString()

return new String[] { "Authorization", "Basic " + authString }
}

@Override
boolean hasCredentials() {
return getToken()
? true
: super.hasCredentials()
return getUser() && (getPassword() || getToken())
}

/** {@inheritDoc} */
@Override
CredentialsProvider getGitCredentials() {
return new UsernamePasswordCredentialsProvider(getUser(), getToken() ?: getPassword())
}

/** {@inheritDoc} */
/** {@inheritDoc} */
@Override
String getName() { "BitBucket" }

Expand Down Expand Up @@ -163,7 +175,12 @@ final class BitbucketRepositoryProvider extends RepositoryProvider {
if( !result )
throw new IllegalStateException("Missing clone URL for: $project")

return result.href
/**
* The clone URL when an API token is used is of the form: https://{bitbucket_username}:{api_token}@bitbucket.org/{workspace}/{repository}.git
* @see <a href="https://support.atlassian.com/bitbucket-cloud/docs/using-api-tokens/">Using API tokens</a>
*/
String cloneUrl = result.href
return getToken() ? cloneUrl.replace('@', ":${getToken()}@") : cloneUrl
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class BitbucketRepositoryProviderTest extends Specification {
when:
def url = new BitbucketRepositoryProvider('pditommaso/tutorial',config).getCloneUrl()
then:
url ==~ /https:\/\/\w[email protected]\/pditommaso\/tutorial.git/
url ==~ /https:\/\/.[email protected]\/pditommaso\/tutorial.git/
}

def testGetHomePage() {
Expand Down Expand Up @@ -182,11 +182,13 @@ class BitbucketRepositoryProviderTest extends Specification {
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')
EXPECTED | CONFIG
false | new ProviderConfig('bitbucket')
false | new ProviderConfig('bitbucket').setUser('foo')
false | new ProviderConfig('bitbucket').setToken('xyz')
true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
true | new ProviderConfig('bitbucket').setUser('foo').setToken('xyz')
true | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar').setToken('xyz')
}

@Unroll
Expand All @@ -198,9 +200,9 @@ class BitbucketRepositoryProviderTest extends Specification {
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')
EXPECTED | CONFIG
null | new ProviderConfig('bitbucket')
["Authorization", "Basic ${"foo:bar".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo').setPassword('bar')
["Authorization", "Basic ${"foo@nextflow.io:xyz".bytes.encodeBase64()}"] | new ProviderConfig('bitbucket').setUser('foo@nextflow.io').setToken('xyz')
}
}