Skip to content

Commit 3851c66

Browse files
authored
fix(worker): replace Bitbucket Cloud user-repos endpoint removed by CHANGE-2770 (#1217)
1 parent d0f7c18 commit 3851c66

2 files changed

Lines changed: 44 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111
- Fixed issue where repo permissions could go stale when authentication or token refresh related errors occured. [#1215](https://github.com/sourcebot-dev/sourcebot/pull/1215)
1212
- [EE] Fixed issue where repo permissions could go stale when an upstream endpoint returned HTTP 410 Gone (e.g. Bitbucket Cloud's CHANGE-2770). [#1216](https://github.com/sourcebot-dev/sourcebot/pull/1216)
13+
- [EE] Fixed Bitbucket Cloud account-driven permission sync after Atlassian's CHANGE-2770 removed `GET /2.0/user/permissions/repositories`. [#1217](https://github.com/sourcebot-dev/sourcebot/pull/1217)
1314

1415
## [4.17.2] - 2026-05-16
1516

packages/backend/src/bitbucket.ts

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import micromatch from "micromatch";
99
import {
1010
SchemaRepository as CloudRepository,
1111
SchemaRepositoryUserPermission as CloudRepositoryUserPermission,
12-
SchemaRepositoryPermission as CloudRepositoryPermission,
1312
} from "@coderabbitai/bitbucket/cloud/openapi";
1413
import { SchemaRestRepository as ServerRepository } from "@coderabbitai/bitbucket/server/openapi";
1514
import { processPromiseResults } from "./connectionUtils.js";
@@ -660,24 +659,54 @@ export const getExplicitUserPermissionsForCloudRepo = async (
660659
/**
661660
* Returns the UUIDs of all private repositories accessible to the authenticated Bitbucket Cloud user.
662661
* Used for account-driven permission syncing.
663-
*
664-
* @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-repositories/#api-user-permissions-repositories-get
662+
*
663+
* @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-workspaces/#api-user-workspaces-get
664+
* @see https://developer.atlassian.com/cloud/bitbucket/rest/api-group-repositories/#api-repositories-workspace-get
665665
*/
666666
export const getReposForAuthenticatedBitbucketCloudUser = async (
667667
client: BitbucketClient,
668668
): Promise<Array<{ uuid: string }>> => {
669-
const path = `/user/permissions/repositories` as CloudGetRequestPath;
670-
671-
const permissions = await fetchWithRetry(() => getPaginatedCloud<CloudRepositoryPermission>(path, async (p, query) => {
672-
const { data } = await client.apiClient.GET(p, {
673-
params: { query },
674-
});
675-
return data;
676-
}), 'user repository permissions', logger);
669+
interface CloudUserWorkspaceAccess {
670+
readonly workspace?: { readonly slug?: string };
671+
}
677672

678-
return permissions
679-
.filter(p => p.repository?.uuid != null)
680-
.map(p => ({ uuid: p.repository!.uuid as string }));
673+
const memberships = await fetchWithRetry(
674+
() => getPaginatedCloud<CloudUserWorkspaceAccess>(
675+
`/user/workspaces` as CloudGetRequestPath,
676+
async (path, query) => {
677+
const { data } = await client.apiClient.GET(path, { params: { query } });
678+
return data;
679+
},
680+
),
681+
'user workspace memberships',
682+
logger,
683+
);
684+
685+
const slugs = memberships
686+
.map(m => m.workspace?.slug)
687+
.filter((slug): slug is string => typeof slug === 'string');
688+
689+
const reposByWorkspace = await Promise.all(slugs.map(workspace => fetchWithRetry(
690+
() => getPaginatedCloud<CloudRepository>(
691+
`/repositories/${workspace}` as CloudGetRequestPath,
692+
async (path, query) => {
693+
const { data } = await client.apiClient.GET(path, {
694+
params: {
695+
path: { workspace },
696+
query: { role: 'member', q: 'is_private=true', ...query },
697+
},
698+
});
699+
return data;
700+
},
701+
),
702+
`repos for workspace ${workspace}`,
703+
logger,
704+
)));
705+
706+
return reposByWorkspace
707+
.flat()
708+
.filter(repo => repo.uuid != null)
709+
.map(repo => ({ uuid: repo.uuid as string }));
681710
};
682711

683712
/**

0 commit comments

Comments
 (0)