Skip to content

Commit 477d314

Browse files
author
Rachel Dowavic
committed
Add Bitbucket API token support
Bitbucket has deprecated app passwords (disabled after June 9, 2026). This adds support for the new API tokens via the BITBUCKET_TOKEN env var, falling back to BITBUCKET_PASSWORD for backward compatibility. Fixes #197
1 parent 15e3a70 commit 477d314

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

‎README.md‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ If you are on MacOS, a community member has created a [Homebrew formula](https:/
5959

6060
``gitbackup`` requires a [GitHub API access token](https://github.com/blog/1509-personal-api-tokens) for
6161
backing up GitHub repositories, a [GitLab personal access token](https://gitlab.com/profile/personal_access_tokens)
62-
for GitLab repositories, a username and [app password](https://bitbucket.org/account/settings/app-passwords/) for
62+
for GitLab repositories, a username and [API token](https://support.atlassian.com/bitbucket-cloud/docs/api-tokens/) (or [app password](https://bitbucket.org/account/settings/app-passwords/)) for
6363
Bitbucket repositories, or a [Forgejo access token][https://docs.codeberg.org/advanced/access-token/] for Forgejo.
6464

6565
You can supply the tokens to ``gitbackup`` using ``GITHUB_TOKEN``, ``GITLAB_TOKEN``, or ``FORGEJO_TOKEN`` environment
66-
variables respectively, and the Bitbucket credentials with ``BITBUCKET_USERNAME`` and ``BITBUCKET_PASSWORD``.
66+
variables respectively, and the Bitbucket credentials with ``BITBUCKET_USERNAME`` and either ``BITBUCKET_TOKEN`` or ``BITBUCKET_PASSWORD``.
6767

6868
### GitHub Specific oAuth App Flow
6969

@@ -84,11 +84,19 @@ time you run it, it will ask you for the keyring password and retrieve the token
8484

8585
#### Bitbucket
8686

87-
For the App password, the following permissions are required:
87+
**API tokens** (recommended):
88+
89+
- `read:user:bitbucket`
90+
- `read:workspace:bitbucket`
91+
- `read:repository:bitbucket`
92+
93+
**App passwords** (deprecated, disabled after June 9, 2026):
8894

8995
- `Account:Read`
9096
- `Repositories:Read`
9197

98+
**Note:** Bitbucket has deprecated app passwords. Use [API tokens](https://support.atlassian.com/bitbucket-cloud/docs/api-tokens/) instead by setting the ``BITBUCKET_TOKEN`` environment variable.
99+
92100
#### GitHub
93101

94102
- `repo`: Reading repositories, including private repositories
@@ -250,6 +258,14 @@ $ GITLAB_TOKEN=secret$token gitbackup -service gitlab -githost.url https://git.y
250258

251259
To backup all your Bitbucket repositories to the default backup directory (``$HOME/.gitbackup/``):
252260

261+
Using an API token (recommended):
262+
263+
```lang=bash
264+
$ BITBUCKET_USERNAME=username BITBUCKET_TOKEN=token gitbackup -service bitbucket
265+
```
266+
267+
Using an app password (deprecated, disabled after June 9, 2026):
268+
253269
```lang=bash
254270
$ BITBUCKET_USERNAME=username BITBUCKET_PASSWORD=password gitbackup -service bitbucket
255271
```

‎client.go‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,17 @@ func newBitbucketClient(gitHostURLParsed *url.URL) *bitbucket.Client {
179179
log.Fatal("BITBUCKET_USERNAME environment variable not set")
180180
}
181181

182-
bitbucketPassword := os.Getenv("BITBUCKET_PASSWORD")
182+
bitbucketPassword := os.Getenv("BITBUCKET_TOKEN")
183183
if bitbucketPassword == "" {
184-
log.Fatal("BITBUCKET_PASSWORD environment variable not set")
184+
bitbucketPassword = os.Getenv("BITBUCKET_PASSWORD")
185+
}
186+
if bitbucketPassword == "" {
187+
log.Fatal("BITBUCKET_TOKEN or BITBUCKET_PASSWORD environment variable must be set")
185188
}
186189

187190
gitHostToken = bitbucketPassword
188-
189191
client := bitbucket.NewBasicAuth(bitbucketUsername, bitbucketPassword)
192+
190193
if gitHostURLParsed != nil {
191194
client.SetApiBaseURL(*gitHostURLParsed)
192195
}

‎client_test.go‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"net/url"
5+
"os"
56
"testing"
67

78
forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2"
@@ -41,9 +42,12 @@ func TestNewClient(t *testing.T) {
4142
t.Errorf("Expected BaseURL to be: %v, Got: %v\n", expectedGitHostBaseURL, gotBaseURL)
4243
}
4344

44-
// Client for bitbucket.com
45+
// Client for bitbucket.com (basic auth via BITBUCKET_PASSWORD)
4546
client = newClient("bitbucket", "")
4647
client = client.(*bitbucket.Client)
48+
if gitHostToken != os.Getenv("BITBUCKET_PASSWORD") {
49+
t.Errorf("Expected gitHostToken to be BITBUCKET_PASSWORD, got: %v", gitHostToken)
50+
}
4751

4852
// Client for codeberg
4953
client = newClient("forgejo", "")
@@ -60,3 +64,23 @@ func TestNewClient(t *testing.T) {
6064
}
6165

6266
}
67+
68+
func TestNewBitbucketClientWithToken(t *testing.T) {
69+
setupRepositoryTests()
70+
defer teardownRepositoryTests()
71+
72+
// Set BITBUCKET_TOKEN and unset BITBUCKET_PASSWORD to test token auth path
73+
os.Setenv("BITBUCKET_TOKEN", "$$$randomtoken")
74+
os.Unsetenv("BITBUCKET_PASSWORD")
75+
defer os.Unsetenv("BITBUCKET_TOKEN")
76+
77+
client := newClient("bitbucket", "")
78+
if client == nil {
79+
t.Fatal("Expected non-nil bitbucket client")
80+
}
81+
_ = client.(*bitbucket.Client)
82+
83+
if gitHostToken != "$$$randomtoken" {
84+
t.Errorf("Expected gitHostToken to be BITBUCKET_TOKEN value, got: %v", gitHostToken)
85+
}
86+
}

0 commit comments

Comments
 (0)