Skip to content

fix(worker): replace Bitbucket Cloud user-repos endpoint removed by CHANGE-2770#1217

Merged
msukkari merged 4 commits into
mainfrom
msukkari/fix-bitbucket-cloud-change-2770
May 22, 2026
Merged

fix(worker): replace Bitbucket Cloud user-repos endpoint removed by CHANGE-2770#1217
msukkari merged 4 commits into
mainfrom
msukkari/fix-bitbucket-cloud-change-2770

Conversation

@msukkari
Copy link
Copy Markdown
Contributor

@msukkari msukkari commented May 21, 2026

Fixes SOU-1179

Summary

GET /2.0/user/permissions/repositories was removed by Atlassian's CHANGE-2770 and now returns 410 Gone, breaking Bitbucket Cloud account-driven permission sync.

There is no direct replacement. This PR rewrites getReposForAuthenticatedBitbucketCloudUser to the two-step pattern Atlassian recommends:

  1. GET /2.0/user/workspaces — list the workspaces the user belongs to.
  2. GET /2.0/repositories/{workspace}?role=member&q=is_private=true — list the private repos the user can see in each workspace.

Test plan

Verified against a live Bitbucket Cloud stack:

  • Account-driven sync completes successfully for workspace admins, members, and external collaborators with per-repo grants.
  • No over-granting: each user only gets AccountToRepoPermission rows for repos they actually have access to in Bitbucket.
  • Revoking a grant in Bitbucket propagates to a 404 in Sourcebot on the next sync tick; re-granting restores access.
  • Public-repo handling: visibility flips (public↔private) converge correctly once connection sync catches up.

🤖 Generated with Claude Code

…HANGE-2770

Atlassian removed GET /2.0/user/permissions/repositories on 2026-02-27
(CHANGE-2770) and the endpoint now returns HTTP 410 Gone for every
caller. Bitbucket Cloud account-driven permission sync was the only
caller of this endpoint, so it has been hard-failing on every cycle
since then.

Atlassian's stated guidance is that there is no direct replacement, and
recommends a two-step pattern instead:

  1. GET /2.0/user/workspaces — list the workspaces the user is a
     member of.
  2. GET /2.0/repositories/{workspace}?role=member — list the repos the
     user can see in each workspace.

`role=member` returns repos where the user has at least explicit read
access, including access inherited from workspace admin, project
membership, or group membership. Bitbucket enforces the filter
server-side, so a workspace admin gets the full workspace, while a user
with no grant on a repo gets nothing back for it — neither over-grants
nor under-grants.

Public-repo handling: the call is intentionally not filtered with
q=is_private=true. Sourcebot's read-side prisma extension already
short-circuits public repos to org-wide access, so an ACCOUNT_DRIVEN
row on a public repo is harmless overlay; but not filtering means that
during a public<->private visibility flip, the user never has a window
where they're missing an ACCOUNT_DRIVEN row for a repo they can see
upstream.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

Walkthrough

Switch Bitbucket Cloud repository enumeration away from the removed /user/permissions/repositories endpoint: fetch user workspaces, fetch repositories per workspace with role=member&q=is_private=true, aggregate repositories with UUIDs, and add a changelog entry documenting the fix.

Changes

Bitbucket Cloud Endpoint Migration

