feat(bitbucket): supports cloud and server APIs#11052
feat(bitbucket): supports cloud and server APIs#11052malhotra5 merged 16 commits intoOpenHands:mainfrom
Conversation
|
For the archive: 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. |
|
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 |
…TBUCKET_MODE env and toml entry
Clean doc
|
Kindly request to review this |
|
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
LMK if this sounds good! |
|
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. |
|
Since the updated docs have moved, I've copied them over to this PR on the docs site. |
| if not user_id: | ||
| raise AuthenticationError( | ||
| 'User ID is required for Bitbucket Server access' | ||
| ) | ||
| url = f'{self.BASE_URL}/users/{user_id}' |
There was a problem hiding this comment.
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,
There was a problem hiding this comment.
Thanks for that, @cbagwell. I've added those changes and credited you.
…TBUCKET_MODE env and toml entry Cherry-picked from PR OpenHands#11052 commit 59314f4
Cherry-picked from PR OpenHands#11052 commit ab1dbea
Cherry-picked from PR OpenHands#11052 commit e842f56
Cherry-picked from PR OpenHands#11052 commit b78d8c2
Cherry-picked from PR OpenHands#11052 commit 1079dc0
|
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:
|
|
@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. |
I agree with this. The only downside is that then 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. |
|
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. |
|
@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. |
|
@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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
@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.
|
Here's a video demonstrating the integration works: Screen.Recording.2026-02-27.at.12.57.56.PM.mov |
|
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. |
|
@jlav I tested 27317d3 and here is my feedback but TLDR is LGTM:
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. |
|
Thank you all! |

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.0REST 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
Thanks
Excited to contribute this — first version working! Big thanks to the community for the support 🙏