Skip to content

Commit 6fcdd2f

Browse files
Merge branch 'sourcebot-dev:main' into fix_469
2 parents 718571b + db6e5d4 commit 6fcdd2f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+2295
-1528
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
- Added fallback to default the Node.JS AWS SDK's `fromNodeProviderChain` when no credentials are provided for a bedrock config. [#513](https://github.com/sourcebot-dev/sourcebot/pull/513)
12+
13+
### Fixed
14+
- Fixed "At least one project, user, or group must be specified" for GitLab configs with `all` in web configurator. [#512](https://github.com/sourcebot-dev/sourcebot/pull/512)
15+
- Fixed zoekt indexing failing with pipe in branch/tag names [#506](https://github.com/sourcebot-dev/sourcebot/pull/506)
16+
- Removed deprecated connection creation/edit UI [#515](https://github.com/sourcebot-dev/sourcebot/pull/515)
17+
18+
## [4.6.8] - 2025-09-15
19+
20+
### Fixed
21+
- Fixed Bitbucket Cloud pagination not working beyond first page. [#295](https://github.com/sourcebot-dev/sourcebot/issues/295)
22+
- Fixed search bar line wrapping. [#501](https://github.com/sourcebot-dev/sourcebot/pull/501)
23+
- Fixed carousel perf issues. [#507](https://github.com/sourcebot-dev/sourcebot/pull/507)
24+
1025
## [4.6.7] - 2025-09-08
1126

1227
### Added

packages/backend/src/bitbucket.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,14 @@ function cloudClient(user: string | undefined, token: string | undefined): Bitbu
148148
**/
149149
const getPaginatedCloud = async <T>(
150150
path: CloudGetRequestPath,
151-
get: (url: CloudGetRequestPath) => Promise<CloudPaginatedResponse<T>>
151+
get: (path: CloudGetRequestPath, query?: Record<string, string>) => Promise<CloudPaginatedResponse<T>>
152152
): Promise<T[]> => {
153153
const results: T[] = [];
154-
let url = path;
154+
let nextPath = path;
155+
let nextQuery = undefined;
155156

156157
while (true) {
157-
const response = await get(url);
158+
const response = await get(nextPath, nextQuery);
158159

159160
if (!response.values || response.values.length === 0) {
160161
break;
@@ -166,25 +167,38 @@ const getPaginatedCloud = async <T>(
166167
break;
167168
}
168169

169-
url = response.next as CloudGetRequestPath;
170+
const parsedUrl = parseUrl(response.next);
171+
nextPath = parsedUrl.path as CloudGetRequestPath;
172+
nextQuery = parsedUrl.query;
170173
}
171174
return results;
172175
}
173-
176+
177+
/**
178+
* Parse the url into a path and query parameters to be used with the api client (openapi-fetch)
179+
*/
180+
function parseUrl(url: string): { path: string; query: Record<string, string>; } {
181+
const fullUrl = new URL(url);
182+
const path = fullUrl.pathname.replace(/^\/\d+(\.\d+)*/, ''); // remove version number in the beginning of the path
183+
const query = Object.fromEntries(fullUrl.searchParams);
184+
logger.debug(`Parsed url ${url} into path ${path} and query ${JSON.stringify(query)}`);
185+
return { path, query };
186+
}
187+
174188

175189
async function cloudGetReposForWorkspace(client: BitbucketClient, workspaces: string[]): Promise<{validRepos: CloudRepository[], notFoundWorkspaces: string[]}> {
176190
const results = await Promise.allSettled(workspaces.map(async (workspace) => {
177191
try {
178192
logger.debug(`Fetching all repos for workspace ${workspace}...`);
179193

180-
const path = `/repositories/${workspace}` as CloudGetRequestPath;
181194
const { durationMs, data } = await measure(async () => {
182-
const fetchFn = () => getPaginatedCloud<CloudRepository>(path, async (url) => {
183-
const response = await client.apiClient.GET(url, {
195+
const fetchFn = () => getPaginatedCloud<CloudRepository>(`/repositories/${workspace}` as CloudGetRequestPath, async (path, query) => {
196+
const response = await client.apiClient.GET(path, {
184197
params: {
185198
path: {
186199
workspace,
187-
}
200+
},
201+
query: query,
188202
}
189203
});
190204
const { data, error } = response;
@@ -238,11 +252,14 @@ async function cloudGetReposForProjects(client: BitbucketClient, projects: strin
238252

239253
logger.debug(`Fetching all repos for project ${project} for workspace ${workspace}...`);
240254
try {
241-
const path = `/repositories/${workspace}` as CloudGetRequestPath;
242-
const repos = await getPaginatedCloud<CloudRepository>(path, async (url) => {
243-
const response = await client.apiClient.GET(url, {
255+
const repos = await getPaginatedCloud<CloudRepository>(`/repositories/${workspace}` as CloudGetRequestPath, async (path, query) => {
256+
const response = await client.apiClient.GET(path, {
244257
params: {
258+
path: {
259+
workspace,
260+
},
245261
query: {
262+
...query,
246263
q: `project.key="${project_name}"`
247264
}
248265
}

packages/backend/src/zoekt.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const indexGitRepository = async (repo: Repo, settings: Settings, ctx: Ap
6363
`-index ${ctx.indexPath}`,
6464
`-max_trigram_count ${settings.maxTrigramCount}`,
6565
`-file_limit ${settings.maxFileSize}`,
66-
`-branches ${revisions.join(',')}`,
66+
`-branches "${revisions.join(',')}"`,
6767
`-tenant_id ${repo.orgId}`,
6868
`-repo_id ${repo.id}`,
6969
`-shard_prefix ${shardPrefix}`,
@@ -84,7 +84,9 @@ export const indexGitRepository = async (repo: Repo, settings: Settings, ctx: Ap
8484
}
8585
if (stderr) {
8686
stderr.split('\n').filter(line => line.trim()).forEach(line => {
87-
logger.error(line);
87+
// TODO: logging as regular info here and not error because non error logs are being
88+
// streamed in stderr and incorrectly being logged as errors at a high level
89+
logger.info(line);
8890
});
8991
}
9092

packages/mcp/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.0.5] - 2025-09-15
11+
12+
### Changed
13+
- Updated API client to match the latest Sourcebot release. [#356](https://github.com/sourcebot-dev/sourcebot/pull/356)
14+
1015
## [1.0.4] - 2025-08-04
1116

1217
### Fixed

packages/mcp/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sourcebot/mcp",
3-
"version": "1.0.4",
3+
"version": "1.0.5",
44
"type": "module",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

packages/mcp/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ server.tool(
161161
};
162162
}
163163

164-
const content: TextContent[] = response.repos.map(repo => {
164+
const content: TextContent[] = response.map(repo => {
165165
return {
166166
type: "text",
167-
text: `id: ${repo.name}\nurl: ${repo.webUrl}`,
167+
text: `id: ${repo.repoName}\nurl: ${repo.webUrl}`,
168168
}
169169
});
170170

packages/mcp/src/schemas.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,34 @@ export const searchResponseSchema = z.object({
9292
isBranchFilteringEnabled: z.boolean(),
9393
});
9494

95-
export const repositorySchema = z.object({
96-
name: z.string(),
97-
branches: z.array(z.string()),
95+
enum RepoIndexingStatus {
96+
NEW = 'NEW',
97+
IN_INDEX_QUEUE = 'IN_INDEX_QUEUE',
98+
INDEXING = 'INDEXING',
99+
INDEXED = 'INDEXED',
100+
FAILED = 'FAILED',
101+
IN_GC_QUEUE = 'IN_GC_QUEUE',
102+
GARBAGE_COLLECTING = 'GARBAGE_COLLECTING',
103+
GARBAGE_COLLECTION_FAILED = 'GARBAGE_COLLECTION_FAILED'
104+
}
105+
106+
export const repositoryQuerySchema = z.object({
107+
codeHostType: z.string(),
108+
repoId: z.number(),
109+
repoName: z.string(),
110+
repoDisplayName: z.string().optional(),
111+
repoCloneUrl: z.string(),
98112
webUrl: z.string().optional(),
99-
rawConfig: z.record(z.string(), z.string()).optional(),
113+
linkedConnections: z.array(z.object({
114+
id: z.number(),
115+
name: z.string(),
116+
})),
117+
imageUrl: z.string().optional(),
118+
indexedAt: z.coerce.date().optional(),
119+
repoIndexingStatus: z.nativeEnum(RepoIndexingStatus),
100120
});
101121

102-
export const listRepositoriesResponseSchema = z.object({
103-
repos: z.array(repositorySchema),
104-
});
122+
export const listRepositoriesResponseSchema = repositoryQuerySchema.array();
105123

106124
export const fileSourceRequestSchema = z.object({
107125
fileName: z.string(),

packages/mcp/src/types.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ export type SearchResultChunk = SearchResultFile["chunks"][number];
2222
export type SearchSymbol = z.infer<typeof symbolSchema>;
2323

2424
export type ListRepositoriesResponse = z.infer<typeof listRepositoriesResponseSchema>;
25-
export type Repository = ListRepositoriesResponse["repos"][number];
2625

2726
export type FileSourceRequest = z.infer<typeof fileSourceRequestSchema>;
2827
export type FileSourceResponse = z.infer<typeof fileSourceResponseSchema>;

packages/web/package.json

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,24 @@
77
"build": "cross-env SKIP_ENV_VALIDATION=1 next build",
88
"start": "next start",
99
"lint": "cross-env SKIP_ENV_VALIDATION=1 eslint .",
10-
"test": "vitest",
10+
"test": "cross-env SKIP_ENV_VALIDATION=1 vitest",
1111
"dev:emails": "email dev --dir ./src/emails",
1212
"stripe:listen": "stripe listen --forward-to http://localhost:3000/api/stripe"
1313
},
1414
"dependencies": {
15-
"@ai-sdk/amazon-bedrock": "^3.0.3",
16-
"@ai-sdk/anthropic": "^2.0.1",
17-
"@ai-sdk/azure": "^2.0.5",
18-
"@ai-sdk/deepseek": "^1.0.2",
19-
"@ai-sdk/google": "^2.0.3",
20-
"@ai-sdk/google-vertex": "^3.0.4",
21-
"@ai-sdk/mistral": "^2.0.1",
22-
"@ai-sdk/openai": "^2.0.5",
23-
"@ai-sdk/openai-compatible": "^1.0.2",
24-
"@ai-sdk/react": "^2.0.8",
25-
"@ai-sdk/xai": "^2.0.2",
15+
"@ai-sdk/amazon-bedrock": "^3.0.22",
16+
"@ai-sdk/anthropic": "^2.0.17",
17+
"@ai-sdk/azure": "^2.0.32",
18+
"@ai-sdk/deepseek": "^1.0.18",
19+
"@ai-sdk/google": "^2.0.14",
20+
"@ai-sdk/google-vertex": "^3.0.27",
21+
"@ai-sdk/mistral": "^2.0.14",
22+
"@ai-sdk/openai": "^2.0.32",
23+
"@ai-sdk/openai-compatible": "^1.0.18",
24+
"@ai-sdk/react": "^2.0.45",
25+
"@ai-sdk/xai": "^2.0.20",
2626
"@auth/prisma-adapter": "^2.7.4",
27+
"@aws-sdk/credential-providers": "^3.890.0",
2728
"@codemirror/commands": "^6.6.0",
2829
"@codemirror/lang-cpp": "^6.0.2",
2930
"@codemirror/lang-css": "^6.3.0",
@@ -109,7 +110,7 @@
109110
"@vercel/otel": "^1.13.0",
110111
"@viz-js/lang-dot": "^1.0.4",
111112
"@xiechao/codemirror-lang-handlebars": "^1.0.4",
112-
"ai": "^5.0.8",
113+
"ai": "^5.0.45",
113114
"ajv": "^8.17.1",
114115
"bcryptjs": "^3.0.2",
115116
"chokidar": "^4.0.3",
@@ -212,7 +213,8 @@
212213
"tsx": "^4.19.2",
213214
"typescript": "^5",
214215
"vite-tsconfig-paths": "^5.1.3",
215-
"vitest": "^2.1.5"
216+
"vitest": "^2.1.5",
217+
"vitest-mock-extended": "^3.1.0"
216218
},
217219
"resolutions": {
218220
"@types/react": "19.1.10",
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { SINGLE_TENANT_ORG_DOMAIN, SINGLE_TENANT_ORG_ID, SINGLE_TENANT_ORG_NAME } from '@/lib/constants';
2+
import { ApiKey, Org, PrismaClient, User } from '@prisma/client';
3+
import { beforeEach } from 'vitest';
4+
import { mockDeep, mockReset } from 'vitest-mock-extended';
5+
6+
beforeEach(() => {
7+
mockReset(prisma);
8+
});
9+
10+
export const prisma = mockDeep<PrismaClient>();
11+
12+
export const MOCK_ORG: Org = {
13+
id: SINGLE_TENANT_ORG_ID,
14+
name: SINGLE_TENANT_ORG_NAME,
15+
domain: SINGLE_TENANT_ORG_DOMAIN,
16+
createdAt: new Date(),
17+
updatedAt: new Date(),
18+
isOnboarded: true,
19+
imageUrl: null,
20+
metadata: null,
21+
memberApprovalRequired: false,
22+
stripeCustomerId: null,
23+
stripeSubscriptionStatus: null,
24+
stripeLastUpdatedAt: null,
25+
inviteLinkEnabled: false,
26+
inviteLinkId: null
27+
}
28+
29+
export const MOCK_API_KEY: ApiKey = {
30+
name: 'Test API Key',
31+
hash: 'apikey',
32+
createdAt: new Date(),
33+
lastUsedAt: new Date(),
34+
orgId: 1,
35+
createdById: '1',
36+
}
37+
38+
export const MOCK_USER: User = {
39+
id: '1',
40+
name: 'Test User',
41+
42+
createdAt: new Date(),
43+
updatedAt: new Date(),
44+
hashedPassword: null,
45+
emailVerified: null,
46+
image: null
47+
}
48+

0 commit comments

Comments
 (0)