Skip to content

feat(bitbucket): supports cloud and server APIs#11052

Merged
malhotra5 merged 16 commits intoOpenHands:mainfrom
phymbert:phymbert/feat/bitbucket/server
Mar 3, 2026
Merged

feat(bitbucket): supports cloud and server APIs#11052
malhotra5 merged 16 commits intoOpenHands:mainfrom
phymbert:phymbert/feat/bitbucket/server

Conversation

@phymbert
Copy link
Copy Markdown
Contributor

Summary

This PR introduces a first working version of Bitbucket Server REST API 1.0 support, in addition to the existing Bitbucket Cloud integration.

It allows OpenHands to work with self-hosted Bitbucket instances that expose the older 1.0 REST endpoints, expanding compatibility beyond just cloud users.

Motivation

Most organizations running Bitbucket use the on-premise edition (Bitbucket Server / Data Center) instead of the cloud service. Supporting the Bitbucket Server REST API 1.0 ensures OpenHands can integrate with these enterprise setups.

Notes

  • This is an initial implementation; it will be updated and refined once #11051 is merged.
  • Current focus is on getting a minimal working version so that contributors using Bitbucket Server can start testing.

Thanks

Excited to contribute this — first version working! Big thanks to the community for the support 🙏

@phymbert
Copy link
Copy Markdown
Contributor Author

For the archive:
Bitbucket data-center-end-of-life

But AFAICT no one knows what will be agentic coding in 3 years.

I am also fine to simply close this feature request if it does not align with the community needs.

@phymbert
Copy link
Copy Markdown
Contributor Author

phymbert commented Oct 1, 2025

How I can help to get feedback on this feature ?

I can understand it's enterprise oriented, but is not the open-source spirit of openhands ?

Without this I can't simply use bitbucket.

Thanks

Comment thread frontend/src/routes/git-settings.tsx Outdated
@phymbert phymbert requested a review from rbren October 5, 2025 16:07
@phymbert
Copy link
Copy Markdown
Contributor Author

Kindly request to review this

@mamoodi mamoodi removed their request for review October 22, 2025 11:17
@malhotra5
Copy link
Copy Markdown
Collaborator

Hi! Just wanted to note that I'm getting around to reviewing this PR. Just so we're on the same page, there's a few action items in order to make progress on this PR

  1. there's some stale changes that I can help clean out (we've made some larger architectural changes in the last month to the codebase)
  2. we're look into the PR Provide httpx default context for OS-provided certs #11505 to help unblock SSL context issues
  3. we'd like to validate the final changes and may request help from a community member for a QA. Alternatively when the time comes I'd be happy to hop on a call where we can walk through a quick demo

LMK if this sounds good!

@mamoodi mamoodi requested a review from malhotra5 November 3, 2025 18:16
@raymyers
Copy link
Copy Markdown
Contributor

This looks like a quality contribution and within a reasonable scope. I believe we can unblock it now.

As @malhotra5 mentioned, one of hesitations has been that we do not have BitBucket Server setup ourselves to test this ongoing. We've decided to handle integrations like that by documenting them as "experimental" for now.

Therefore I plan to resolve the merge conflicts and integrate this. #11505 has also resolved the ssh context blocker.

@mamoodi mamoodi requested review from raymyers and removed request for rbren December 5, 2025 14:12
@raymyers raymyers self-assigned this Dec 22, 2025
@raymyers
Copy link
Copy Markdown
Contributor

Since the updated docs have moved, I've copied them over to this PR on the docs site.

OpenHands/docs#226

Comment on lines +167 to +171
if not user_id:
raise AuthenticationError(
'User ID is required for Bitbucket Server access'
)
url = f'{self.BASE_URL}/users/{user_id}'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is preventing testing with PAT (without a username and Bearer in header) which I think majority of bitbucket server users are using.

A second issue is related to the url used for basic auth. It takes the user's slug which is sometimes different than what they login in with (email for login, slug doesn't have @ sign for example) and query will error out in this case. You can filter based on login name though.

