Skip to content

Commit 68265ac

Browse files
Fix auth with GitHub (#288)
1 parent 09894a5 commit 68265ac

File tree

2 files changed

+53
-37
lines changed

2 files changed

+53
-37
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111
- Added `exclude.readOnly` and `exclude.hidden` options to Gerrit connection config. [#280](https://github.com/sourcebot-dev/sourcebot/pull/280)
1212

13+
### Fixes
14+
- Fixes regression introduced in v3.1.0 that causes auth errors with GitHub. [#288](https://github.com/sourcebot-dev/sourcebot/pull/288)
15+
1316
## [3.1.1] - 2025-04-28
1417

1518
### Changed

packages/backend/src/repoManager.ts

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -170,50 +170,54 @@ export class RepoManager implements IRepoManager {
170170
// fetch the token here using the connections from the repo. Multiple connections could be referencing this repo, and each
171171
// may have their own token. This method will just pick the first connection that has a token (if one exists) and uses that. This
172172
// may technically cause syncing to fail if that connection's token just so happens to not have access to the repo it's referrencing.
173-
private async getAuthForRepo(repo: RepoWithConnections, db: PrismaClient): Promise<{ username: string, password: string } | undefined> {
174-
const repoConnections = repo.connections;
175-
if (repoConnections.length === 0) {
176-
this.logger.error(`Repo ${repo.id} has no connections`);
177-
return undefined;
178-
}
173+
private async getCloneCredentialsForRepo(repo: RepoWithConnections, db: PrismaClient): Promise<{ username?: string, password: string } | undefined> {
174+
175+
for (const { connection } of repo.connections) {
176+
if (connection.connectionType === 'github') {
177+
const config = connection.config as unknown as GithubConnectionConfig;
178+
if (config.token) {
179+
const token = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
180+
return {
181+
password: token,
182+
}
183+
}
184+
}
179185

180-
let username = (() => {
181-
switch (repo.external_codeHostType) {
182-
case 'gitlab':
183-
return 'oauth2';
184-
case 'bitbucket-cloud':
185-
case 'bitbucket-server':
186-
case 'github':
187-
case 'gitea':
188-
default:
189-
return '';
186+
else if (connection.connectionType === 'gitlab') {
187+
const config = connection.config as unknown as GitlabConnectionConfig;
188+
if (config.token) {
189+
const token = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
190+
return {
191+
username: 'oauth2',
192+
password: token,
193+
}
194+
}
190195
}
191-
})();
192196

193-
let password: string | undefined = undefined;
194-
for (const repoConnection of repoConnections) {
195-
const connection = repoConnection.connection;
196-
if (connection.connectionType !== 'github' && connection.connectionType !== 'gitlab' && connection.connectionType !== 'gitea' && connection.connectionType !== 'bitbucket') {
197-
continue;
197+
else if (connection.connectionType === 'gitea') {
198+
const config = connection.config as unknown as GiteaConnectionConfig;
199+
if (config.token) {
200+
const token = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
201+
return {
202+
password: token,
203+
}
204+
}
198205
}
199206

200-
const config = connection.config as unknown as GithubConnectionConfig | GitlabConnectionConfig | GiteaConnectionConfig | BitbucketConnectionConfig;
201-
if (config.token) {
202-
password = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
203-
if (password) {
204-
// If we're using a bitbucket connection we need to set the username to be able to clone the repo
205-
if (connection.connectionType === 'bitbucket') {
206-
const bitbucketConfig = config as BitbucketConnectionConfig;
207-
username = bitbucketConfig.user ?? "x-token-auth";
207+
else if (connection.connectionType === 'bitbucket') {
208+
const config = connection.config as unknown as BitbucketConnectionConfig;
209+
if (config.token) {
210+
const token = await getTokenFromConfig(config.token, connection.orgId, db, this.logger);
211+
const username = config.user ?? 'x-token-auth';
212+
return {
213+
username,
214+
password: token,
208215
}
209-
break;
210216
}
211217
}
212218
}
213219

214-
return password
215-
? { username, password }
216-
: undefined;
220+
return undefined;
217221
}
218222

219223
private async syncGitRepository(repo: RepoWithConnections, repoAlreadyInIndexingState: boolean) {
@@ -244,11 +248,20 @@ export class RepoManager implements IRepoManager {
244248
} else {
245249
this.logger.info(`Cloning ${repo.displayName}...`);
246250

247-
const auth = await this.getAuthForRepo(repo, this.db);
251+
const auth = await this.getCloneCredentialsForRepo(repo, this.db);
248252
const cloneUrl = new URL(repo.cloneUrl);
249253
if (auth) {
250-
cloneUrl.username = auth.username;
251-
cloneUrl.password = auth.password;
254+
// @note: URL has a weird behavior where if you set the password but
255+
// _not_ the username, the ":" delimiter will still be present in the
256+
// URL (e.g., https://:password@example.com). To get around this, if
257+
// we only have a password, we set the username to the password.
258+
// @see: https://www.typescriptlang.org/play/?#code/MYewdgzgLgBArgJwDYwLwzAUwO4wKoBKAMgBQBEAFlFAA4QBcA9I5gB4CGAtjUpgHShOZADQBKANwAoREj412ECNhAIAJmhhl5i5WrJTQkELz5IQAcxIy+UEAGUoCAJZhLo0UA
259+
if (!auth.username) {
260+
cloneUrl.username = auth.password;
261+
} else {
262+
cloneUrl.username = auth.username;
263+
cloneUrl.password = auth.password;
264+
}
252265
}
253266

254267
const { durationMs } = await measure(() => cloneRepository(cloneUrl.toString(), repoPath, ({ method, stage, progress }) => {

0 commit comments

Comments
 (0)