mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 20:35:24 +00:00
feat(auth): github app (#570)
* properly handle emails for github app auth case * add docs info for auth through github app * more info in docs for user auth perms * modify review agent env var names * github app service auth * coderabbit suggestions * fixes * fix build
This commit is contained in:
parent
03999f0de0
commit
c2299aa86b
31 changed files with 1094 additions and 50 deletions
|
|
@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
### Added
|
||||
- Implement dynamic tab titles for files and folders in browse tab. [#560](https://github.com/sourcebot-dev/sourcebot/pull/560)
|
||||
- Added support for passing db connection url as seperate `DATABASE_HOST`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, `DATABASE_NAME`, and `DATABASE_ARGS` env vars. [#545](https://github.com/sourcebot-dev/sourcebot/pull/545)
|
||||
- Added support for GitHub Apps for service auth. [#570](https://github.com/sourcebot-dev/sourcebot/pull/570)
|
||||
|
||||
### Fixed
|
||||
- Fixed "dubious ownership" errors when cloning / fetching repos. [#553](https://github.com/sourcebot-dev/sourcebot/pull/553)
|
||||
|
|
@ -27,9 +29,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Removed spam "login page loaded" log. [#552](https://github.com/sourcebot-dev/sourcebot/pull/552)
|
||||
- Removed connections management page. [#563](https://github.com/sourcebot-dev/sourcebot/pull/563)
|
||||
|
||||
### Added
|
||||
- Added support for passing db connection url as seperate `DATABASE_HOST`, `DATABASE_USERNAME`, `DATABASE_PASSWORD`, `DATABASE_NAME`, and `DATABASE_ARGS` env vars. [#545](https://github.com/sourcebot-dev/sourcebot/pull/545)
|
||||
|
||||
## [4.7.3] - 2025-09-29
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Sourcebot's built-in authentication system gates your deployment, and allows adm
|
|||
<Card horizontal title="Authentication providers" icon="lock" href="/docs/configuration/auth/providers">
|
||||
Configure additional authentication providers for your deployment.
|
||||
</Card>
|
||||
<Card horizontal title="Inviting members" icon="user" href="/docs/configuration/auth/inviting-members">
|
||||
<Card horizontal title="Access settings" icon="user" href="/docs/configuration/auth/access-settings">
|
||||
Learn how to configure how members join your deployment.
|
||||
</Card>
|
||||
<Card horizontal title="Roles and permissions" icon="shield" href="/docs/configuration/auth/roles-and-permissions">
|
||||
|
|
|
|||
|
|
@ -33,6 +33,13 @@ The following authentication providers require an [enterprise license](/docs/lic
|
|||
|
||||
[Auth.js GitHub Provider Docs](https://authjs.dev/getting-started/providers/github)
|
||||
|
||||
Authentication using both a **GitHub OAuth App** and a **GitHub App** is supported. In both cases, you must provide Sourcebot the `CLIENT_ID` and `SECRET_ID` and configure the
|
||||
callback URL correctly (more info in Auth.js docs).
|
||||
|
||||
When using a **GitHub App** for auth, enable the following permissions:
|
||||
- `“Email addresses” account permissions (read)`
|
||||
- `"Metadata" repository permissions (read)` (only needed if enabling [permission syncing](/docs/features/permission-syncing))
|
||||
|
||||
**Required environment variables:**
|
||||
- `AUTH_EE_GITHUB_CLIENT_ID`
|
||||
- `AUTH_EE_GITHUB_CLIENT_SECRET`
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ The following environment variables allow you to configure your Sourcebot deploy
|
|||
### Review Agent Environment Variables
|
||||
| Variable | Default | Description |
|
||||
| :------- | :------ | :---------- |
|
||||
| `GITHUB_APP_ID` | `-` | <p>The GitHub App ID used for review agent authentication.</p> |
|
||||
| `GITHUB_APP_PRIVATE_KEY_PATH` | `-` | <p>The container relative path to the private key file for the GitHub App used by the review agent.</p> |
|
||||
| `GITHUB_APP_WEBHOOK_SECRET` | `-` | <p>The webhook secret for the GitHub App used by the review agent.</p> |
|
||||
| `GITHUB_REVIEW_AGENT_APP_ID` | `-` | <p>The GitHub App ID used for review agent authentication.</p> |
|
||||
| `GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH` | `-` | <p>The container relative path to the private key file for the GitHub App used by the review agent.</p> |
|
||||
| `GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET` | `-` | <p>The webhook secret for the GitHub App used by the review agent.</p> |
|
||||
| `OPENAI_API_KEY` | `-` | <p>The OpenAI API key used by the review agent.</p> |
|
||||
| `REVIEW_AGENT_API_KEY` | `-` | <p>The Sourcebot API key used by the review agent.</p> |
|
||||
| `REVIEW_AGENT_AUTO_REVIEW_ENABLED` | `false` | <p>Enables/disables automatic code reviews by the review agent.</p> |
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ Before you get started, make sure you have an OpenAPI account that you can creat
|
|||
<Step title="Configure the environment variables in Sourcebot">
|
||||
Sourcebot requires the following environment variables to begin reviewing PRs through your new GitHub app:
|
||||
|
||||
- `GITHUB_APP_ID`: The client ID of your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings)
|
||||
- `GITHUB_APP_WEBHOOK_SECRET`: The webhook secret you defined in your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings)
|
||||
- `GITHUB_APP_PRIVATE_KEY_PATH`: The path to your app's private key. If you're running Sourcebot from a container, this is the path to this file from within your container
|
||||
- `GITHUB_REVIEW_AGENT_APP_ID`: The client ID of your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings)
|
||||
- `GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET`: The webhook secret you defined in your GitHub app. Can be found in your [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings)
|
||||
- `GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH`: The path to your app's private key. If you're running Sourcebot from a container, this is the path to this file from within your container
|
||||
(ex `/data/review-agent-key.pem`). You must copy the private key file into the directory you mount to Sourcebot (similar to the config file).
|
||||
|
||||
You can generate a private key file for your app in the [app settings](https://docs.github.com/en/apps/creating-github-apps/writing-code-for-a-github-app/quickstart#navigate-to-your-app-settings). You must copy this private key file into the
|
||||
|
|
@ -74,9 +74,9 @@ Before you get started, make sure you have an OpenAPI account that you can creat
|
|||
- "/Users/michael/sourcebot_review_agent_workspace:/data"
|
||||
environment:
|
||||
CONFIG_PATH: "/data/config.json"
|
||||
GITHUB_APP_ID: "my-github-app-id"
|
||||
GITHUB_APP_WEBHOOK_SECRET: "my-github-app-webhook-secret"
|
||||
GITHUB_APP_PRIVATE_KEY_PATH: "/data/review-agent-key.pem"
|
||||
GITHUB_REVIEW_AGENT_APP_ID: "my-github-app-id"
|
||||
GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET: "my-github-app-webhook-secret"
|
||||
GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH: "/data/review-agent-key.pem"
|
||||
REVIEW_AGENT_API_KEY: "sourcebot-my-key"
|
||||
OPENAI_API_KEY: "sk-proj-my-open-api-key"
|
||||
```
|
||||
|
|
|
|||
82
docs/snippets/schemas/v3/app.schema.mdx
Normal file
82
docs/snippets/schemas/v3/app.schema.mdx
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
{/* THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! */}
|
||||
```json
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AppConfig",
|
||||
"oneOf": [
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"description": "The private key of the GitHub App.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
76
docs/snippets/schemas/v3/githubApp.schema.mdx
Normal file
76
docs/snippets/schemas/v3/githubApp.schema.mdx
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
{/* THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! */}
|
||||
```json
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"description": "The private key of the GitHub App.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
|
@ -4273,6 +4273,89 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"description": "Defines a collection of apps that are available to Sourcebot.",
|
||||
"items": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AppConfig",
|
||||
"oneOf": [
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"description": "The private key of the GitHub App."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
"dependencies": {
|
||||
"@coderabbitai/bitbucket": "^1.1.3",
|
||||
"@gitbeaker/rest": "^40.5.1",
|
||||
"@octokit/app": "^16.1.1",
|
||||
"@octokit/rest": "^21.0.2",
|
||||
"@sentry/cli": "^2.42.2",
|
||||
"@sentry/node": "^9.3.0",
|
||||
|
|
|
|||
138
packages/backend/src/ee/githubAppManager.ts
Normal file
138
packages/backend/src/ee/githubAppManager.ts
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { GithubAppConfig, SourcebotConfig } from "@sourcebot/schemas/v3/index.type";
|
||||
import { loadConfig } from "@sourcebot/shared";
|
||||
import { env } from "../env.js";
|
||||
import { createLogger } from "@sourcebot/logger";
|
||||
import { getTokenFromConfig } from "../utils.js";
|
||||
import { PrismaClient } from "@sourcebot/db";
|
||||
import { App } from "@octokit/app";
|
||||
|
||||
const logger = createLogger('githubAppManager');
|
||||
const GITHUB_DEFAULT_DEPLOYMENT_HOSTNAME = 'github.com';
|
||||
|
||||
type Installation = {
|
||||
id: number;
|
||||
appId: number;
|
||||
account: {
|
||||
login: string;
|
||||
type: 'organization' | 'user';
|
||||
};
|
||||
createdAt: string;
|
||||
expiresAt: string;
|
||||
token: string;
|
||||
};
|
||||
|
||||
export class GithubAppManager {
|
||||
private static instance: GithubAppManager | null = null;
|
||||
private octokitApps: Map<number, App>;
|
||||
private installationMap: Map<string, Installation>;
|
||||
private db: PrismaClient | null = null;
|
||||
private initialized: boolean = false;
|
||||
|
||||
private constructor() {
|
||||
this.octokitApps = new Map<number, App>();
|
||||
this.installationMap = new Map<string, Installation>();
|
||||
}
|
||||
|
||||
public static getInstance(): GithubAppManager {
|
||||
if (!GithubAppManager.instance) {
|
||||
GithubAppManager.instance = new GithubAppManager();
|
||||
}
|
||||
return GithubAppManager.instance;
|
||||
}
|
||||
|
||||
private ensureInitialized(): void {
|
||||
if (!this.initialized) {
|
||||
throw new Error('GithubAppManager must be initialized before use. Call init() first.');
|
||||
}
|
||||
}
|
||||
|
||||
public async init(db: PrismaClient) {
|
||||
this.db = db;
|
||||
const config = await loadConfig(env.CONFIG_PATH!);
|
||||
const githubApps = config.apps?.filter(app => app.type === 'githubApp') as GithubAppConfig[];
|
||||
|
||||
logger.info(`Found ${githubApps.length} GitHub apps in config`);
|
||||
|
||||
for (const app of githubApps) {
|
||||
const deploymentHostname = app.deploymentHostname as string || GITHUB_DEFAULT_DEPLOYMENT_HOSTNAME;
|
||||
|
||||
// @todo: we should move SINGLE_TENANT_ORG_ID to shared package or just remove the need to pass this in
|
||||
// when resolving tokens
|
||||
const SINGLE_TENANT_ORG_ID = 1;
|
||||
const privateKey = await getTokenFromConfig(app.privateKey, SINGLE_TENANT_ORG_ID, this.db!);
|
||||
|
||||
const octokitApp = new App({
|
||||
appId: Number(app.id),
|
||||
privateKey: privateKey,
|
||||
});
|
||||
this.octokitApps.set(Number(app.id), octokitApp);
|
||||
|
||||
const installations = await octokitApp.octokit.request("GET /app/installations");
|
||||
logger.info(`Found ${installations.data.length} GitHub App installations for ${deploymentHostname}/${app.id}:`);
|
||||
|
||||
for (const installationData of installations.data) {
|
||||
if (!installationData.account || !installationData.account.login || !installationData.account.type) {
|
||||
logger.warn(`Skipping installation ${installationData.id}: missing account data (${installationData.account})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info(`\tInstallation ID: ${installationData.id}, Account: ${installationData.account.login}, Type: ${installationData.account.type}`);
|
||||
|
||||
const owner = installationData.account.login;
|
||||
const accountType = installationData.account.type.toLowerCase() as 'organization' | 'user';
|
||||
const installationOctokit = await octokitApp.getInstallationOctokit(installationData.id);
|
||||
const auth = await installationOctokit.auth({ type: "installation" }) as { expires_at: string, token: string };
|
||||
|
||||
const installation: Installation = {
|
||||
id: installationData.id,
|
||||
appId: Number(app.id),
|
||||
account: {
|
||||
login: owner,
|
||||
type: accountType,
|
||||
},
|
||||
createdAt: installationData.created_at,
|
||||
expiresAt: auth.expires_at,
|
||||
token: auth.token
|
||||
};
|
||||
this.installationMap.set(this.generateMapKey(owner, deploymentHostname), installation);
|
||||
}
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
public async getInstallationToken(owner: string, deploymentHostname: string = GITHUB_DEFAULT_DEPLOYMENT_HOSTNAME): Promise<string> {
|
||||
this.ensureInitialized();
|
||||
|
||||
const key = this.generateMapKey(owner, deploymentHostname);
|
||||
const installation = this.installationMap.get(key) as Installation | undefined;
|
||||
if (!installation) {
|
||||
throw new Error(`GitHub App Installation not found for ${key}`);
|
||||
}
|
||||
|
||||
if (installation.expiresAt < new Date().toISOString()) {
|
||||
const octokitApp = this.octokitApps.get(installation.appId) as App;
|
||||
const installationOctokit = await octokitApp.getInstallationOctokit(installation.id);
|
||||
const auth = await installationOctokit.auth({ type: "installation" }) as { expires_at: string, token: string };
|
||||
|
||||
const newInstallation: Installation = {
|
||||
...installation,
|
||||
expiresAt: auth.expires_at,
|
||||
token: auth.token
|
||||
};
|
||||
this.installationMap.set(key, newInstallation);
|
||||
|
||||
return newInstallation.token;
|
||||
} else {
|
||||
return installation.token;
|
||||
}
|
||||
}
|
||||
|
||||
public appsConfigured() {
|
||||
return this.octokitApps.size > 0;
|
||||
}
|
||||
|
||||
private generateMapKey(owner: string, deploymentHostname: string): string {
|
||||
return `${deploymentHostname}/${owner}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ const QUEUE_NAME = 'repoPermissionSyncQueue';
|
|||
|
||||
const logger = createLogger('repo-permission-syncer');
|
||||
|
||||
|
||||
export class RepoPermissionSyncer {
|
||||
private queue: Queue<RepoPermissionSyncJob>;
|
||||
private worker: Worker<RepoPermissionSyncJob>;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ import { BackendException, BackendError } from "@sourcebot/error";
|
|||
import { processPromiseResults, throwIfAnyFailed } from "./connectionUtils.js";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { env } from "./env.js";
|
||||
import { GithubAppManager } from "./ee/githubAppManager.js";
|
||||
import { hasEntitlement } from "@sourcebot/shared";
|
||||
|
||||
const logger = createLogger('github');
|
||||
const GITHUB_CLOUD_HOSTNAME = "github.com";
|
||||
|
|
@ -55,6 +57,40 @@ export const createOctokitFromToken = async ({ token, url }: { token?: string, u
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get an authenticated Octokit instance using GitHub App if available,
|
||||
* otherwise falls back to the provided octokit instance.
|
||||
*/
|
||||
const getOctokitWithGithubApp = async (
|
||||
octokit: Octokit,
|
||||
owner: string,
|
||||
url: string | undefined,
|
||||
context: string
|
||||
): Promise<Octokit> => {
|
||||
if (!hasEntitlement('github-app') || !GithubAppManager.getInstance().appsConfigured()) {
|
||||
return octokit;
|
||||
}
|
||||
|
||||
try {
|
||||
const hostname = url ? new URL(url).hostname : GITHUB_CLOUD_HOSTNAME;
|
||||
const token = await GithubAppManager.getInstance().getInstallationToken(owner, hostname);
|
||||
const { octokit: octokitFromToken, isAuthenticated } = await createOctokitFromToken({
|
||||
token,
|
||||
url,
|
||||
});
|
||||
|
||||
if (isAuthenticated) {
|
||||
return octokitFromToken;
|
||||
} else {
|
||||
logger.error(`Failed to authenticate with GitHub App for ${context}. Falling back to legacy token resolution.`);
|
||||
return octokit;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Error getting GitHub App token for ${context}. Falling back to legacy token resolution.`, error);
|
||||
return octokit;
|
||||
}
|
||||
}
|
||||
|
||||
export const getGitHubReposFromConfig = async (config: GithubConnectionConfig, orgId: number, db: PrismaClient, signal: AbortSignal) => {
|
||||
const hostname = config.url ?
|
||||
new URL(config.url).hostname :
|
||||
|
|
@ -107,19 +143,19 @@ export const getGitHubReposFromConfig = async (config: GithubConnectionConfig, o
|
|||
};
|
||||
|
||||
if (config.orgs) {
|
||||
const { validRepos, notFoundOrgs } = await getReposForOrgs(config.orgs, octokit, signal);
|
||||
const { validRepos, notFoundOrgs } = await getReposForOrgs(config.orgs, octokit, signal, config.url);
|
||||
allRepos = allRepos.concat(validRepos);
|
||||
notFound.orgs = notFoundOrgs;
|
||||
}
|
||||
|
||||
if (config.repos) {
|
||||
const { validRepos, notFoundRepos } = await getRepos(config.repos, octokit, signal);
|
||||
const { validRepos, notFoundRepos } = await getRepos(config.repos, octokit, signal, config.url);
|
||||
allRepos = allRepos.concat(validRepos);
|
||||
notFound.repos = notFoundRepos;
|
||||
}
|
||||
|
||||
if (config.users) {
|
||||
const { validRepos, notFoundUsers } = await getReposOwnedByUsers(config.users, octokit, signal);
|
||||
const { validRepos, notFoundUsers } = await getReposOwnedByUsers(config.users, octokit, signal, config.url);
|
||||
allRepos = allRepos.concat(validRepos);
|
||||
notFound.users = notFoundUsers;
|
||||
}
|
||||
|
|
@ -178,11 +214,12 @@ export const getReposForAuthenticatedUser = async (visibility: 'all' | 'private'
|
|||
}
|
||||
}
|
||||
|
||||
const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: AbortSignal) => {
|
||||
const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
|
||||
const results = await Promise.allSettled(users.map(async (user) => {
|
||||
try {
|
||||
logger.debug(`Fetching repository info for user ${user}...`);
|
||||
|
||||
const octokitToUse = await getOctokitWithGithubApp(octokit, user, url, `user ${user}`);
|
||||
const { durationMs, data } = await measure(async () => {
|
||||
const fetchFn = async () => {
|
||||
let query = `user:${user}`;
|
||||
|
|
@ -194,7 +231,7 @@ const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: A
|
|||
// the username as a parameter.
|
||||
// @see: https://github.com/orgs/community/discussions/24382#discussioncomment-3243958
|
||||
// @see: https://api.github.com/search/repositories?q=user:USERNAME
|
||||
const searchResults = await octokit.paginate(octokit.rest.search.repos, {
|
||||
const searchResults = await octokitToUse.paginate(octokitToUse.rest.search.repos, {
|
||||
q: query,
|
||||
per_page: 100,
|
||||
request: {
|
||||
|
|
@ -237,13 +274,14 @@ const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: A
|
|||
};
|
||||
}
|
||||
|
||||
const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSignal) => {
|
||||
const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
|
||||
const results = await Promise.allSettled(orgs.map(async (org) => {
|
||||
try {
|
||||
logger.info(`Fetching repository info for org ${org}...`);
|
||||
|
||||
const octokitToUse = await getOctokitWithGithubApp(octokit, org, url, `org ${org}`);
|
||||
const { durationMs, data } = await measure(async () => {
|
||||
const fetchFn = () => octokit.paginate(octokit.repos.listForOrg, {
|
||||
const fetchFn = () => octokitToUse.paginate(octokitToUse.repos.listForOrg, {
|
||||
org: org,
|
||||
per_page: 100,
|
||||
request: {
|
||||
|
|
@ -283,14 +321,15 @@ const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSi
|
|||
};
|
||||
}
|
||||
|
||||
const getRepos = async (repoList: string[], octokit: Octokit, signal: AbortSignal) => {
|
||||
const getRepos = async (repoList: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
|
||||
const results = await Promise.allSettled(repoList.map(async (repo) => {
|
||||
try {
|
||||
const [owner, repoName] = repo.split('/');
|
||||
logger.info(`Fetching repository info for ${repo}...`);
|
||||
|
||||
const octokitToUse = await getOctokitWithGithubApp(octokit, owner, url, `repo ${repo}`);
|
||||
const { durationMs, data: result } = await measure(async () => {
|
||||
const fetchFn = () => octokit.repos.get({
|
||||
const fetchFn = () => octokitToUse.repos.get({
|
||||
owner,
|
||||
repo: repoName,
|
||||
request: {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { ConnectionManager } from './connectionManager.js';
|
|||
import { DEFAULT_SETTINGS, INDEX_CACHE_DIR, REPOS_CACHE_DIR } from './constants.js';
|
||||
import { RepoPermissionSyncer } from './ee/repoPermissionSyncer.js';
|
||||
import { UserPermissionSyncer } from "./ee/userPermissionSyncer.js";
|
||||
import { GithubAppManager } from "./ee/githubAppManager.js";
|
||||
import { env } from "./env.js";
|
||||
import { RepoIndexManager } from "./repoIndexManager.js";
|
||||
import { PromClient } from './promClient.js';
|
||||
|
|
@ -58,6 +59,11 @@ const promClient = new PromClient();
|
|||
|
||||
const settings = await getSettings(env.CONFIG_PATH);
|
||||
|
||||
|
||||
if (hasEntitlement('github-app')) {
|
||||
await GithubAppManager.getInstance().init(prisma);
|
||||
}
|
||||
|
||||
const connectionManager = new ConnectionManager(prisma, settings, redis);
|
||||
const repoPermissionSyncer = new RepoPermissionSyncer(prisma, settings, redis);
|
||||
const userPermissionSyncer = new UserPermissionSyncer(prisma, settings, redis);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { getTokenFromConfig as getTokenFromConfigBase } from "@sourcebot/crypto"
|
|||
import { BackendException, BackendError } from "@sourcebot/error";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { GithubConnectionConfig, GitlabConnectionConfig, GiteaConnectionConfig, BitbucketConnectionConfig, AzureDevOpsConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
|
||||
import { GithubAppManager } from "./ee/githubAppManager.js";
|
||||
import { hasEntitlement } from "@sourcebot/shared";
|
||||
import { REPOS_CACHE_DIR } from "./constants.js";
|
||||
|
||||
export const measure = async <T>(cb: () => Promise<T>) => {
|
||||
|
|
@ -126,6 +128,28 @@ 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, db: PrismaClient, logger?: Logger): Promise<RepoAuthCredentials | undefined> => {
|
||||
// If we have github apps configured we assume that we must use them for github service auth
|
||||
if (repo.external_codeHostType === 'github' && hasEntitlement('github-app') && GithubAppManager.getInstance().appsConfigured()) {
|
||||
const owner = repo.displayName?.split('/')[0];
|
||||
const deploymentHostname = new URL(repo.external_codeHostUrl).hostname;
|
||||
if (!owner || !deploymentHostname) {
|
||||
throw new Error(`Failed to fetch GitHub App for repo ${repo.displayName}:Invalid repo displayName (${repo.displayName}) or deployment hostname (${deploymentHostname})`);
|
||||
}
|
||||
|
||||
const token = await GithubAppManager.getInstance().getInstallationToken(owner, deploymentHostname);
|
||||
return {
|
||||
hostUrl: repo.external_codeHostUrl,
|
||||
token,
|
||||
cloneUrlWithToken: createGitCloneUrlWithToken(
|
||||
repo.cloneUrl,
|
||||
{
|
||||
username: 'x-access-token',
|
||||
password: token
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
for (const { connection } of repo.connections) {
|
||||
if (connection.connectionType === 'github') {
|
||||
const config = connection.config as unknown as GithubConnectionConfig;
|
||||
|
|
|
|||
81
packages/schemas/src/v3/app.schema.ts
Normal file
81
packages/schemas/src/v3/app.schema.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||
const schema = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AppConfig",
|
||||
"oneOf": [
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"description": "The private key of the GitHub App.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
} as const;
|
||||
export { schema as appSchema };
|
||||
6
packages/schemas/src/v3/app.type.ts
Normal file
6
packages/schemas/src/v3/app.type.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||
|
||||
export type AppConfig = GithubAppConfig;
|
||||
export type GithubAppConfig = {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
75
packages/schemas/src/v3/githubApp.schema.ts
Normal file
75
packages/schemas/src/v3/githubApp.schema.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||
const schema = {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"description": "The private key of the GitHub App.",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
} as const;
|
||||
export { schema as githubAppSchema };
|
||||
34
packages/schemas/src/v3/githubApp.type.ts
Normal file
34
packages/schemas/src/v3/githubApp.type.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||
|
||||
export type GithubAppConfig = {
|
||||
/**
|
||||
* GitHub App Configuration
|
||||
*/
|
||||
type: "githubApp";
|
||||
/**
|
||||
* The hostname of the GitHub App deployment.
|
||||
*/
|
||||
deploymentHostname?: string;
|
||||
/**
|
||||
* The ID of the GitHub App.
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* The private key of the GitHub App.
|
||||
*/
|
||||
privateKey?:
|
||||
| {
|
||||
/**
|
||||
* The name of the secret that contains the token.
|
||||
*/
|
||||
secret: string;
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* The name of the environment variable that contains the token. Only supported in declarative connection configs.
|
||||
*/
|
||||
env: string;
|
||||
};
|
||||
} & {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
|
|
@ -4272,6 +4272,89 @@ const schema = {
|
|||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"description": "Defines a collection of apps that are available to Sourcebot.",
|
||||
"items": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AppConfig",
|
||||
"oneOf": [
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The name of the secret that contains the token."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"secret"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"env": {
|
||||
"type": "string",
|
||||
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"env"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"description": "The private key of the GitHub App."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": [
|
||||
"privateKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"privateKeyPath"
|
||||
]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
|
|
@ -25,6 +25,10 @@ export type LanguageModel =
|
|||
| OpenAICompatibleLanguageModel
|
||||
| OpenRouterLanguageModel
|
||||
| XaiLanguageModel;
|
||||
export type AppConfig = GithubAppConfig;
|
||||
export type GithubAppConfig = {
|
||||
[k: string]: unknown;
|
||||
};
|
||||
|
||||
export interface SourcebotConfig {
|
||||
$schema?: string;
|
||||
|
|
@ -45,6 +49,10 @@ export interface SourcebotConfig {
|
|||
* Defines a collection of language models that are available to Sourcebot.
|
||||
*/
|
||||
models?: LanguageModel[];
|
||||
/**
|
||||
* Defines a collection of apps that are available to Sourcebot.
|
||||
*/
|
||||
apps?: AppConfig[];
|
||||
}
|
||||
/**
|
||||
* Defines the global settings for Sourcebot.
|
||||
|
|
|
|||
|
|
@ -39,15 +39,16 @@ const entitlements = [
|
|||
"code-nav",
|
||||
"audit",
|
||||
"analytics",
|
||||
"permission-syncing"
|
||||
"permission-syncing",
|
||||
"github-app"
|
||||
] as const;
|
||||
export type Entitlement = (typeof entitlements)[number];
|
||||
|
||||
const entitlementsByPlan: Record<Plan, Entitlement[]> = {
|
||||
oss: ["anonymous-access"],
|
||||
"cloud:team": ["billing", "multi-tenancy", "sso", "code-nav"],
|
||||
"self-hosted:enterprise": ["search-contexts", "sso", "code-nav", "audit", "analytics", "permission-syncing"],
|
||||
"self-hosted:enterprise-unlimited": ["search-contexts", "anonymous-access", "sso", "code-nav", "audit", "analytics", "permission-syncing"],
|
||||
"self-hosted:enterprise": ["search-contexts", "sso", "code-nav", "audit", "analytics", "permission-syncing", "github-app"],
|
||||
"self-hosted:enterprise-unlimited": ["search-contexts", "anonymous-access", "sso", "code-nav", "audit", "analytics", "permission-syncing", "github-app"],
|
||||
// Special entitlement for https://demo.sourcebot.dev
|
||||
"cloud:demo": ["anonymous-access", "code-nav", "search-contexts"],
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const agents = [
|
|||
id: "review-agent",
|
||||
name: "Review Agent",
|
||||
description: "An AI code review agent that reviews your PRs. Uses the code indexed on Sourcebot to provide codebase-wide context.",
|
||||
requiredEnvVars: ["GITHUB_APP_ID", "GITHUB_APP_WEBHOOK_SECRET", "GITHUB_APP_PRIVATE_KEY_PATH", "OPENAI_API_KEY"],
|
||||
requiredEnvVars: ["GITHUB_REVIEW_AGENT_APP_ID", "GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET", "GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH", "OPENAI_API_KEY"],
|
||||
configureUrl: "https://docs.sourcebot.dev/docs/features/agents/review-agent"
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -73,19 +73,24 @@ export const SettingsDropdown = ({
|
|||
<Settings className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-64">
|
||||
<DropdownMenuContent className="w-64" align="end" sideOffset={5}>
|
||||
{session?.user ? (
|
||||
<DropdownMenuGroup>
|
||||
<div className="flex flex-row items-start gap-3 p-2">
|
||||
<Avatar className="flex-shrink-0">
|
||||
<div className="flex flex-row items-center gap-3 px-3 py-3">
|
||||
<Avatar className="h-10 w-10 flex-shrink-0">
|
||||
<AvatarImage
|
||||
src={session.user.image ?? ""}
|
||||
/>
|
||||
<AvatarFallback>
|
||||
{session.user.name && session.user.name.length > 0 ? session.user.name[0] : 'U'}
|
||||
<AvatarFallback className="bg-primary/10 text-primary font-semibold">
|
||||
{session.user.name && session.user.name.length > 0 ? session.user.name[0].toUpperCase() : 'U'}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<p className="text-sm font-medium break-all flex-1 leading-relaxed">{session.user.email ?? "User"}</p>
|
||||
<div className="flex flex-col flex-1 min-w-0">
|
||||
<p className="text-sm font-semibold truncate">{session.user.name ?? "User"}</p>
|
||||
{session.user.email && (
|
||||
<p className="text-xs text-muted-foreground truncate">{session.user.email}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -14,16 +14,16 @@ import { createLogger } from "@sourcebot/logger";
|
|||
const logger = createLogger('github-webhook');
|
||||
|
||||
let githubApp: App | undefined;
|
||||
if (env.GITHUB_APP_ID && env.GITHUB_APP_WEBHOOK_SECRET && env.GITHUB_APP_PRIVATE_KEY_PATH) {
|
||||
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
||||
try {
|
||||
const privateKey = fs.readFileSync(env.GITHUB_APP_PRIVATE_KEY_PATH, "utf8");
|
||||
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
||||
|
||||
const throttledOctokit = Octokit.plugin(throttling);
|
||||
githubApp = new App({
|
||||
appId: env.GITHUB_APP_ID,
|
||||
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
||||
privateKey: privateKey,
|
||||
webhooks: {
|
||||
secret: env.GITHUB_APP_WEBHOOK_SECRET,
|
||||
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
||||
},
|
||||
Octokit: throttledOctokit,
|
||||
throttle: {
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ export const getSSOProviders = (): Provider[] => {
|
|||
const providers: Provider[] = [];
|
||||
|
||||
if (env.AUTH_EE_GITHUB_CLIENT_ID && env.AUTH_EE_GITHUB_CLIENT_SECRET) {
|
||||
const baseUrl = env.AUTH_EE_GITHUB_BASE_URL ?? "https://github.com";
|
||||
const apiUrl = env.AUTH_EE_GITHUB_BASE_URL ? `${env.AUTH_EE_GITHUB_BASE_URL}/api/v3` : "https://api.github.com";
|
||||
providers.push(GitHub({
|
||||
clientId: env.AUTH_EE_GITHUB_CLIENT_ID,
|
||||
clientSecret: env.AUTH_EE_GITHUB_CLIENT_SECRET,
|
||||
enterprise: {
|
||||
baseUrl: env.AUTH_EE_GITHUB_BASE_URL,
|
||||
},
|
||||
authorization: {
|
||||
url: `${baseUrl}/login/oauth/authorize`,
|
||||
params: {
|
||||
scope: [
|
||||
'read:user',
|
||||
|
|
@ -41,12 +41,6 @@ export const getSSOProviders = (): Provider[] => {
|
|||
].join(' '),
|
||||
},
|
||||
},
|
||||
token: {
|
||||
url: `${baseUrl}/login/oauth/access_token`,
|
||||
},
|
||||
userinfo: {
|
||||
url: `${apiUrl}/user`,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,9 +85,9 @@ export const env = createEnv({
|
|||
SOURCEBOT_EE_AUDIT_LOGGING_ENABLED: booleanSchema.default('true'),
|
||||
|
||||
// GitHub app for review agent
|
||||
GITHUB_APP_ID: z.string().optional(),
|
||||
GITHUB_APP_WEBHOOK_SECRET: z.string().optional(),
|
||||
GITHUB_APP_PRIVATE_KEY_PATH: z.string().optional(),
|
||||
GITHUB_REVIEW_AGENT_APP_ID: z.string().optional(),
|
||||
GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET: z.string().optional(),
|
||||
GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH: z.string().optional(),
|
||||
REVIEW_AGENT_API_KEY: z.string().optional(),
|
||||
REVIEW_AGENT_LOGGING_ENABLED: booleanSchema.default('true'),
|
||||
REVIEW_AGENT_AUTO_REVIEW_ENABLED: booleanSchema.default('false'),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
'use server';
|
||||
|
||||
import { env } from "@/env.mjs";
|
||||
import { invalidZoektResponse, ServiceError } from "../../lib/serviceError";
|
||||
import { isServiceError } from "../../lib/utils";
|
||||
import { zoektFetch } from "./zoektClient";
|
||||
|
|
|
|||
9
schemas/v3/app.json
Normal file
9
schemas/v3/app.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "AppConfig",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "./githubApp.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
42
schemas/v3/githubApp.json
Normal file
42
schemas/v3/githubApp.json
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"title": "GithubAppConfig",
|
||||
"properties": {
|
||||
"type": {
|
||||
"const": "githubApp",
|
||||
"description": "GitHub App Configuration"
|
||||
},
|
||||
"deploymentHostname": {
|
||||
"type": "string",
|
||||
"format": "hostname",
|
||||
"default": "github.com",
|
||||
"description": "The hostname of the GitHub App deployment.",
|
||||
"examples": [
|
||||
"github.com",
|
||||
"github.example.com"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The ID of the GitHub App."
|
||||
},
|
||||
"privateKey": {
|
||||
"$ref": "./shared.json#/definitions/Token",
|
||||
"description": "The private key of the GitHub App."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"id"
|
||||
],
|
||||
"oneOf": [
|
||||
{
|
||||
"required": ["privateKey"]
|
||||
},
|
||||
{
|
||||
"required": ["privateKeyPath"]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
@ -118,6 +118,13 @@
|
|||
"items": {
|
||||
"$ref": "./languageModel.json"
|
||||
}
|
||||
},
|
||||
"apps": {
|
||||
"type": "array",
|
||||
"description": "Defines a collection of apps that are available to Sourcebot.",
|
||||
"items": {
|
||||
"$ref": "./app.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
|||
246
yarn.lock
246
yarn.lock
|
|
@ -3412,6 +3412,21 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/app@npm:^16.1.1":
|
||||
version: 16.1.1
|
||||
resolution: "@octokit/app@npm:16.1.1"
|
||||
dependencies:
|
||||
"@octokit/auth-app": "npm:^8.1.1"
|
||||
"@octokit/auth-unauthenticated": "npm:^7.0.2"
|
||||
"@octokit/core": "npm:^7.0.5"
|
||||
"@octokit/oauth-app": "npm:^8.0.2"
|
||||
"@octokit/plugin-paginate-rest": "npm:^13.2.0"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
"@octokit/webhooks": "npm:^14.0.0"
|
||||
checksum: 10c0/1989da2151db879e85d0dbe7f1fdfff613a0d00b65eb58b8c08617186726ea9c64061639e6c630d6840772f084468d7cdfd48c001fc7f91313b67cf58a827d81
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-app@npm:^7.2.1":
|
||||
version: 7.2.1
|
||||
resolution: "@octokit/auth-app@npm:7.2.1"
|
||||
|
|
@ -3428,6 +3443,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-app@npm:^8.1.1":
|
||||
version: 8.1.1
|
||||
resolution: "@octokit/auth-app@npm:8.1.1"
|
||||
dependencies:
|
||||
"@octokit/auth-oauth-app": "npm:^9.0.2"
|
||||
"@octokit/auth-oauth-user": "npm:^6.0.1"
|
||||
"@octokit/request": "npm:^10.0.5"
|
||||
"@octokit/request-error": "npm:^7.0.1"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
toad-cache: "npm:^3.7.0"
|
||||
universal-github-app-jwt: "npm:^2.2.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/f33d12a5e7339135d013f10ae6e5f6f6f9b94af860df6b300eb158f27c445e626fd08e2d2d8eb39cdab1391918ed244a1e041ebf3c470074f42b8573c52d1dc7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-app@npm:^8.1.3, @octokit/auth-oauth-app@npm:^8.1.4":
|
||||
version: 8.1.4
|
||||
resolution: "@octokit/auth-oauth-app@npm:8.1.4"
|
||||
|
|
@ -3441,6 +3472,19 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-app@npm:^9.0.2":
|
||||
version: 9.0.2
|
||||
resolution: "@octokit/auth-oauth-app@npm:9.0.2"
|
||||
dependencies:
|
||||
"@octokit/auth-oauth-device": "npm:^8.0.2"
|
||||
"@octokit/auth-oauth-user": "npm:^6.0.1"
|
||||
"@octokit/request": "npm:^10.0.5"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/85f4dc9caad7fa0b963ac33452ad6af3488c0b4b07d2b39b624f1bb8eec4bc5ccf3bfdd379db133439afd437d1389bd4674ba66911d685ebb5cc31c713f04594
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-device@npm:^7.1.5":
|
||||
version: 7.1.5
|
||||
resolution: "@octokit/auth-oauth-device@npm:7.1.5"
|
||||
|
|
@ -3453,6 +3497,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-device@npm:^8.0.2":
|
||||
version: 8.0.2
|
||||
resolution: "@octokit/auth-oauth-device@npm:8.0.2"
|
||||
dependencies:
|
||||
"@octokit/oauth-methods": "npm:^6.0.1"
|
||||
"@octokit/request": "npm:^10.0.5"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/b69b2bda7139b4c49c7ac70f86da53317fcf49e6562fe734ecebef2ce5c8e8f973bf00e6b29e0a45cd88346fc335e73d70c53920b438e73f165590f4fb58864f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-user@npm:^5.1.3, @octokit/auth-oauth-user@npm:^5.1.4":
|
||||
version: 5.1.4
|
||||
resolution: "@octokit/auth-oauth-user@npm:5.1.4"
|
||||
|
|
@ -3466,6 +3522,19 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-oauth-user@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "@octokit/auth-oauth-user@npm:6.0.1"
|
||||
dependencies:
|
||||
"@octokit/auth-oauth-device": "npm:^8.0.2"
|
||||
"@octokit/oauth-methods": "npm:^6.0.1"
|
||||
"@octokit/request": "npm:^10.0.5"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/8e4c8172be454a46eab05ac5ae1c6cc2984ffee7378f9196d441a53cdba9b722b8f408b9dd4b42fe736cda4144d7a22d2e026888d2ff14c8865e020cb760379a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-token@npm:^5.0.0":
|
||||
version: 5.1.2
|
||||
resolution: "@octokit/auth-token@npm:5.1.2"
|
||||
|
|
@ -3473,6 +3542,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-token@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "@octokit/auth-token@npm:6.0.0"
|
||||
checksum: 10c0/32ecc904c5f6f4e5d090bfcc679d70318690c0a0b5040cd9a25811ad9dcd44c33f2cf96b6dbee1cd56cf58fde28fb1819c01b58718aa5c971f79c822357cb5c0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-unauthenticated@npm:^6.1.2, @octokit/auth-unauthenticated@npm:^6.1.3":
|
||||
version: 6.1.3
|
||||
resolution: "@octokit/auth-unauthenticated@npm:6.1.3"
|
||||
|
|
@ -3483,6 +3559,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/auth-unauthenticated@npm:^7.0.2":
|
||||
version: 7.0.2
|
||||
resolution: "@octokit/auth-unauthenticated@npm:7.0.2"
|
||||
dependencies:
|
||||
"@octokit/request-error": "npm:^7.0.1"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
checksum: 10c0/07a05683c0451232d1d952e725ad3a4d50dfe5487452f93cfe54b99220b46a5e94a04084b7954738f9d8437f91afce3ab535bc67fa787f711452063baf5f6821
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/core@npm:^6.1.4":
|
||||
version: 6.1.4
|
||||
resolution: "@octokit/core@npm:6.1.4"
|
||||
|
|
@ -3513,6 +3599,21 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/core@npm:^7.0.5":
|
||||
version: 7.0.5
|
||||
resolution: "@octokit/core@npm:7.0.5"
|
||||
dependencies:
|
||||
"@octokit/auth-token": "npm:^6.0.0"
|
||||
"@octokit/graphql": "npm:^9.0.2"
|
||||
"@octokit/request": "npm:^10.0.4"
|
||||
"@octokit/request-error": "npm:^7.0.1"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
before-after-hook: "npm:^4.0.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/09aeba5f9a6b58c4e7cdd59d883a1b787bc32b17fee3b6c73af47e9b8510dc1aa6e2399274e36106ca27485d4e7b2ffda28af306ad4819fa96cd90caecf15ae7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/endpoint@npm:^10.1.3":
|
||||
version: 10.1.3
|
||||
resolution: "@octokit/endpoint@npm:10.1.3"
|
||||
|
|
@ -3533,6 +3634,16 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/endpoint@npm:^11.0.1":
|
||||
version: 11.0.1
|
||||
resolution: "@octokit/endpoint@npm:11.0.1"
|
||||
dependencies:
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
universal-user-agent: "npm:^7.0.2"
|
||||
checksum: 10c0/a445c42a4cef357f7a181ac1dc5970db7d6c3bb36c81e10dd4032020873d4ec97402f08ebfa6ea747de8edd255ccf19a57cbb66dc4a05e5cff8c0445e29cd73d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/graphql@npm:^8.1.2":
|
||||
version: 8.2.1
|
||||
resolution: "@octokit/graphql@npm:8.2.1"
|
||||
|
|
@ -3555,6 +3666,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/graphql@npm:^9.0.2":
|
||||
version: 9.0.2
|
||||
resolution: "@octokit/graphql@npm:9.0.2"
|
||||
dependencies:
|
||||
"@octokit/request": "npm:^10.0.4"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/aaba3de627475ac2be24d676be643c85bec089b1d9ef2c3a678fab03a525c0fd9b6c61622d190e84447ecb6aa9271882f8bcce5c278221337fd4be68d36acf10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-app@npm:^7.1.6":
|
||||
version: 7.1.6
|
||||
resolution: "@octokit/oauth-app@npm:7.1.6"
|
||||
|
|
@ -3571,6 +3693,22 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-app@npm:^8.0.2":
|
||||
version: 8.0.3
|
||||
resolution: "@octokit/oauth-app@npm:8.0.3"
|
||||
dependencies:
|
||||
"@octokit/auth-oauth-app": "npm:^9.0.2"
|
||||
"@octokit/auth-oauth-user": "npm:^6.0.1"
|
||||
"@octokit/auth-unauthenticated": "npm:^7.0.2"
|
||||
"@octokit/core": "npm:^7.0.5"
|
||||
"@octokit/oauth-authorization-url": "npm:^8.0.0"
|
||||
"@octokit/oauth-methods": "npm:^6.0.1"
|
||||
"@types/aws-lambda": "npm:^8.10.83"
|
||||
universal-user-agent: "npm:^7.0.0"
|
||||
checksum: 10c0/7bb064bfe21a6db45b57f8f5e5ecd533e802c0073d293b1c75abf264d9bc0fc952492757ea5af516207af4ec781cf793cb37e8cbbb3a8df691592bc25c364644
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-authorization-url@npm:^7.0.0, @octokit/oauth-authorization-url@npm:^7.1.1":
|
||||
version: 7.1.1
|
||||
resolution: "@octokit/oauth-authorization-url@npm:7.1.1"
|
||||
|
|
@ -3578,6 +3716,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-authorization-url@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "@octokit/oauth-authorization-url@npm:8.0.0"
|
||||
checksum: 10c0/ab4964bebd8d076f945a2f3210a8a0a221a408362569d9fc2f49875ad06e594365f5fd871dac08d820793f687bff50237f7acf40d9d39c5f9de7575b6f4bad93
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-methods@npm:^5.1.4, @octokit/oauth-methods@npm:^5.1.5":
|
||||
version: 5.1.5
|
||||
resolution: "@octokit/oauth-methods@npm:5.1.5"
|
||||
|
|
@ -3590,6 +3735,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/oauth-methods@npm:^6.0.1":
|
||||
version: 6.0.1
|
||||
resolution: "@octokit/oauth-methods@npm:6.0.1"
|
||||
dependencies:
|
||||
"@octokit/oauth-authorization-url": "npm:^8.0.0"
|
||||
"@octokit/request": "npm:^10.0.5"
|
||||
"@octokit/request-error": "npm:^7.0.1"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
checksum: 10c0/b9aa6458f8ffdb067552cdc7783038a075401b9b3875c970ca23a29157f75573a18240b1327fb732be30468857abea41761de1f28a50a7c549ff0ced24c9d499
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/openapi-types@npm:^24.2.0":
|
||||
version: 24.2.0
|
||||
resolution: "@octokit/openapi-types@npm:24.2.0"
|
||||
|
|
@ -3604,6 +3761,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/openapi-types@npm:^26.0.0":
|
||||
version: 26.0.0
|
||||
resolution: "@octokit/openapi-types@npm:26.0.0"
|
||||
checksum: 10c0/671f12c1db70b4bc8c719ec7aa10de034925f4326db0fff22837afcc0b41fd1c015d164673ef5603c5ac787a430c514b821852bfbe6f06edc4a41ad3de342e94
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/openapi-webhooks-types@npm:11.0.0":
|
||||
version: 11.0.0
|
||||
resolution: "@octokit/openapi-webhooks-types@npm:11.0.0"
|
||||
|
|
@ -3611,6 +3775,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/openapi-webhooks-types@npm:12.0.3":
|
||||
version: 12.0.3
|
||||
resolution: "@octokit/openapi-webhooks-types@npm:12.0.3"
|
||||
checksum: 10c0/1c2429bd939edcf6a6e8db7240a2138a897cd7d6a0922ac083d8ed58868866b036b90888eb22d1b952df75d5741e2df1aa02d38f182c5be83394dc30b24d657d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/plugin-paginate-graphql@npm:^5.2.4":
|
||||
version: 5.2.4
|
||||
resolution: "@octokit/plugin-paginate-graphql@npm:5.2.4"
|
||||
|
|
@ -3642,6 +3813,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/plugin-paginate-rest@npm:^13.2.0":
|
||||
version: 13.2.1
|
||||
resolution: "@octokit/plugin-paginate-rest@npm:13.2.1"
|
||||
dependencies:
|
||||
"@octokit/types": "npm:^15.0.1"
|
||||
peerDependencies:
|
||||
"@octokit/core": ">=6"
|
||||
checksum: 10c0/16cd034ee6426f742514d0ca553a2c4355cd68c2eb9211030f3ec2538f4c833d587b3737bb720e34f98be8fae15acb07693d17314350cf067557abb4cb1598fb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/plugin-request-log@npm:^5.3.1":
|
||||
version: 5.3.1
|
||||
resolution: "@octokit/plugin-request-log@npm:5.3.1"
|
||||
|
|
@ -3716,6 +3898,28 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/request-error@npm:^7.0.0, @octokit/request-error@npm:^7.0.1":
|
||||
version: 7.0.1
|
||||
resolution: "@octokit/request-error@npm:7.0.1"
|
||||
dependencies:
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
checksum: 10c0/c3f29db87a8d59b8217cbda8cb32be4a553de21ab08bac7ec5909e7c4a4934a32a07575547049fb11a07f0eeec45d0ae5c38295995445adda4ae17b2c66cba85
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/request@npm:^10.0.4, @octokit/request@npm:^10.0.5":
|
||||
version: 10.0.5
|
||||
resolution: "@octokit/request@npm:10.0.5"
|
||||
dependencies:
|
||||
"@octokit/endpoint": "npm:^11.0.1"
|
||||
"@octokit/request-error": "npm:^7.0.1"
|
||||
"@octokit/types": "npm:^15.0.0"
|
||||
fast-content-type-parse: "npm:^3.0.0"
|
||||
universal-user-agent: "npm:^7.0.2"
|
||||
checksum: 10c0/66b607ec97280ce2a857826b7c862a48d81fdafe97c7b6b527ce7bf83b0f6eb706ce3df44eafb57c7ed0ee0b5f255db1c1471ed6d9152b8932e6e88feb845bba
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/request@npm:^9.2.1, @octokit/request@npm:^9.2.2":
|
||||
version: 9.2.2
|
||||
resolution: "@octokit/request@npm:9.2.2"
|
||||
|
|
@ -3772,6 +3976,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/types@npm:^15.0.0, @octokit/types@npm:^15.0.1":
|
||||
version: 15.0.1
|
||||
resolution: "@octokit/types@npm:15.0.1"
|
||||
dependencies:
|
||||
"@octokit/openapi-types": "npm:^26.0.0"
|
||||
checksum: 10c0/f1f8d8a988c6295d669461082936a4e27d5a021ff870ebb93b8afa8f227f6eb0fb520f98631af31fc56dea0cb84e15df65e736f408cde321693154e4432c575d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/webhooks-methods@npm:^5.1.1":
|
||||
version: 5.1.1
|
||||
resolution: "@octokit/webhooks-methods@npm:5.1.1"
|
||||
|
|
@ -3779,6 +3992,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/webhooks-methods@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "@octokit/webhooks-methods@npm:6.0.0"
|
||||
checksum: 10c0/7f10740e838d65c78e859bb041499cca69df7831e9f633ee70a46ca8e53d0844f2c84500df204453d171c8c3c0f8eb8b68716ee1d5c95e3cf5d09690f32e13e1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/webhooks@npm:^13.6.1":
|
||||
version: 13.8.2
|
||||
resolution: "@octokit/webhooks@npm:13.8.2"
|
||||
|
|
@ -3790,6 +4010,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@octokit/webhooks@npm:^14.0.0":
|
||||
version: 14.1.3
|
||||
resolution: "@octokit/webhooks@npm:14.1.3"
|
||||
dependencies:
|
||||
"@octokit/openapi-webhooks-types": "npm:12.0.3"
|
||||
"@octokit/request-error": "npm:^7.0.0"
|
||||
"@octokit/webhooks-methods": "npm:^6.0.0"
|
||||
checksum: 10c0/7f423700784cb769f15303353154e841f66e031289a25ce44852883121ad9752f0b9ea06bde1388ff38310f64208b6de748e2c24ccd9f3021f708e5e9c6ecfac
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@openrouter/ai-sdk-provider@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "@openrouter/ai-sdk-provider@npm:1.1.0"
|
||||
|
|
@ -7546,6 +7777,7 @@ __metadata:
|
|||
dependencies:
|
||||
"@coderabbitai/bitbucket": "npm:^1.1.3"
|
||||
"@gitbeaker/rest": "npm:^40.5.1"
|
||||
"@octokit/app": "npm:^16.1.1"
|
||||
"@octokit/rest": "npm:^21.0.2"
|
||||
"@sentry/cli": "npm:^2.42.2"
|
||||
"@sentry/node": "npm:^9.3.0"
|
||||
|
|
@ -9607,6 +9839,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"before-after-hook@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "before-after-hook@npm:4.0.0"
|
||||
checksum: 10c0/9f8ae8d1b06142bcfb9ef6625226b5e50348bb11210f266660eddcf9734e0db6f9afc4cb48397ee3f5ac0a3728f3ae401cdeea88413f7bed748a71db84657be2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"best-effort-json-parser@npm:^1.1.2":
|
||||
version: 1.1.3
|
||||
resolution: "best-effort-json-parser@npm:1.1.3"
|
||||
|
|
@ -12316,6 +12555,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-content-type-parse@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "fast-content-type-parse@npm:3.0.0"
|
||||
checksum: 10c0/06251880c83b7118af3a5e66e8bcee60d44f48b39396fc60acc2b4630bd5f3e77552b999b5c8e943d45a818854360e5e97164c374ec4b562b4df96a2cdf2e188
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-copy@npm:^3.0.2":
|
||||
version: 3.0.2
|
||||
resolution: "fast-copy@npm:3.0.2"
|
||||
|
|
|
|||
Loading…
Reference in a new issue