Skip to content

fix(bitbucket): replace retired CHANGE-3022 API endpoints#883

Open
drazisil-codecov wants to merge 5 commits intomainfrom
fix/bitbucket-repos-permissions-api
Open

fix(bitbucket): replace retired CHANGE-3022 API endpoints#883
drazisil-codecov wants to merge 5 commits intomainfrom
fix/bitbucket-repos-permissions-api

Conversation

@drazisil-codecov
Copy link
Copy Markdown
Contributor

@drazisil-codecov drazisil-codecov commented Apr 21, 2026

Summary

Bitbucket retired three endpoints on 2026-04-14 (CHANGE-3022), all returning 410. This PR replaces them with current equivalents and removes one deprecated call entirely.

Dead endpoints fixed (410 → working):

  • GET /user/permissions/repositories → removed (list_permissions) and replaced (get_authenticated)
  • GET /workspaces/{uuid}/permissionsGET /workspaces/{slug}/permissions — was passing UUID where slug is required, causing 404s in get_is_admin

Changes:

  • get_is_admin: use self.data["owner"]["username"] (slug) instead of service_id (UUID)
  • get_authenticated: GET /repositories/{workspace}?role=contributor&q=full_name="..." — presence in results = write access
  • list_permissions(): deleted entirely (see below)

Why list_permissions() was dropped, not replaced:

list_permissions called GET /repositories?role=member to discover repos where a user had collaborator access outside of workspace membership. That endpoint is itself deprecated per the Bitbucket swagger spec — the recommended replacement is the workspace-scoped GET /repositories/{workspace}.

Since list_repos already calls GET /repositories/{workspace} for every workspace slug returned by list_teams(), and Bitbucket is retiring the "repo-level collaborator without workspace membership" access model, list_permissions was redundant. Dropping it means the sync path uses only non-deprecated workspace-scoped calls.

Swagger spec used for response shape verification: https://dac-static.atlassian.com/cloud/bitbucket/swagger.v3.json

Test plan

  • 71 unit + integration tests pass locally
  • Tested end-to-end locally via real Bitbucket OAuth flow with two accounts:
    • sync_repos calls only workspace-scoped endpoints (GET /repositories/{workspace})
    • Non-member workspaces are correctly skipped — never queried
    • A repo added to a workspace mid-session was picked up on the next sync
    • get_is_admin correctly returns 403 for a user who is a workspace member but not an owner
    • get_authenticated correctly denies edit access for a user not in the workspace
    • A user who is a member of a workspace cannot see private repos in that workspace they do not have explicit access to — Bitbucket's API only returns repos the requesting user can access, so workspace membership alone is not sufficient
  • Verify sync_repos / sync_teams no longer produce 410s in Sentry after deploy

🤖 Generated with Claude Code

- list_permissions: GET /user/permissions/repositories → GET /repositories?role=member
- get_authenticated: GET /user/permissions/repositories → GET /repositories?role=contributor
- get_is_admin: workspace UUID → slug in /workspaces/{slug}/permissions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@sentry
Copy link
Copy Markdown
Contributor

sentry Bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.25%. Comparing base (7cb0beb) to head (d237ed7).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #883      +/-   ##
==========================================
- Coverage   92.26%   92.25%   -0.01%     
==========================================
  Files        1307     1307              
  Lines       48014    47984      -30     
  Branches     1632     1625       -7     
==========================================
- Hits        44298    44270      -28     
- Misses       3407     3408       +1     
+ Partials      309      306       -3     
Flag Coverage Δ
apiunit 96.35% <ø> (ø)
sharedintegration 36.97% <0.00%> (+0.06%) ⬆️
sharedunit 84.89% <100.00%> (-0.02%) ⬇️
workerintegration 58.54% <ø> (ø)
workerunit 90.39% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@codecov-notifications
Copy link
Copy Markdown

codecov-notifications Bot commented Apr 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Apr 21, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing fix/bitbucket-repos-permissions-api (d237ed7) with main (b3cda5a)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (7cb0beb) during the generation of this report, so b3cda5a was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

GET /user/permissions/repositories (the endpoint list_permissions called)
was retired by Bitbucket CHANGE-3022. The replacement GET /repositories?role=member
is itself deprecated — Bitbucket's new access model is workspace-based.

list_permissions existed to discover repos where a user had collaborator access
outside of workspace membership. Since Bitbucket is retiring that collaborator
model, list_teams() workspace slugs are sufficient — the per-workspace
GET /repositories/{workspace} calls in list_repos already cover everything.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@drazisil-codecov
Copy link
Copy Markdown
Contributor Author

Follow-up to initial fix: removed list_permissions() from the sync path entirely.

list_permissions() originally called GET /user/permissions/repositories (retired, 410) to discover repos where a user had collaborator access outside of workspace membership — cases where the user wasn't a workspace member but had repo-level write access granted directly.

The initial fix in this PR replaced that with GET /repositories?role=member, but that endpoint is itself deprecated by Bitbucket — they recommend workspace-scoped calls instead.

The root cause is that Bitbucket's new access model is workspace-based. The "repo-level collaborator without workspace membership" pattern is the deprecated collaborator role they're phasing out. Because of this:

  • list_teams() already returns all workspace slugs the user belongs to
  • The list_repos loop already calls GET /repositories/{workspace} (workspace-scoped, non-deprecated) for each one
  • list_permissions() was adding owner slugs to that set, but those slugs were already covered by workspace membership in practice

So we dropped list_permissions() and the deprecated cross-workspace call with it. The per-workspace GET /repositories/{workspace} calls in the loop are the correct non-deprecated path.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f3232d3. Configure here.

Comment thread libs/shared/shared/torngit/bitbucket.py Outdated
drazisil-codecov and others added 3 commits April 21, 2026 16:45
GET /repositories?role=contributor is deprecated per the swagger spec.
Replace with GET /repositories/{workspace}?role=contributor which is
the non-deprecated workspace-scoped equivalent and returns the same shape.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
repos_to_log was always [] after list_permissions was removed.
Simplify _get_teams_and_username_to_list to return usernames directly
and drop the empty list from callers and log statements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /repositories mock was left over from list_permissions removal.
Nothing calls that URL anymore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
async def get_is_admin(self, user, token=None):
user_uuid = "{" + user["service_id"] + "}"
workspace_slug = self.data["owner"]["service_id"]
workspace_slug = self.data["owner"]["username"]
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.

Just checking: do we receive webhooks on name changes? I want to make sure that users who rename their org/slug (if that's possible) will have it reflected in Codecov.

Comment on lines -400 to -415
"""
Lists all repositories a user is part of.
*Note:
Bitbucket API V2 does not provide a dedicated endpoint which returns all repos a user is part of.
It provides however, an endpoint to get all the repos a user is part of from an specific org or user.
Endpoint to list repos from an specific user:
- /repositories/{username}
In order to get all the repositories a user is part of, we first need to get all the orgs and repo owners
- Orgs/Teams can be obtained using the 'list_teams' method
- Usernames of repo owners is a bit tricky since Bitbucket doesnt provide an endpoint for this
- Solution:
Use the 'list_permissions' method to get all repo permissions and exctract owner's username
from the repository 'full_name' attribute
Once we have all orgs/teams and owner's usernames we should call "/repositories/{username}" endpoint
for each of the orgs/teams and owner's usernames.
"""
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.

I feel like we're losing some potential useful context with the removal of this docstring. Can we update it to better reflect what it's actually doing?

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.

2 participants