Skip to content

Commit 6510f5a

Browse files
committed
bitbucket: require BITBUCKET_WORKSPACES, prefer BITBUCKET_EMAIL
Atlassian removed the cross-workspace listing endpoints (/2.0/workspaces and /2.0/user/permissions/workspaces) on April 14, 2026 under CHANGE-2770 / CHANGE-3022. The Bitbucket backup path has been failing with HTTP 410 Gone since then, because getBitbucketRepositories() called client.Workspaces.List() to discover which workspaces to iterate. There is no supported replacement that enumerates a user's workspaces; the caller must now know each workspace slug out-of-band. Require the user to provide them via a new BITBUCKET_WORKSPACES env var (comma-separated). Repository listing per workspace still works via the unchanged /2.0/repositories/{workspace} endpoint, so the rest of the flow is unaffected. Also rename the credential username variable to make it clearer that for Atlassian API tokens this must be the account email, not the legacy Bitbucket username. Accept BITBUCKET_EMAIL in preference to BITBUCKET_USERNAME; keep BITBUCKET_USERNAME as a fallback so existing setups using app passwords (still supported until July 28, 2026) keep working. Fixes #223
1 parent d7e0272 commit 6510f5a

3 files changed

Lines changed: 38 additions & 14 deletions

File tree

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ SSH cloning on Windows requires your SSH key to be accessible from WSL2 or Docke
167167

