Skip to content

Commit 55d7ff0

Browse files
Merge branch 'main' into brendan/job-manager
2 parents a42d131 + 2afc265 commit 55d7ff0

13 files changed

Lines changed: 729 additions & 406 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- Made the backend worker API address configurable via the `WORKER_API_URL` environment variable (default `http://localhost:3060`) instead of being hardcoded. [#1409](https://github.com/sourcebot-dev/sourcebot/pull/1409)
1414
- [EE] Disabled `DELETE /api/ee/user` while SCIM provisioning is enabled, and switched it to an org-scoped membership removal (with last-owner protection) instead of a global account delete. [#1425](https://github.com/sourcebot-dev/sourcebot/pull/1425)
1515
- [EE] Unified the `GET /api/ee/user` and `GET /api/ee/users` response shapes behind a shared mapper; the single-user endpoint is now scoped to org membership, and both include role, membership status, and last activity. [#1425](https://github.com/sourcebot-dev/sourcebot/pull/1425)
16+
- Browse blob, tree, and commit pages now fetch file sources, folder contents, and diffs client-side via API routes instead of embedding them in the server-rendered page, keeping documents small and event-loop pressure low for large files and commits. [#1426](https://github.com/sourcebot-dev/sourcebot/pull/1426)
1617

1718
### Added
1819
- Added per-step token cost tracking and estimated tool call token usage to Ask Sourcebot chat history. [#1353](https://github.com/sourcebot-dev/sourcebot/pull/1353)
@@ -23,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2324
- [EE] Added text file attachments to Ask Sourcebot, letting users attach text/code/config files to a chat message via the paperclip button, drag-and-drop, or paste, with large pastes auto-converted to attachments. [#1374](https://github.com/sourcebot-dev/sourcebot/pull/1374)
2425
- [EE] Added image attachments to Ask Sourcebot, letting users attach images to a chat message when the selected model supports image input. [#1375](https://github.com/sourcebot-dev/sourcebot/pull/1375)
2526
- Added deployment system resource stats (CPU cores + cgroup quota, host + container memory, disk, load average) to the service ping, so resource issues can be diagnosed more quickly. [#1424](https://github.com/sourcebot-dev/sourcebot/pull/1424)
27+
- Added a `robots.txt` that disallows crawlers, with an allowlist for link-preview bots so shared links keep their OpenGraph previews. [#1426](https://github.com/sourcebot-dev/sourcebot/pull/1426)
2628

2729
### Fixed
2830
- Send anonymous server-side PostHog events as personless so unauthenticated requests don't inflate person counts. [#1367](https://github.com/sourcebot-dev/sourcebot/pull/1367)
@@ -35,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3537
- Upgraded `nodemailer` to `^9.0.1`. [#1356](https://github.com/sourcebot-dev/sourcebot/pull/1356)
3638
- Upgraded `@opentelemetry/core` to `^2.8.0`. [#1413](https://github.com/sourcebot-dev/sourcebot/pull/1413)
3739
- [EE] Fixed connector setup dialogs to add scrolling when connector setup content goes out of view.
40+
- Fixed Gitea sync failing with `ERR_STREAM_PREMATURE_CLOSE` by forcing identity encoding on the Gitea API fetch and guarding against null repository responses. [#1405](https://github.com/sourcebot-dev/sourcebot/pull/1405)
3841

3942
## [5.0.4] - 2026-06-18
4043

packages/backend/src/gitea.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ import { measure } from './utils.js';
1212
const logger = createLogger('gitea');
1313
const GITEA_CLOUD_HOSTNAME = "gitea.com";
1414

15+
// Some Gitea instances (particularly when behind certain reverse proxies or with
16+
// response compression enabled) cause `cross-fetch` to fail while reading the
17+
// response body with ERR_STREAM_PREMATURE_CLOSE. Forcing identity encoding and
18+
// closing the connection avoids the premature close.
19+
// @see https://github.com/sourcebot-dev/sourcebot/issues/1404
20+
const customFetch: typeof fetch = (url, options = {}) => {
21+
return fetch(url, {
22+
...options,
23+
headers: {
24+
...(options.headers ?? {}),
25+
'Accept-Encoding': 'identity',
26+
'Connection': 'close',
27+
},
28+
});
29+
};
30+
1531
export const getGiteaReposFromConfig = async (config: GiteaConnectionConfig) => {
1632
const hostname = config.url ?
1733
new URL(config.url).hostname :
@@ -25,7 +41,7 @@ export const getGiteaReposFromConfig = async (config: GiteaConnectionConfig) =>
2541

2642
const api = giteaApi(config.url ?? 'https://gitea.com', {
2743
token: token,
28-
customFetch: fetch,
44+
customFetch,
2945
});
3046

3147
let allRepos: GiteaRepository[] = [];
@@ -49,8 +65,11 @@ export const getGiteaReposFromConfig = async (config: GiteaConnectionConfig) =>
4965
allWarnings = allWarnings.concat(warnings);
5066
}
5167

52-
allRepos = allRepos.filter(repo => repo.full_name !== undefined);
5368
allRepos = allRepos.filter(repo => {
69+
if (repo === null || repo === undefined) {
70+
logger.warn(`Skipping null/undefined repository returned by the Gitea API`);
71+
return false;
72+
}
5473
if (repo.full_name === undefined) {
5574
logger.warn(`Repository with undefined full_name found: repoId=${repo.id}`);
5675
return false;
@@ -208,6 +227,10 @@ const getRepos = async <T>(repoList: string[], api: Api<T>) => {
208227
api.repos.repoGet(owner, repoName),
209228
);
210229

230+
if (response.error || !response.data) {
231+
throw response.error ?? new Error(`Received empty response body while fetching repository ${repo}`);
232+
}
233+
211234
logger.debug(`Found repo ${repo} in ${durationMs}ms.`);
212235
return {
213236
type: 'valid' as const,
Lines changed: 17 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,6 @@
11
import { getRepoInfoByName } from "@/actions";
2-
import { PathHeader } from "@/app/(app)/components/pathHeader";
3-
import { Button } from "@/components/ui/button";
4-
import { Separator } from "@/components/ui/separator";
5-
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
6-
import { cn, getCodeHostInfoForRepo, isServiceError, truncateSha } from "@/lib/utils";
7-
import { X } from "lucide-react";
8-
import Image from "next/image";
9-
import Link from "next/link";
10-
import { getBrowsePath } from "../../../hooks/utils";
11-
import { BlameAgeLegend } from "./blameAgeLegend";
12-
import { BlameViewToggle } from "./blameViewToggle";
13-
import { PureCodePreviewPanel } from "./pureCodePreviewPanel";
14-
import { getFileBlame, getFileSource } from '@/features/git';
15-
16-
const formatFileSize = (bytes: number): string => {
17-
if (bytes < 1024) {
18-
return `${bytes} B`;
19-
}
20-
if (bytes < 1024 * 1024) {
21-
return `${(bytes / 1024).toFixed(1)} KB`;
22-
}
23-
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
24-
};
2+
import { isServiceError } from "@/lib/utils";
3+
import { CodePreviewPanelClient } from "./codePreviewPanelClient";
254

265
interface CodePreviewPanelProps {
276
path: string;
@@ -36,156 +15,25 @@ interface CodePreviewPanelProps {
3615
}
3716

3817
export const CodePreviewPanel = async ({ path, repoName, revisionName, previewRef, blame }: CodePreviewPanelProps) => {
39-
const contentRef = previewRef ?? revisionName;
40-
41-
const [fileSourceResponse, repoInfoResponse, blameResponse] = await Promise.all([
42-
getFileSource({
43-
path,
44-
repo: repoName,
45-
ref: contentRef,
46-
}, { source: 'sourcebot-web-client' }),
47-
getRepoInfoByName(repoName),
48-
blame
49-
? getFileBlame({
50-
path,
51-
repo: repoName,
52-
ref: contentRef,
53-
}, { source: 'sourcebot-web-client' })
54-
: Promise.resolve(undefined),
55-
]);
56-
57-
if (isServiceError(fileSourceResponse)) {
58-
return <div>Error loading file source: {fileSourceResponse.message}</div>
59-
}
18+
const repoInfoResponse = await getRepoInfoByName(repoName);
6019

6120
if (isServiceError(repoInfoResponse)) {
6221
return <div>Error loading repo info: {repoInfoResponse.message}</div>
6322
}
6423

65-
if (blameResponse !== undefined && isServiceError(blameResponse)) {
66-
return <div>Error loading blame: {blameResponse.message}</div>
67-
}
68-
69-
const source = fileSourceResponse.source;
70-
const lineCount = source.length === 0
71-
? 0
72-
: source.split('\n').length - (source.endsWith('\n') ? 1 : 0);
73-
const byteSize = Buffer.byteLength(source, 'utf-8');
74-
const fileSize = formatFileSize(byteSize);
75-
76-
const codeHostInfo = getCodeHostInfoForRepo({
77-
codeHostType: repoInfoResponse.codeHostType,
78-
name: repoInfoResponse.name,
79-
displayName: repoInfoResponse.displayName,
80-
externalWebUrl: repoInfoResponse.externalWebUrl,
81-
});
82-
83-
// @todo: this is a hack to support linking to files for ADO. ADO doesn't support web urls with HEAD so we replace it with main. THis
84-
// will break if the default branch is not main.
85-
const fileWebUrl = repoInfoResponse.codeHostType === "azuredevops" && fileSourceResponse.externalWebUrl ?
86-
fileSourceResponse.externalWebUrl.replace("version=GBHEAD", "version=GBmain") : fileSourceResponse.externalWebUrl;
87-
8824
return (
89-
<>
90-
<div className="flex flex-row py-1 px-2 items-center justify-between">
91-
<PathHeader
92-
path={path}
93-
repo={{
94-
name: repoName,
95-
codeHostType: repoInfoResponse.codeHostType,
96-
displayName: repoInfoResponse.displayName,
97-
externalWebUrl: repoInfoResponse.externalWebUrl,
98-
}}
99-
revisionName={contentRef}
100-
/>
101-
102-
{fileWebUrl && (
103-
104-
<a
105-
href={fileWebUrl}
106-
target="_blank"
107-
rel="noopener noreferrer"
108-
className="flex flex-row items-center gap-2 px-2 py-0.5 rounded-md flex-shrink-0"
109-
>
110-
<Image
111-
src={codeHostInfo.icon}
112-
alt={codeHostInfo.codeHostName}
113-
className={cn('w-4 h-4 flex-shrink-0', codeHostInfo.iconClassName)}
114-
/>
115-
<span className="text-sm font-medium">Open in {codeHostInfo.codeHostName}</span>
116-
</a>
117-
)}
118-
</div>
119-
<Separator />
120-
{!previewRef && (
121-
<div className="flex flex-row items-center gap-3 px-4 py-1 border-b shrink-0">
122-
<BlameViewToggle
123-
repoName={repoName}
124-
revisionName={revisionName}
125-
path={path}
126-
blame={blame ?? false}
127-
/>
128-
<span className="text-sm text-muted-foreground">
129-
{lineCount.toLocaleString()} lines · {fileSize}
130-
</span>
131-
{blame && (
132-
<>
133-
<Separator orientation="vertical" className="h-4" />
134-
<BlameAgeLegend />
135-
</>
136-
)}
137-
</div>
138-
)}
139-
{previewRef && (
140-
<div className="flex flex-row items-center justify-between gap-2 px-4 py-2 border-b shrink-0">
141-
<span className="text-sm">
142-
Previewing file at revision{" "}
143-
<Link
144-
href={getBrowsePath({
145-
repoName,
146-
revisionName,
147-
path: '',
148-
pathType: 'commit',
149-
commitSha: previewRef,
150-
})}
151-
className="font-mono text-link hover:underline"
152-
>
153-
{truncateSha(previewRef)}
154-
</Link>
155-
</span>
156-
<Tooltip key={previewRef}>
157-
<TooltipTrigger>
158-
<Button
159-
asChild
160-
variant="ghost"
161-
size="icon"
162-
className="h-6 w-6 text-muted-foreground"
163-
>
164-
<Link
165-
href={getBrowsePath({
166-
repoName,
167-
revisionName,
168-
path,
169-
pathType: 'blob',
170-
})}
171-
aria-label="Close preview"
172-
>
173-
<X className="h-4 w-4" />
174-
</Link>
175-
</Button>
176-
</TooltipTrigger>
177-
<TooltipContent>Close preview</TooltipContent>
178-
</Tooltip>
179-
</div>
180-
)}
181-
<PureCodePreviewPanel
182-
source={fileSourceResponse.source}
183-
language={fileSourceResponse.language}
184-
repoName={repoName}
185-
path={path}
186-
revisionName={contentRef ?? 'HEAD'}
187-
blame={blameResponse}
188-
/>
189-
</>
25+
<CodePreviewPanelClient
26+
path={path}
27+
repoName={repoName}
28+
revisionName={revisionName}
29+
previewRef={previewRef}
30+
blame={blame}
31+
repo={{
32+
name: repoInfoResponse.name,
33+
codeHostType: repoInfoResponse.codeHostType,
34+
displayName: repoInfoResponse.displayName,
35+
externalWebUrl: repoInfoResponse.externalWebUrl,
36+
}}
37+
/>
19038
)
191-
}
39+
}

0 commit comments

Comments
 (0)