Here is what I came up with that allows both PAT and basic auth logins to work for me so I can then test the remaining features of PR.

--- a/openhands/integrations/bitbucket/service/base.py
+++ b/openhands/integrations/bitbucket/service/base.py
@@ -163,14 +163,22 @@ class BitBucketMixinBase(BaseGitService, HTTPClient):
     async def get_user(self) -> User:
         """Get the authenticated user's information."""
         if self._is_server:
-            user_id = getattr(self, 'user_id', None)
-            if not user_id:
-                raise AuthenticationError(
-                    'User ID is required for Bitbucket Server access'
-                )
-            url = f'{self.BASE_URL}/users/{user_id}'
-            data, _ = await self._make_request(url)
-            links = data.get('links', {})
+            token_value = self.token.get_secret_value()
+
+            # PAT auth - return empty user since we can't determine identity
+            if ':' not in token_value:
+                return User(id='', login='', avatar_url='', name=None, email=None)
+
+            # Basic auth - extract username and query users API to get slug
+            name = token_value.split(':', 1)[0]
+            users_url = f'{self.BASE_URL}/users'
+            data, _ = await self._make_request(users_url, {'filter': name})
+            users = data.get('values', [])
+            if not users:
+                raise AuthenticationError(f'User not found: {name}')
+
+            user_data = users[0]
+            links = user_data.get('links', {})
             avatar = ''
             if isinstance(links, dict):
                 self_links = links.get('self') or []
