Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions packages/backend/src/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { expect, test, describe, vi, beforeEach, afterEach } from 'vitest';
import { arraysEqualShallow, fetchWithRetry } from './utils';
import { isRemotePath } from '@sourcebot/shared';
import { arraysEqualShallow, fetchWithRetry, getAuthCredentialsForRepo } from './utils';
import { env, isRemotePath } from '@sourcebot/shared';
import { Logger } from 'winston';
import { RequestError } from '@octokit/request-error';
import type { RepoWithConnections } from './types';

vi.mock('@sentry/node', () => ({
captureException: vi.fn(),
Expand All @@ -15,6 +16,33 @@ const createMockLogger = (): Logger => ({
debug: vi.fn(),
} as unknown as Logger);

describe('getAuthCredentialsForRepo', () => {
const originalAskGithubToken = env.EXPERIMENT_ASK_GH_GITHUB_TOKEN;

afterEach(() => {
env.EXPERIMENT_ASK_GH_GITHUB_TOKEN = originalAskGithubToken;
});

test('uses the Ask GitHub PAT before other GitHub credentials', async () => {
env.EXPERIMENT_ASK_GH_GITHUB_TOKEN = 'github-pat-token';
const repo = {
displayName: 'codemirror/dev',
external_codeHostType: 'github',
external_codeHostUrl: 'https://github.com',
cloneUrl: 'https://github.com/codemirror/dev.git',
connections: [],
} as RepoWithConnections;

const credentials = await getAuthCredentialsForRepo(repo);

expect(credentials).toEqual({
hostUrl: 'https://github.com',
token: 'github-pat-token',
cloneUrlWithToken: 'https://x-access-token:github-pat-token@github.com/codemirror/dev.git',
});
});
});

test('should return true for identical arrays', () => {
expect(arraysEqualShallow([1, 2, 3], [1, 2, 3])).toBe(true);
});
Expand Down
21 changes: 19 additions & 2 deletions packages/backend/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Logger } from "winston";
import { RepoAuthCredentials, RepoWithConnections } from "./types.js";
import path from 'path';
import { getTokenFromConfig } from "@sourcebot/shared";
import { env, getTokenFromConfig } from "@sourcebot/shared";
import * as Sentry from "@sentry/node";
import { GithubConnectionConfig, GitlabConnectionConfig, GiteaConnectionConfig, BitbucketConnectionConfig, AzureDevOpsConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
import { GithubAppManager } from "./ee/githubAppManager.js";
Expand Down Expand Up @@ -115,6 +115,23 @@ export const fetchWithRetry = async <T>(
// may have their own token. This method will just pick the first connection that has a token (if one exists) and uses that. This
// may technically cause syncing to fail if that connection's token just so happens to not have access to the repo it's referencing.
export const getAuthCredentialsForRepo = async (repo: RepoWithConnections, logger?: Logger): Promise<RepoAuthCredentials | undefined> => {
if (repo.external_codeHostType === 'github' && env.EXPERIMENT_ASK_GH_GITHUB_TOKEN) {
logger?.debug(`Using Ask GitHub PAT for service auth for repo ${repo.displayName} hosted at ${repo.external_codeHostUrl}`);

const token = env.EXPERIMENT_ASK_GH_GITHUB_TOKEN;
return {
hostUrl: repo.external_codeHostUrl,
token,
cloneUrlWithToken: createGitCloneUrlWithToken(
repo.cloneUrl,
{
username: 'x-access-token',
password: token,
}
),
};
}
Comment thread
brendan-kellam marked this conversation as resolved.

// If we have github apps configured we assume that we must use them for github service auth
if (repo.external_codeHostType === 'github' && await hasEntitlement('github-app') && GithubAppManager.getInstance().appsConfigured()) {
logger?.debug(`Using GitHub App for service auth for repo ${repo.displayName} hosted at ${repo.external_codeHostUrl}`);
Expand Down Expand Up @@ -284,4 +301,4 @@ export const setIntervalAsync = (target: () => Promise<void>, pollingIntervalMs:
setIntervalWithPromise(target),
pollingIntervalMs
);
}
}
1 change: 1 addition & 0 deletions packages/shared/src/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ const options = {
EXPERIMENT_SELF_SERVE_REPO_INDEXING_GITHUB_TOKEN: z.string().optional(),
PERMISSION_SYNC_REPO_DRIVEN_ENABLED: booleanSchema.default('true'),
EXPERIMENT_ASK_GH_ENABLED: booleanSchema.default('false'),
EXPERIMENT_ASK_GH_GITHUB_TOKEN: z.string().optional(),

// Used as the key for AES-256-CBC encryption (@see shared/src/crypto.ts).
// The key is read as ASCII (1 char = 1 byte), so AES-256's 32-byte key
Expand Down
Loading