From e8de7acb99c0cd1199a9b48e11cd0999adcdb49a Mon Sep 17 00:00:00 2001 From: Rachel Dowavic Date: Fri, 20 Feb 2026 22:26:24 +1100 Subject: [PATCH] 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 --- README.md | 22 +++++++++++++++++++--- client.go | 9 ++++++--- client_test.go | 21 +++++++++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6985a2a..2671507 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,11 @@ If you are on MacOS, a community member has created a [Homebrew formula](https:/ ``gitbackup`` requires a [GitHub API access token](https://github.com/blog/1509-personal-api-tokens) for backing up GitHub repositories, a [GitLab personal access token](https://gitlab.com/profile/personal_access_tokens) -for GitLab repositories, a username and [app password](https://bitbucket.org/account/settings/app-passwords/) for +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 Bitbucket repositories, or a [Forgejo access token][https://docs.codeberg.org/advanced/access-token/] for Forgejo. You can supply the tokens to ``gitbackup`` using ``GITHUB_TOKEN``, ``GITLAB_TOKEN``, or ``FORGEJO_TOKEN`` environment -variables respectively, and the Bitbucket credentials with ``BITBUCKET_USERNAME`` and ``BITBUCKET_PASSWORD``. +variables respectively, and the Bitbucket credentials with ``BITBUCKET_USERNAME`` and either ``BITBUCKET_TOKEN`` or ``BITBUCKET_PASSWORD``. ### GitHub Specific oAuth App Flow @@ -84,11 +84,19 @@ time you run it, it will ask you for the keyring password and retrieve the token #### Bitbucket -For the App password, the following permissions are required: +**API tokens** (recommended): + +- `read:user:bitbucket` +- `read:workspace:bitbucket` +- `read:repository:bitbucket` + +**App passwords** (deprecated, disabled after June 9, 2026): - `Account:Read` - `Repositories:Read` +**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. + #### GitHub - `repo`: Reading repositories, including private repositories @@ -250,6 +258,14 @@ $ GITLAB_TOKEN=secret$token gitbackup -service gitlab -githost.url https://git.y To backup all your Bitbucket repositories to the default backup directory (``$HOME/.gitbackup/``): +Using an API token (recommended): + +```lang=bash +$ BITBUCKET_USERNAME=username BITBUCKET_TOKEN=token gitbackup -service bitbucket +``` + +Using an app password (deprecated, disabled after June 9, 2026): + ```lang=bash $ BITBUCKET_USERNAME=username BITBUCKET_PASSWORD=password gitbackup -service bitbucket ``` diff --git a/client.go b/client.go index 7ceaa64..e778594 100644 --- a/client.go +++ b/client.go @@ -179,14 +179,17 @@ func newBitbucketClient(gitHostURLParsed *url.URL) *bitbucket.Client { log.Fatal("BITBUCKET_USERNAME environment variable not set") } - bitbucketPassword := os.Getenv("BITBUCKET_PASSWORD") + bitbucketPassword := os.Getenv("BITBUCKET_TOKEN") if bitbucketPassword == "" { - log.Fatal("BITBUCKET_PASSWORD environment variable not set") + bitbucketPassword = os.Getenv("BITBUCKET_PASSWORD") + } + if bitbucketPassword == "" { + log.Fatal("BITBUCKET_TOKEN or BITBUCKET_PASSWORD environment variable must be set") } gitHostToken = bitbucketPassword - client := bitbucket.NewBasicAuth(bitbucketUsername, bitbucketPassword) + if gitHostURLParsed != nil { client.SetApiBaseURL(*gitHostURLParsed) } diff --git a/client_test.go b/client_test.go index 28d8baf..539c9a6 100644 --- a/client_test.go +++ b/client_test.go @@ -2,6 +2,7 @@ package main import ( "net/url" + "os" "testing" forgejo "codeberg.org/mvdkleijn/forgejo-sdk/forgejo/v2" @@ -60,3 +61,23 @@ func TestNewClient(t *testing.T) { } } + +func TestNewBitbucketClientWithToken(t *testing.T) { + setupRepositoryTests() + defer teardownRepositoryTests() + + // Set BITBUCKET_TOKEN and unset BITBUCKET_PASSWORD to test token auth path + os.Setenv("BITBUCKET_TOKEN", "$$$randomtoken") + os.Unsetenv("BITBUCKET_PASSWORD") + defer os.Unsetenv("BITBUCKET_TOKEN") + + client := newClient("bitbucket", "") + if client == nil { + t.Fatal("Expected non-nil bitbucket client") + } + _ = client.(*bitbucket.Client) + + if gitHostToken != "$$$randomtoken" { + t.Errorf("Expected gitHostToken to be BITBUCKET_TOKEN value, got: %v", gitHostToken) + } +}