Skip to content

Commit 53248d8

Browse files
committed
code rabbit fixes
1 parent 108d609 commit 53248d8

File tree

5 files changed

+74
-35
lines changed

5 files changed

+74
-35
lines changed

docs/docs/connections/bitbucket-cloud.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import BitbucketAppPassword from '/snippets/bitbucket-app-password.mdx';
3636
{
3737
"type": "bitbucket",
3838
"deploymentType": "cloud",
39-
"project": [
39+
"projects": [
4040
"myWorkspace/myRepo"
4141
]
4242
}

docs/docs/connections/bitbucket-data-center.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import BitbucketAppPassword from '/snippets/bitbucket-app-password.mdx';
1414
{
1515
"type": "bitbucket",
1616
"deploymentType": "server",
17-
"url": "https://mybitbucketdeployment.com"
17+
"url": "https://mybitbucketdeployment.com",
1818
"repos": [
1919
"myProject/myRepo"
2020
]
@@ -26,7 +26,7 @@ import BitbucketAppPassword from '/snippets/bitbucket-app-password.mdx';
2626
{
2727
"type": "bitbucket",
2828
"deploymentType": "server",
29-
"url": "https://mybitbucketdeployment.com"
29+
"url": "https://mybitbucketdeployment.com",
3030
"projects": [
3131
"myProject"
3232
]
@@ -38,7 +38,7 @@ import BitbucketAppPassword from '/snippets/bitbucket-app-password.mdx';
3838
{
3939
"type": "bitbucket",
4040
"deploymentType": "server",
41-
"url": "https://mybitbucketdeployment.com"
41+
"url": "https://mybitbucketdeployment.com",
4242
// Include all repos in myProject...
4343
"projects": [
4444
"myProject"

packages/backend/src/bitbucket.ts

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import { SchemaRestRepository as ServerRepository } from "@coderabbitai/bitbucket/server/openapi";
1313
import { processPromiseResults } from "./connectionUtils.js";
1414
import { throwIfAnyFailed } from "./connectionUtils.js";
15+
import { PaginatedResponse } from "@gitbeaker/rest";
1516

1617
const logger = createLogger("Bitbucket");
1718
const BITBUCKET_CLOUD_GIT = 'https://bitbucket.org';
@@ -27,7 +28,6 @@ interface BitbucketClient {
2728
apiClient: any;
2829
baseUrl: string;
2930
gitUrl: string;
30-
getPaginated: <T, V extends CloudGetRequestPath | ServerGetRequestPath>(path: V, get: (url: V) => Promise<PaginatedResponse<T>>) => Promise<T[]>;
3131
getReposForWorkspace: (client: BitbucketClient, workspaces: string[]) => Promise<{validRepos: BitbucketRepository[], notFoundWorkspaces: string[]}>;
3232
getReposForProjects: (client: BitbucketClient, projects: string[]) => Promise<{validRepos: BitbucketRepository[], notFoundProjects: string[]}>;
3333
getRepos: (client: BitbucketClient, repos: string[]) => Promise<{validRepos: BitbucketRepository[], notFoundRepos: string[]}>;
@@ -40,7 +40,7 @@ type CloudGetRequestPath = ClientPathsWithMethod<CloudAPI, "get">;
4040
type ServerAPI = ReturnType<typeof createBitbucketServerClient>;
4141
type ServerGetRequestPath = ClientPathsWithMethod<ServerAPI, "get">;
4242

43-
type PaginatedResponse<T> = {
43+
type CloudPaginatedResponse<T> = {
4444
readonly next?: string;
4545
readonly page?: number;
4646
readonly pagelen?: number;
@@ -49,6 +49,15 @@ type PaginatedResponse<T> = {
4949
readonly values?: readonly T[];
5050
}
5151

52+
type ServerPaginatedResponse<T> = {
53+
readonly size: number;
54+
readonly limit: number;
55+
readonly isLastPage: boolean;
56+
readonly values: readonly T[];
57+
readonly start: number;
58+
readonly nextPageStart: number;
59+
}
60+
5261
export const getBitbucketReposFromConfig = async (config: BitbucketConnectionConfig, orgId: number, db: PrismaClient) => {
5362
const token = config.token ?
5463
await getTokenFromConfig(config.token, orgId, db, logger) :
@@ -82,7 +91,7 @@ export const getBitbucketReposFromConfig = async (config: BitbucketConnectionCon
8291
if (config.projects) {
8392
const { validRepos, notFoundProjects } = await client.getReposForProjects(client, config.projects);
8493
allRepos = allRepos.concat(validRepos);
85-
notFound.repos = notFoundProjects;
94+
notFound.orgs = notFoundProjects;
8695
}
8796

8897
if (config.repos) {
@@ -103,12 +112,18 @@ export const getBitbucketReposFromConfig = async (config: BitbucketConnectionCon
103112

104113
function cloudClient(user: string | undefined, token: string | undefined): BitbucketClient {
105114

106-
const authorizationString = !user || user == "x-token-auth" ? `Bearer ${token}` : `Basic ${Buffer.from(`${user}:${token}`).toString('base64')}`;
115+
const authorizationString =
116+
token
117+
? !user || user == "x-token-auth"
118+
? `Bearer ${token}`
119+
: `Basic ${Buffer.from(`${user}:${token}`).toString('base64')}`
120+
: undefined;
121+
107122
const clientOptions: ClientOptions = {
108123
baseUrl: BITBUCKET_CLOUD_API,
109124
headers: {
110125
Accept: "application/json",
111-
Authorization: authorizationString,
126+
...(authorizationString ? { Authorization: authorizationString } : {}),
112127
},
113128
};
114129

@@ -119,7 +134,6 @@ function cloudClient(user: string | undefined, token: string | undefined): Bitbu
119134
apiClient: apiClient,
120135
baseUrl: BITBUCKET_CLOUD_API,
121136
gitUrl: BITBUCKET_CLOUD_GIT,
122-
getPaginated: getPaginatedCloud,
123137
getReposForWorkspace: cloudGetReposForWorkspace,
124138
getReposForProjects: cloudGetReposForProjects,
125139
getRepos: cloudGetRepos,
@@ -133,7 +147,10 @@ function cloudClient(user: string | undefined, token: string | undefined): Bitbu
133147
* We need to do `V extends CloudGetRequestPath` since we will need to call `apiClient.GET(url, ...)`, which
134148
* expects `url` to be of type `CloudGetRequestPath`. See example.
135149
**/
136-
const getPaginatedCloud = async <T, V extends CloudGetRequestPath | ServerGetRequestPath>(path: V, get: (url: V) => Promise<PaginatedResponse<T>>) => {
150+
const getPaginatedCloud = async <T>(
151+
path: CloudGetRequestPath,
152+
get: (url: CloudGetRequestPath) => Promise<CloudPaginatedResponse<T>>
153+
): Promise<T[]> => {
137154
const results: T[] = [];
138155
let url = path;
139156

@@ -150,8 +167,7 @@ const getPaginatedCloud = async <T, V extends CloudGetRequestPath | ServerGetReq
150167
break;
151168
}
152169

153-
// cast required here since response.next is a string.
154-
url = response.next as V;
170+
url = response.next as CloudGetRequestPath;
155171
}
156172
return results;
157173
}
@@ -164,7 +180,7 @@ async function cloudGetReposForWorkspace(client: BitbucketClient, workspaces: st
164180

165181
const path = `/repositories/${workspace}` as CloudGetRequestPath;
166182
const { durationMs, data } = await measure(async () => {
167-
const fetchFn = () => client.getPaginated<CloudRepository, typeof path>(path, async (url) => {
183+
const fetchFn = () => getPaginatedCloud<CloudRepository>(path, async (url) => {
168184
const response = await client.apiClient.GET(url, {
169185
params: {
170186
path: {
@@ -223,9 +239,15 @@ async function cloudGetReposForProjects(client: BitbucketClient, projects: strin
223239

224240
logger.debug(`Fetching all repos for project ${project} for workspace ${workspace}...`);
225241
try {
226-
const path = `/repositories/${workspace}?q=project.key="${project_name}"` as CloudGetRequestPath;
227-
const repos = await client.getPaginated<CloudRepository, typeof path>(path, async (url) => {
228-
const response = await client.apiClient.GET(url);
242+
const path = `/repositories/${workspace}` as CloudGetRequestPath;
243+
const repos = await getPaginatedCloud<CloudRepository>(path, async (url) => {
244+
const response = await client.apiClient.GET(url, {
245+
params: {
246+
query: {
247+
q: `project.key="${project_name}"`
248+
}
249+
}
250+
});
229251
const { data, error } = response;
230252
if (error) {
231253
throw new Error (`Failed to fetch projects for workspace ${workspace}: ${error.type}`);
@@ -317,6 +339,10 @@ function cloudShouldExcludeRepo(repo: BitbucketRepository, config: BitbucketConn
317339
return true;
318340
}
319341

342+
if (!!config.exclude?.archived) {
343+
logger.warn(`Exclude archived repos flag provided in config but Bitbucket Cloud does not support archived repos. Ignoring...`);
344+
}
345+
320346
if (!!config.exclude?.forks && cloudRepo.parent !== undefined) {
321347
return true;
322348
}
@@ -358,7 +384,6 @@ function serverClient(url: string, user: string | undefined, token: string | und
358384
apiClient: apiClient,
359385
baseUrl: url,
360386
gitUrl: url,
361-
getPaginated: getPaginatedServer,
362387
getReposForWorkspace: serverGetReposForWorkspace,
363388
getReposForProjects: serverGetReposForProjects,
364389
getRepos: serverGetRepos,
@@ -368,25 +393,27 @@ function serverClient(url: string, user: string | undefined, token: string | und
368393
return client;
369394
}
370395

371-
const getPaginatedServer = async <T, V extends CloudGetRequestPath | ServerGetRequestPath>(path: V, get: (url: V) => Promise<PaginatedResponse<T>>) => {
396+
const getPaginatedServer = async <T>(
397+
path: ServerGetRequestPath,
398+
get: (url: ServerGetRequestPath, start?: number) => Promise<ServerPaginatedResponse<T>>
399+
): Promise<T[]> => {
372400
const results: T[] = [];
373-
let url = path;
401+
let nextStart: number | undefined;
374402

375403
while (true) {
376-
const response = await get(url);
404+
const response = await get(path, nextStart);
377405

378406
if (!response.values || response.values.length === 0) {
379407
break;
380408
}
381409

382410
results.push(...response.values);
383411

384-
if (!response.next) {
412+
if (response.isLastPage) {
385413
break;
386414
}
387415

388-
// cast required here since response.next is a string.
389-
url = response.next as V;
416+
nextStart = response.nextPageStart;
390417
}
391418
return results;
392419
}
@@ -406,8 +433,14 @@ async function serverGetReposForProjects(client: BitbucketClient, projects: stri
406433

407434
const path = `/rest/api/1.0/projects/${project}/repos` as ServerGetRequestPath;
408435
const { durationMs, data } = await measure(async () => {
409-
const fetchFn = () => client.getPaginated<ServerRepository, typeof path>(path, async (url) => {
410-
const response = await client.apiClient.GET(url);
436+
const fetchFn = () => getPaginatedServer<ServerRepository>(path, async (url, start) => {
437+
const response = await client.apiClient.GET(url, {
438+
params: {
439+
query: {
440+
start,
441+
}
442+
}
443+
});
411444
const { data, error } = response;
412445
if (error) {
413446
throw new Error(`Failed to fetch repos for project ${project}: ${JSON.stringify(error)}`);
@@ -504,8 +537,10 @@ function serverShouldExcludeRepo(repo: BitbucketRepository, config: BitbucketCon
504537
return true;
505538
}
506539

507-
// Note: Bitbucket Server doesn't have a direct way to check if a repo is a fork
508-
// We'll need to check the origin property if it exists
540+
if (!!config.exclude?.archived && serverRepo.archived) {
541+
return true;
542+
}
543+
509544
if (!!config.exclude?.forks && serverRepo.origin !== undefined) {
510545
return true;
511546
}

packages/backend/src/repoCompileUtils.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ export const compileBitbucketConfig = async (
378378
const displayName = isServer ? (repo as BitbucketServerRepository).name! : (repo as BitbucketCloudRepository).full_name!;
379379
const externalId = isServer ? (repo as BitbucketServerRepository).id!.toString() : (repo as BitbucketCloudRepository).uuid!;
380380
const isPublic = isServer ? (repo as BitbucketServerRepository).public : (repo as BitbucketCloudRepository).is_private === false;
381+
const isArchived = isServer ? (repo as BitbucketServerRepository).archived === true : false;
382+
const isFork = isServer ? (repo as BitbucketServerRepository).origin !== undefined : (repo as BitbucketCloudRepository).parent !== undefined;
381383
const repoName = path.join(repoNameRoot, displayName);
382384
const cloneUrl = getCloneUrl(repo);
383385
const webUrl = getWebUrl(repo);
@@ -390,8 +392,8 @@ export const compileBitbucketConfig = async (
390392
webUrl: webUrl,
391393
name: repoName,
392394
displayName: displayName,
393-
isFork: false,
394-
isArchived: false,
395+
isFork: isFork,
396+
isArchived: isArchived,
395397
org: {
396398
connect: {
397399
id: orgId,
@@ -407,8 +409,8 @@ export const compileBitbucketConfig = async (
407409
'zoekt.web-url-type': codeHostType,
408410
'zoekt.web-url': webUrl,
409411
'zoekt.name': repoName,
410-
'zoekt.archived': marshalBool(false),
411-
'zoekt.fork': marshalBool(false),
412+
'zoekt.archived': marshalBool(isArchived),
413+
'zoekt.fork': marshalBool(isFork),
412414
'zoekt.public': marshalBool(isPublic),
413415
'zoekt.display-name': displayName,
414416
},

packages/backend/src/repoManager.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export class RepoManager implements IRepoManager {
190190
}
191191
})();
192192

193-
let password: string = "";
193+
let password: string | undefined = undefined;
194194
for (const repoConnection of repoConnections) {
195195
const connection = repoConnection.connection;
196196
if (connection.connectionType !== 'github' && connection.connectionType !== 'gitlab' && connection.connectionType !== 'gitea' && connection.connectionType !== 'bitbucket') {
@@ -201,7 +201,7 @@ export class RepoManager implements IRepoManager {
201201
if (config.token) {
202202
password = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
203203
if (password) {
204-
// If we're using a bitbucket connection, check to see if we're provided a username
204+
// If we're using a bitbucket connection we need to set the username to be able to clone the repo
205205
if (connection.connectionType === 'bitbucket') {
206206
const bitbucketConfig = config as BitbucketConnectionConfig;
207207
username = bitbucketConfig.user ?? "x-token-auth";
@@ -211,7 +211,9 @@ export class RepoManager implements IRepoManager {
211211
}
212212
}
213213

214-
return { username, password };
214+
return password
215+
? { username, password }
216+
: undefined;
215217
}
216218

217219
private async syncGitRepository(repo: RepoWithConnections, repoAlreadyInIndexingState: boolean) {

0 commit comments

Comments
 (0)