Layer / File(s) Summary
API migration and import cleanup
packages/backend/src/bitbucket.ts
Removed unused SchemaRepositoryPermission import and replaced getReposForAuthenticatedBitbucketCloudUser with a two-step flow: fetch /user/workspaces, then /repositories/{workspace}?role=member&q=is_private=true for each workspace, flatten results, and return repositories that include a non-null uuid.
Changelog documentation
CHANGELOG.md
Added an Unreleased "Fixed" entry documenting the EE Bitbucket Cloud account-driven permission sync fix after Atlassian removed the /user/permissions/repositories endpoint, referencing this PR (#1217).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • sourcebot-dev/sourcebot#1215: Modifies packages/backend/src/bitbucket.ts with overlapping changes to Bitbucket repo/permission discovery and pagination/auth handling.
  • sourcebot-dev/sourcebot#925: Introduced prior getReposForAuthenticatedBitbucketCloudUser changes used for Bitbucket Cloud permission syncing.

Suggested reviewers

  • brendan-kellam
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: replacing a removed Bitbucket Cloud endpoint with an alternative approach as required by CHANGE-2770.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch msukkari/fix-bitbucket-cloud-change-2770

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/backend/src/bitbucket.ts (1)

711-730: 💤 Low value

Query parameter merge order could allow role to be overwritten.

On line 722, { role: 'member', ...query } places role first, which means if query ever contains a role key (e.g., from a malformed pagination URL), it would override the intended member value. While Bitbucket pagination URLs shouldn't include query filters, reversing the order is safer and more defensive.

Suggested fix
-                        query: { role: 'member', ...query },
+                        query: { ...query, role: 'member' },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/bitbucket.ts` around lines 711 - 730, The merge order
for query params in the getPaginatedCloud call can allow an incoming query.role
to override the intended role='member'; update the params merge so the
pagination query is spread first and then role: 'member' is applied (i.e., use {
...query, role: 'member' }) inside the client.apiClient.GET call used in
reposByWorkspace/fetchWithRetry/getPaginatedCloud so that role is always
enforced regardless of any role in query.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/backend/src/bitbucket.ts`:
- Around line 711-730: The merge order for query params in the getPaginatedCloud
call can allow an incoming query.role to override the intended role='member';
update the params merge so the pagination query is spread first and then role:
'member' is applied (i.e., use { ...query, role: 'member' }) inside the
client.apiClient.GET call used in
reposByWorkspace/fetchWithRetry/getPaginatedCloud so that role is always
enforced regardless of any role in query.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8256326c-16e1-4014-b879-c8aa2e24beb3

📥 Commits

Reviewing files that changed from the base of the PR and between d0f7c18 and 8b65564.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • packages/backend/src/bitbucket.ts

msukkari and others added 2 commits May 21, 2026 16:39
…t-cloud-change-2770

# Conflicts:
#	CHANGELOG.md
Matches the GitHub and GitLab branches of accountPermissionSyncer,
which both pass visibility='private' for the same reason: public repos
are gated by the read-side prisma filter via isPublic, so no
AccountToRepoPermission row is needed for them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/backend/src/bitbucket.ts (1)

689-704: ⚡ Quick win

Spread operator ordering may allow pagination params to override filters.

The pattern { role: 'member', q: 'is_private=true', ...query } places explicit params before the spread, meaning any same-named keys in query (from pagination URLs) would override them. This is inconsistent with cloudGetReposForProjects (lines 283-286) which correctly places explicit params after the spread.

♻️ Suggested fix
                     params: {
                         path: { workspace },
-                        query: { role: 'member', q: 'is_private=true', ...query },
+                        query: { ...query, role: 'member', q: 'is_private=true' },
                     },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/src/bitbucket.ts` around lines 689 - 704, The params spread
currently places pagination query after explicit filters in the
getPaginatedCloud call for reposByWorkspace, allowing pagination keys to
overwrite filters; update the params ordering in the client.apiClient.GET call
inside getPaginatedCloud (used by fetchWithRetry for reposByWorkspace) so that
the spread comes first and explicit filters come after (i.e., use ...query then
role: 'member', q: 'is_private=true') to ensure the explicit filters cannot be
overridden by pagination parameters.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/backend/src/bitbucket.ts`:
- Around line 689-704: The params spread currently places pagination query after
explicit filters in the getPaginatedCloud call for reposByWorkspace, allowing
pagination keys to overwrite filters; update the params ordering in the
client.apiClient.GET call inside getPaginatedCloud (used by fetchWithRetry for
reposByWorkspace) so that the spread comes first and explicit filters come after
(i.e., use ...query then role: 'member', q: 'is_private=true') to ensure the
explicit filters cannot be overridden by pagination parameters.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a54911b9-8dfc-40a8-90e7-de8c84d4757d

📥 Commits

Reviewing files that changed from the base of the PR and between 9c3e6f2 and c810e7c.

📒 Files selected for processing (1)
  • packages/backend/src/bitbucket.ts

@msukkari msukkari merged commit 3851c66 into main May 22, 2026
11 checks passed
@msukkari msukkari deleted the msukkari/fix-bitbucket-cloud-change-2770 branch May 22, 2026 00:18
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