168168
``gitbackup`` requires a [GitHub API access token](https://github.com/blog/1509-personal-api-tokens) for
169169
backing up GitHub repositories, a [GitLab personal access token](https://gitlab.com/-/user_settings/personal_access_tokens)
170-
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
170+
for GitLab repositories, an Atlassian account email plus an [API token](https://support.atlassian.com/bitbucket-cloud/docs/api-tokens/) (or [app password](https://bitbucket.org/account/settings/app-passwords/)) for
171171
Bitbucket repositories, or a [Forgejo access token][https://docs.codeberg.org/advanced/access-token/] for Forgejo.
172172

173173
You can supply the tokens to ``gitbackup`` using ``GITHUB_TOKEN``, ``GITLAB_TOKEN``, or ``FORGEJO_TOKEN`` environment
174-
variables respectively, and the Bitbucket credentials with ``BITBUCKET_USERNAME`` and either ``BITBUCKET_TOKEN`` or ``BITBUCKET_PASSWORD``.
174+
variables respectively, and the Bitbucket credentials with ``BITBUCKET_EMAIL`` (or ``BITBUCKET_USERNAME`` for legacy app-password setups) and either ``BITBUCKET_TOKEN`` or ``BITBUCKET_PASSWORD``. Bitbucket additionally requires ``BITBUCKET_WORKSPACES`` (comma-separated workspace slugs) because Atlassian removed the cross-workspace listing APIs on April 14, 2026.
175175

176176
### GitHub Specific oAuth App Flow
177177

@@ -435,18 +435,22 @@ $ GITLAB_TOKEN=secret$token gitbackup -service gitlab -githost.url https://git.y
435435

436436
#### Backing up your Bitbucket repositories
437437

438+
Atlassian removed the cross-workspace listing endpoints on April 14, 2026.
439+
There is no supported way to enumerate the workspaces a user belongs to programmatically, so you must list them
440+
explicitly via ``BITBUCKET_WORKSPACES``.
441+
438442
To backup all your Bitbucket repositories to the default backup directory (``$HOME/.gitbackup/``):
439443

440444
Using an API token (recommended):
441445

442446
```lang=bash
443-
$ BITBUCKET_USERNAME=<your atlassian email> BITBUCKET_TOKEN=token gitbackup -service bitbucket
447+
$ BITBUCKET_EMAIL=<your atlassian email> BITBUCKET_TOKEN=token BITBUCKET_WORKSPACES=ws1,ws2 gitbackup -service bitbucket
444448
```
445449

446450
Using an app password (deprecated, disabled after June 9, 2026):
447451

448452
```lang=bash
449-
$ BITBUCKET_USERNAME=username BITBUCKET_PASSWORD=password gitbackup -service bitbucket
453+
$ BITBUCKET_USERNAME=username BITBUCKET_PASSWORD=password BITBUCKET_WORKSPACES=ws1,ws2 gitbackup -service bitbucket
450454
```
451455

452456
#### Backing up your Forgejo repositories

bitbucket.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"log"
5+
"os"
46
"strings"
57

68
bitbucket "github.com/ktrysmt/go-bitbucket"
@@ -11,15 +13,26 @@ func getBitbucketRepositories(
1113
ignoreFork bool,
1214
) ([]*Repository, error) {
1315

16+
// As of April 14, 2026 Atlassian removed the cross-workspace listing
17+
// endpoints (/2.0/workspaces and /2.0/user/permissions/workspaces) under
18+
// changelog entries CHANGE-2770 / CHANGE-3022. There is no supported way
19+
// to enumerate the workspaces a user belongs to programmatically. The
20+
// caller must supply the workspace slugs via BITBUCKET_WORKSPACES
21+
// (comma-separated).
22+
workspacesEnv := os.Getenv("BITBUCKET_WORKSPACES")
23+
if workspacesEnv == "" {
24+
log.Fatal("BITBUCKET_WORKSPACES environment variable not set (comma-separated workspace slugs)")
25+
}
26+
1427
var repositories []*Repository
1528

16-
resp, err := client.Workspaces.List()
17-
if err != nil {
18-
return nil, err
19-
}
29+
for _, slug := range strings.Split(workspacesEnv, ",") {
30+
slug = strings.TrimSpace(slug)
31+
if slug == "" {
32+
continue
33+
}
2034

21-
for _, workspace := range resp.Workspaces {
22-
options := &bitbucket.RepositoriesOptions{Owner: workspace.Slug}
35+
options := &bitbucket.RepositoriesOptions{Owner: slug}
2336

2437
resp, err := client.Repositories.ListForAccount(options)
2538
if err != nil {

client.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,16 @@ func newGitLabClient(gitHostURLParsed *url.URL) *gitlab.Client {
171171

172172
// newBitbucketClient creates a new Bitbucket client
173173
func newBitbucketClient(gitHostURLParsed *url.URL) *bitbucket.Client {
174-
bitbucketUsername := os.Getenv("BITBUCKET_USERNAME")
175-
if bitbucketUsername == "" {
176-
log.Fatal("BITBUCKET_USERNAME environment variable not set")
174+
// Atlassian API tokens are scoped to the Atlassian account, which is
175+
// identified by an email address rather than a Bitbucket username.
176+
// Prefer BITBUCKET_EMAIL for clarity and fall back to BITBUCKET_USERNAME
177+
// for backwards compatibility with legacy app-password setups.
178+
bitbucketEmailOrUsername := os.Getenv("BITBUCKET_EMAIL")
179+
if bitbucketEmailOrUsername == "" {
180+
bitbucketEmailOrUsername = os.Getenv("BITBUCKET_USERNAME")
181+
}
182+
if bitbucketEmailOrUsername == "" {
183+
log.Fatal("BITBUCKET_EMAIL or BITBUCKET_USERNAME environment variable not set")
177184
}
178185

179186
bitbucketPasswordOrToken := os.Getenv("BITBUCKET_TOKEN")
@@ -185,7 +192,7 @@ func newBitbucketClient(gitHostURLParsed *url.URL) *bitbucket.Client {
185192
}
186193

187194
gitHostToken = bitbucketPasswordOrToken
188-
client, err := bitbucket.NewBasicAuth(bitbucketUsername, bitbucketPasswordOrToken)
195+
client, err := bitbucket.NewBasicAuth(bitbucketEmailOrUsername, bitbucketPasswordOrToken)
189196
if err != nil {
190197
log.Fatalf("Error creating Bitbucket client: %v", err)
191198
}

0 commit comments

Comments
 (0)