@@ -179,8 +187,8 @@ class BitBucketMixinBase(BaseGitService, HTTPClient):
             display_name = data.get('displayName')
             email = data.get('emailAddress')
             return User(
-                id=str(data.get('id') or data.get('slug') or user_id),
-                login=data.get('name') or user_id,
+                id=str(user_data.get('id') or user_data.get('slug') or name),
+                login=user_data.get('name') or name,
                 avatar_url=avatar,
                 name=display_name,
                 email=email,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that, @cbagwell. I've added those changes and credited you.

aivong-openhands added a commit to aivong-openhands/OpenHands that referenced this pull request Feb 9, 2026
…TBUCKET_MODE env and toml entry

Cherry-picked from PR OpenHands#11052 commit 59314f4
aivong-openhands added a commit to aivong-openhands/OpenHands that referenced this pull request Feb 9, 2026
aivong-openhands added a commit to aivong-openhands/OpenHands that referenced this pull request Feb 9, 2026
Cherry-picked from PR OpenHands#11052 commit e842f56
aivong-openhands added a commit to aivong-openhands/OpenHands that referenced this pull request Feb 9, 2026
aivong-openhands added a commit to aivong-openhands/OpenHands that referenced this pull request Feb 9, 2026
@cbagwell
Copy link
Copy Markdown
Contributor

I have a new patch and so I've pushed my branch for easier reference: https://github.com/cbagwell/OpenHands/tree/pr11052-plus-cbagwell-fixes

Its based on this branch plus:

  • an additional merge from main from 2026/02/18.
  • the patches I posted in above comments but as commits now.
  • A new patch to fixes issues with the dropdown repo list.
    • There were a few pagination issues for both the full PROJECT/REPO lists and for filtering to single project such as typing "PROJ/".

@jlav
Copy link
Copy Markdown
Contributor

jlav commented Feb 25, 2026

@cbagwell Thanks for your continued updates on this integration!

We've stood up a Bitbucket Data Center for testing and have been able to validate.

One thought though - the cloud and server APIs are divergent enough that I think it makes sense to break Data Center out as a separate integration and to keep the two isolated.

I experimented with that idea on this branch which is a refactor of this PR. It incorporates @cbagwell's comments as well - I'd need to double check if it has all the latest contributions from https://github.com/cbagwell/OpenHands/tree/pr11052-plus-cbagwell-fixes.

You can see that approach results in a much larger changeset, but it does keep the integrations distinct from one another. Do you have any strong opinions one way or the other?

@raymyers Wanted to tag you in for an opinion as well since we briefly discussed this, and I don't want to make a firm call here since I'm not yet a maintainer.

@raymyers
Copy link
Copy Markdown
Contributor

the cloud and server APIs are divergent enough that I think it makes sense to break Data Center out as a separate integration and to keep the two isolated.

I agree with this. The only downside is that then bitbucket with no qualifier is wll implicitly mean "bitbucket cloud", because changing the existing name to would probably be disruptive. But I think docs and comments could make it clear enough, and I expect it to be a bit less confusing than the "mode" option.

The main challenge here has been that we know how to test this and safely integrate it. So as long as we can do that, I support the split.

@jlav
Copy link
Copy Markdown
Contributor

jlav commented Feb 26, 2026

Okay, thank you. I'll get that pulled into this PR tomorrow morning. Plan to build on top of the existing work (from 1079dc0) so that the original authorship of all the contributors in this PR is preserved.

@cbagwell
Copy link
Copy Markdown
Contributor

@jlav I think I see all the things accounted for in yours. Once you have it in a PR, it will be easy for me to load on my local machine and confirm.

I'm also glad to see that that you addressed the skills issue.

@jlav
Copy link
Copy Markdown
Contributor

jlav commented Feb 27, 2026

@cbagwell I ported those changes over, so you should be able to test it out again if you'd like.

You'll find some new inputs on the /settings/integrations page to add your host and token:
image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't super obvious to me, but it seems in V1 runtimes that skills are loaded from this repository, and they are not loaded from this directory.

I've put this skill file here for consistency, and it needs to be ported over to that repo. To validate for now, I manually added this skill to a conversation, and it works as expected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jlav Do you have plans to port over to that repo? I would be happy to create that PR for you so you can concentrate on new code.

@jlav
Copy link
Copy Markdown
Contributor

jlav commented Feb 27, 2026

Here's a video demonstrating the integration works:

Screen.Recording.2026-02-27.at.12.57.56.PM.mov

@jlav
Copy link
Copy Markdown
Contributor

jlav commented Feb 27, 2026

Alright, I did one final pass over this PR and I think it's in a good spot. @tofarr or @malhotra5, mind taking a look when you get the chance? There's a customer waiting for this integration.

@cbagwell
Copy link
Copy Markdown
Contributor

cbagwell commented Feb 27, 2026

@jlav I tested 27317d3 and here is my feedback but TLDR is LGTM:

  • Was able to use repo selection with no major issues. Branch selection as well.
  • I concentrated on the buttons at bottom since they were not in your recording. Repo/Branch buttons work. Pull/Push/Create PR all work.
  • Pre-existing nit with both cloud/datacenter: When providing bitbucket URL, it would be nice to look for and strip off http/https prefixes like gitlab provider does. When starting fresh I tend to copy URL from web browser and paste in. The result is a error message that makes you think your token is bad instead of the URL. Example in screen clearly shows no https:// though so its documented how it should be.
  • Pagination of repos feels a little off. I have 100's of projects and 100's of repos per project so it stresses it. My initial debug didn't turn anything up and I don't want that to hold this up at all.

Feels like its limiting itself to the first page of project results still but I have no proof of that yet. If I type nothing it feels like its returning all project+repos (hard to confirm with such large #) via infinite scrolling. If I type PROJECT/, it correctly lists all repos within projects. The issue I see is if I filter by typing "foo", it returns an incomplete list back and hard to describe how it chose the subset.

Copy link
Copy Markdown
Collaborator

@malhotra5 malhotra5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I haven't tested this myself as its been tested by multiple others folks

The code seems to follow our existing patterns, it would be good to double check whether we can have pagination for repo related actions.

@malhotra5 malhotra5 merged commit e7934ea into OpenHands:main Mar 3, 2026
17 checks passed
@raymyers
Copy link
Copy Markdown
Contributor

raymyers commented Mar 3, 2026

Thank you @phymbert for this substantial contribution as well as @cbagwell, @jlav and others for all the work refining and testing. I'm glad were able to get this revived after the V1 transition delayed things.

@enyst
Copy link
Copy Markdown
Collaborator

enyst commented Mar 3, 2026

Thank you all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants