From 45da8ee741de3213f21591933d014f234995a67b Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Mon, 26 Aug 2024 17:53:56 -0700 Subject: [PATCH 1/3] more --- apps/webservice/package.json | 2 + .../(integration)/github/GithubConfigFile.tsx | 15 +- .../(integration)/github/page.tsx | 9 +- .../app/api/github/config/handle-configs.ts | 206 ++++++++++++++++++ .../src/app/api/github/config/route.ts | 174 +++++++++++++++ apps/webservice/src/env.ts | 2 + packages/api/src/router/github.ts | 15 +- packages/db/src/schema/github.ts | 43 ++-- pnpm-lock.yaml | 22 +- 9 files changed, 445 insertions(+), 43 deletions(-) create mode 100644 apps/webservice/src/app/api/github/config/handle-configs.ts create mode 100644 apps/webservice/src/app/api/github/config/route.ts diff --git a/apps/webservice/package.json b/apps/webservice/package.json index e9efe6128..85c764fd3 100644 --- a/apps/webservice/package.json +++ b/apps/webservice/package.json @@ -24,6 +24,8 @@ "@internationalized/date": "^3.5.4", "@monaco-editor/react": "^4.6.0", "@octokit/auth-app": "^7.1.0", + "@octokit/rest": "^20.1.1", + "@octokit/webhooks-types": "^7.5.1", "@t3-oss/env-nextjs": "^0.10.1", "@tailwindcss/typography": "^0.5.13", "@tanstack/react-query": "^5.35.5", diff --git a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx index 3cd076bcb..61cfe3941 100644 --- a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx @@ -1,3 +1,4 @@ +import { GithubConfigFile } from "@ctrlplane/db/schema"; import { Card, CardContent, @@ -7,15 +8,9 @@ import { } from "@ctrlplane/ui/card"; import { Separator } from "@ctrlplane/ui/separator"; -import { api } from "~/trpc/react"; - export const GithubConfigFileSync: React.FC<{ - workspaceId?: string; -}> = ({ workspaceId }) => { - const configFiles = api.github.configFile.list.useQuery(workspaceId ?? "", { - enabled: workspaceId != null, - }); - + configFiles: GithubConfigFile[]; +}> = ({ configFiles }) => { return ( @@ -31,8 +26,8 @@ export const GithubConfigFileSync: React.FC<{ - {configFiles.data?.map((configFile) => ( -
{configFile.name}
+ {configFiles.map((configFile) => ( +
{configFile.path}
))}
diff --git a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx index 78037835a..bfe78a071 100644 --- a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx @@ -29,6 +29,13 @@ export default function GitHubIntegrationPage({ enabled: session.status === "authenticated", }); + const configFiles = api.github.configFile.list.useQuery( + workspace.data?.id ?? "", + { + enabled: workspace.data != null, + }, + ); + return (
@@ -77,7 +84,7 @@ export default function GitHubIntegrationPage({ loading={workspace.isLoading || githubUser.isLoading} /> - +
); } diff --git a/apps/webservice/src/app/api/github/config/handle-configs.ts b/apps/webservice/src/app/api/github/config/handle-configs.ts new file mode 100644 index 000000000..c19e3cffb --- /dev/null +++ b/apps/webservice/src/app/api/github/config/handle-configs.ts @@ -0,0 +1,206 @@ +import { and, eq, inArray } from "@ctrlplane/db"; +import { db } from "@ctrlplane/db/client"; +import { + deployment, + githubConfigFile, + GithubOrganization, + system, + workspace, +} from "@ctrlplane/db/schema"; + +interface DeploymentConfig { + name: string; + slug: string; + system: string; + workspace: string; + description?: string; +} + +interface ParsedConfig { + path: string; + repositoryName: string; + branch: string; + deployments: DeploymentConfig[]; +} + +export const handleNewConfigs = async ( + newParsedConfigs: ParsedConfig[], + internalOrganization: GithubOrganization, + repositoryName: string, +) => { + if (newParsedConfigs.length === 0) return; + + const newConfigs = await db + .insert(githubConfigFile) + .values( + newParsedConfigs.map((cf) => ({ + ...cf, + workspaceId: internalOrganization.workspaceId, + organizationId: internalOrganization.id, + repositoryName: repositoryName, + })), + ) + .returning(); + + const newDeploymentInfo = await db + .select() + .from(system) + .innerJoin(workspace, eq(system.workspaceId, workspace.id)) + .where( + and( + inArray( + system.slug, + newParsedConfigs + .map((d) => d.deployments.map((d) => d.system)) + .flat(), + ), + inArray( + workspace.slug, + newParsedConfigs + .map((d) => d.deployments.map((d) => d.workspace)) + .flat(), + ), + ), + ); + + const deploymentsFromNewConfigs = newParsedConfigs + .map((cf) => + cf.deployments.map((d) => { + const info = newDeploymentInfo.find( + (i) => i.system.slug === d.system && i.workspace.slug === d.workspace, + ); + if (info == null) throw new Error("Deployment info not found"); + const { system, workspace } = info; + + return { + ...d, + systemId: system.id, + workspaceId: workspace.id, + description: d.description ?? "", + githubConfigFileId: newConfigs.find( + (icf) => + icf.path === cf.path && icf.repositoryName === cf.repositoryName, + )?.id, + }; + }), + ) + .flat(); + + await db.insert(deployment).values(deploymentsFromNewConfigs); +}; + +export const handleModifiedConfigs = async ( + modifiedParsedConfigs: ParsedConfig[], + internalOrganization: GithubOrganization, + repositoryName: string, +) => { + if (modifiedParsedConfigs.length === 0) return; + const existingConfigs = await db + .select() + .from(githubConfigFile) + .where( + and( + inArray( + githubConfigFile.path, + modifiedParsedConfigs.map((cf) => cf.path), + ), + eq(githubConfigFile.organizationId, internalOrganization.id), + eq(githubConfigFile.repositoryName, repositoryName), + ), + ); + + const existingDeployments = await db + .select() + .from(deployment) + .innerJoin(system, eq(deployment.systemId, system.id)) + .innerJoin(workspace, eq(system.workspaceId, workspace.id)) + .where( + inArray( + deployment.githubConfigFileId, + existingConfigs.map((c) => c.id), + ), + ); + + const deploymentsInConfigFiles = modifiedParsedConfigs + .map((d) => d.deployments) + .flat(); + + const removedDeployments = existingDeployments.filter( + (ed) => + !deploymentsInConfigFiles.some( + (dc) => + dc.slug === ed.deployment.slug && + dc.system === ed.system.slug && + dc.workspace === ed.workspace.slug, + ), + ); + + if (removedDeployments.length > 0) + await db.delete(deployment).where( + inArray( + deployment.id, + removedDeployments.map((d) => d.deployment.id), + ), + ); + + const newDeployments = modifiedParsedConfigs + .map((cf) => + cf.deployments.map((d) => ({ + ...d, + cf: { + path: cf.path, + repositoryName: cf.repositoryName, + }, + })), + ) + .flat() + .filter( + (d) => + !existingDeployments.some( + (ed) => + ed.deployment.slug === d.slug && + ed.system.slug === d.system && + ed.workspace.slug === d.workspace, + ), + ); + + if (newDeployments.length === 0) return; + + const newDeploymentInfo = await db + .select() + .from(system) + .innerJoin(workspace, eq(system.workspaceId, workspace.id)) + .where( + and( + inArray( + system.slug, + newDeployments.map((d) => d.system), + ), + inArray( + workspace.slug, + newDeployments.map((d) => d.workspace), + ), + ), + ); + + const newDeploymentsInsert = newDeployments.map((d) => { + const info = newDeploymentInfo.find( + (i) => i.system.slug === d.system && i.workspace.slug === d.workspace, + ); + if (info == null) throw new Error("Deployment info not found"); + const { system, workspace } = info; + + return { + ...d, + systemId: system.id, + workspaceId: workspace.id, + description: d.description ?? "", + githubConfigFileId: existingConfigs.find( + (icf) => + icf.path === d.cf.path && icf.repositoryName === d.cf.repositoryName, + )?.id, + }; + }); + + await db.insert(deployment).values(newDeploymentsInsert); +}; diff --git a/apps/webservice/src/app/api/github/config/route.ts b/apps/webservice/src/app/api/github/config/route.ts new file mode 100644 index 000000000..572bd1956 --- /dev/null +++ b/apps/webservice/src/app/api/github/config/route.ts @@ -0,0 +1,174 @@ +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; +import { createAppAuth } from "@octokit/auth-app"; +import { Octokit } from "@octokit/rest"; +import { PushEvent } from "@octokit/webhooks-types"; +import * as yaml from "js-yaml"; +import { isPresent } from "ts-is-present"; + +import { and, eq, inArray, takeFirstOrNull } from "@ctrlplane/db"; +import { db } from "@ctrlplane/db/client"; +import { githubConfigFile, githubOrganization } from "@ctrlplane/db/schema"; +import { configFile } from "@ctrlplane/validators"; + +import { env } from "~/env"; +import { handleModifiedConfigs, handleNewConfigs } from "./handle-configs"; + +const getOctokitInstallation = (installationId: number) => + env.GITHUB_BOT_APP_ID == null || + env.GITHUB_BOT_PRIVATE_KEY == null || + env.GITHUB_BOT_CLIENT_ID == null || + env.GITHUB_BOT_CLIENT_SECRET == null + ? null + : new Octokit({ + authStrategy: createAppAuth, + auth: { + appId: env.GITHUB_BOT_APP_ID, + privateKey: env.GITHUB_BOT_PRIVATE_KEY, + clientId: env.GITHUB_BOT_CLIENT_ID, + clientSecret: env.GITHUB_BOT_CLIENT_SECRET, + installationId, + }, + }); + +const isConfigFile = (path: string) => + path.endsWith("ctrlplane.yaml") || path.endsWith("ctrlplane.yml"); + +interface GetParsedConfigOptions { + organization: string; + repository: string; + path: string; + branch: string; +} + +const getParsedConfig = ( + { organization, repository, path, branch }: GetParsedConfigOptions, + installationOctokit: Octokit, +) => + installationOctokit.repos + .getContent({ + owner: organization, + repo: repository, + path, + ref: branch, + }) + .then(({ data }) => { + if (!("content" in data)) throw new Error("Invalid response data"); + const content = Buffer.from(data.content, "base64").toString("utf-8"); + const yamlContent = yaml.load(content); + const parsed = configFile.safeParse(yamlContent); + if (!parsed.success) throw new Error("Invalid config file"); + return { + ...parsed.data, + repositoryName: repository, + path, + branch, + }; + }); + +const handleCommitWebhookEvent = async (event: PushEvent) => { + const { ref, organization, repository, commits: eventCommits } = event; + if (organization == null) + throw new Error("Event not associated with an organization"); + + const branch = ref.split("/").pop(); + + const internalOrganization = await db + .select() + .from(githubOrganization) + .where(eq(githubOrganization.organizationName, organization.login)) + .then(takeFirstOrNull); + + if (internalOrganization == null) throw new Error("Organization not found"); + + if (internalOrganization.branch !== branch) return; + + const removedConfigFiles = eventCommits + .flatMap((v) => v.removed) + .filter(isConfigFile); + + if (removedConfigFiles.length > 0) + await db + .delete(githubConfigFile) + .where( + and( + inArray(githubConfigFile.path, removedConfigFiles), + eq(githubConfigFile.organizationId, internalOrganization.id), + ), + ); + + const newConfigFiles = eventCommits + .flatMap((v) => v.added) + .filter(isConfigFile); + const modifiedConfigFiles = eventCommits + .flatMap((v) => v.modified) + .filter(isConfigFile); + + const installationOctokit = getOctokitInstallation( + internalOrganization.installationId, + ); + + if (installationOctokit == null) throw new Error("GitHub bot not configured"); + + const [newParsedConfigs, modifiedParsedConfigs] = await Promise.all([ + Promise.allSettled( + newConfigFiles.map(async (cf) => { + return getParsedConfig( + { + organization: internalOrganization.organizationName, + repository: repository.name, + path: cf, + branch: internalOrganization.branch, + }, + installationOctokit, + ); + }), + ).then((results) => + results + .map((r) => (r.status === "fulfilled" ? r.value : null)) + .filter(isPresent), + ), + + Promise.allSettled( + modifiedConfigFiles.map(async (cf) => { + return getParsedConfig( + { + organization: internalOrganization.organizationName, + repository: repository.name, + path: cf, + branch: internalOrganization.branch, + }, + installationOctokit, + ); + }), + ).then((results) => + results + .map((r) => (r.status === "fulfilled" ? r.value : null)) + .filter(isPresent), + ), + ]); + + await handleModifiedConfigs( + modifiedParsedConfigs, + internalOrganization, + repository.name, + ); + + await handleNewConfigs( + newParsedConfigs, + internalOrganization, + repository.name, + ); +}; + +export const POST = async (req: NextRequest) => { + try { + const event = req.headers.get("x-github-event")?.toString(); + const data = await req.json(); + + if (event === "push") await handleCommitWebhookEvent(data as PushEvent); + return new NextResponse("OK"); + } catch (e) { + return new NextResponse((e as any).message, { status: 500 }); + } +}; diff --git a/apps/webservice/src/env.ts b/apps/webservice/src/env.ts index bc9dec43f..de6e8f7e8 100644 --- a/apps/webservice/src/env.ts +++ b/apps/webservice/src/env.ts @@ -22,6 +22,8 @@ export const env = createEnv({ POSTGRES_URL: z.string().url(), GITHUB_BOT_CLIENT_ID: z.string().optional(), GITHUB_BOT_CLIENT_SECRET: z.string().optional(), + GITHUB_BOT_APP_ID: z.string().optional(), + GITHUB_BOT_PRIVATE_KEY: z.string().optional(), }, /** diff --git a/packages/api/src/router/github.ts b/packages/api/src/router/github.ts index 4761408d0..fc02231d2 100644 --- a/packages/api/src/router/github.ts +++ b/packages/api/src/router/github.ts @@ -256,16 +256,9 @@ export const githubRouter = createTRPCRouter({ installation_id: org.installationId, }) .then(async ({ data: installation }) => { - const installationOctokit = new Octokit({ - authStrategy: createAppAuth, - auth: { - appId: env.GITHUB_BOT_APP_ID, - privateKey: env.GITHUB_BOT_PRIVATE_KEY, - clientId: env.GITHUB_BOT_CLIENT_ID, - clientSecret: env.GITHUB_BOT_CLIENT_SECRET, - installationId: installation.id, - }, - }); + const installationOctokit = getOctokitInstallation( + installation.id, + ); const installationToken = (await installationOctokit.auth({ type: "installation", @@ -367,7 +360,6 @@ export const githubRouter = createTRPCRouter({ workspaceId: org.workspaceId, organizationId: org.id, repositoryName: d.repository.name, - branch: org.branch, })), ) .returning(); @@ -394,7 +386,6 @@ export const githubRouter = createTRPCRouter({ description: d.description ?? "", githubConfigFileId: insertedConfigFiles.find( (icf) => - icf.name === cf.name && icf.path === cf.path && icf.repositoryName === cf.repository.name, )?.id, diff --git a/packages/db/src/schema/github.ts b/packages/db/src/schema/github.ts index 68c0f009e..f3ab02fff 100644 --- a/packages/db/src/schema/github.ts +++ b/packages/db/src/schema/github.ts @@ -5,6 +5,7 @@ import { pgTable, text, timestamp, + uniqueIndex, uuid, } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; @@ -45,19 +46,29 @@ export type GithubOrganization = InferSelectModel; export const githubOrganizationInsert = createInsertSchema(githubOrganization); -export const githubConfigFile = pgTable("github_config_file", { - id: uuid("id").primaryKey().defaultRandom(), - organizationId: uuid("organization_id") - .notNull() - .references(() => githubOrganization.id, { onDelete: "cascade" }), - repositoryName: text("repository_name").notNull(), - branch: text("branch").notNull().default("main"), - path: text("path").notNull(), - name: text("name").notNull(), - workspaceId: uuid("workspace_id") - .notNull() - .references(() => workspace.id, { onDelete: "cascade" }), - lastSyncedAt: timestamp("last_synced_at", { - withTimezone: true, - }).defaultNow(), -}); +export const githubConfigFile = pgTable( + "github_config_file", + { + id: uuid("id").primaryKey().defaultRandom(), + organizationId: uuid("organization_id") + .notNull() + .references(() => githubOrganization.id, { onDelete: "cascade" }), + repositoryName: text("repository_name").notNull(), + path: text("path").notNull(), + workspaceId: uuid("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + lastSyncedAt: timestamp("last_synced_at", { + withTimezone: true, + }).defaultNow(), + }, + (t) => ({ + unique: uniqueIndex("unique_organization_repository_path").on( + t.organizationId, + t.repositoryName, + t.path, + ), + }), +); + +export type GithubConfigFile = InferSelectModel; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e3ee5642..4cc63fc8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -307,6 +307,12 @@ importers: '@octokit/auth-app': specifier: ^7.1.0 version: 7.1.0 + '@octokit/rest': + specifier: ^20.1.1 + version: 20.1.1 + '@octokit/webhooks-types': + specifier: ^7.5.1 + version: 7.5.1 '@t3-oss/env-nextjs': specifier: ^0.10.1 version: 0.10.1(typescript@5.5.4)(zod@3.23.8) @@ -1010,7 +1016,7 @@ importers: version: 1.13.4(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(eslint@9.9.0(jiti@1.21.6)) + version: 2.29.1(@typescript-eslint/parser@7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3))(eslint@9.9.0(jiti@1.21.6)) eslint-plugin-jsx-a11y: specifier: ^6.8.0 version: 6.9.0(eslint@9.9.0(jiti@1.21.6)) @@ -2081,6 +2087,9 @@ packages: '@octokit/types@13.5.0': resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==} + '@octokit/webhooks-types@7.5.1': + resolution: {integrity: sha512-1dozxWEP8lKGbtEu7HkRbK1F/nIPuJXNfT0gd96y6d3LcHZTtRtlf8xz3nicSJfesADxJyDh+mWBOsdLkqgzYw==} + '@panva/hkdf@1.2.0': resolution: {integrity: sha512-97ZQvZJ4gJhi24Io6zI+W7B67I82q1I8i3BSzQ4OyZj1z4OW87/ruF26lrMES58inTKLy2KgVIDcx8PU4AaANQ==} @@ -9139,6 +9148,8 @@ snapshots: dependencies: '@octokit/openapi-types': 22.2.0 + '@octokit/webhooks-types@7.5.1': {} + '@panva/hkdf@1.2.0': {} '@pkgjs/parseargs@0.11.0': @@ -12744,16 +12755,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.8.1(eslint-import-resolver-node@0.3.9)(eslint@9.9.0(jiti@1.21.6)): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.9.0(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: + '@typescript-eslint/parser': 7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3) eslint: 9.9.0(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(eslint@9.9.0(jiti@1.21.6)): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3))(eslint@9.9.0(jiti@1.21.6)): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -12763,7 +12775,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.9.0(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(eslint-import-resolver-node@0.3.9)(eslint@9.9.0(jiti@1.21.6)) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@9.9.0(jiti@1.21.6)) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -12773,6 +12785,8 @@ snapshots: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 7.16.1(eslint@9.9.0(jiti@1.21.6))(typescript@5.5.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack From a3d7eac254d2cf155ed97fc6364983cd76966806 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 27 Aug 2024 16:13:39 -0700 Subject: [PATCH 2/3] clean up --- .../integrations/(integration)/github/GithubConfigFile.tsx | 2 +- apps/webservice/src/app/api/github/config/handle-configs.ts | 3 ++- apps/webservice/src/app/api/github/config/route.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx index 61cfe3941..040871f7e 100644 --- a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/GithubConfigFile.tsx @@ -1,4 +1,4 @@ -import { GithubConfigFile } from "@ctrlplane/db/schema"; +import type { GithubConfigFile } from "@ctrlplane/db/schema"; import { Card, CardContent, diff --git a/apps/webservice/src/app/api/github/config/handle-configs.ts b/apps/webservice/src/app/api/github/config/handle-configs.ts index c19e3cffb..7ae27091a 100644 --- a/apps/webservice/src/app/api/github/config/handle-configs.ts +++ b/apps/webservice/src/app/api/github/config/handle-configs.ts @@ -1,9 +1,10 @@ import { and, eq, inArray } from "@ctrlplane/db"; import { db } from "@ctrlplane/db/client"; +import type { + GithubOrganization} from "@ctrlplane/db/schema"; import { deployment, githubConfigFile, - GithubOrganization, system, workspace, } from "@ctrlplane/db/schema"; diff --git a/apps/webservice/src/app/api/github/config/route.ts b/apps/webservice/src/app/api/github/config/route.ts index 572bd1956..6629a3753 100644 --- a/apps/webservice/src/app/api/github/config/route.ts +++ b/apps/webservice/src/app/api/github/config/route.ts @@ -2,7 +2,7 @@ import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { createAppAuth } from "@octokit/auth-app"; import { Octokit } from "@octokit/rest"; -import { PushEvent } from "@octokit/webhooks-types"; +import type { PushEvent } from "@octokit/webhooks-types"; import * as yaml from "js-yaml"; import { isPresent } from "ts-is-present"; From 08ba25cb369b90d9a81cec9a5063239adabd4af1 Mon Sep 17 00:00:00 2001 From: Aditya Choudhari Date: Tue, 27 Aug 2024 16:13:51 -0700 Subject: [PATCH 3/3] more nit --- .../workspace/integrations/(integration)/github/page.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx index bfe78a071..3b21573a4 100644 --- a/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx +++ b/apps/webservice/src/app/[workspaceSlug]/settings/(settings)/workspace/integrations/(integration)/github/page.tsx @@ -31,9 +31,7 @@ export default function GitHubIntegrationPage({ const configFiles = api.github.configFile.list.useQuery( workspace.data?.id ?? "", - { - enabled: workspace.data != null, - }, + { enabled: workspace.data != null }, ); return (