From 94877debc183a7b676c4cb52480119e2de092afd Mon Sep 17 00:00:00 2001 From: bkellam Date: Thu, 30 Oct 2025 21:10:52 -0700 Subject: [PATCH 1/5] migrate to using enums for connectionType and external_codeHostType --- .../migration.sql | 14 +++++++++ .../migration.sql | 22 +++++++++++++ packages/db/prisma/schema.prisma | 31 +++++++++++++++++-- 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 packages/db/prisma/migrations/20251031012851_change_connection_type_to_enum/migration.sql create mode 100644 packages/db/prisma/migrations/20251031014307_change_repo_code_host_type_to_enum/migration.sql diff --git a/packages/db/prisma/migrations/20251031012851_change_connection_type_to_enum/migration.sql b/packages/db/prisma/migrations/20251031012851_change_connection_type_to_enum/migration.sql new file mode 100644 index 00000000..6580b522 --- /dev/null +++ b/packages/db/prisma/migrations/20251031012851_change_connection_type_to_enum/migration.sql @@ -0,0 +1,14 @@ +/* + Migrates the `connectionType` column from text to a enum. The values in this field are known to + be one of the following: github, gitlab, gitea, gerrit, bitbucket, azuredevops, git. + + This is occording to what we would expect to be in a valid config file for the schema version at commit 4899c9fbc755851af2ddcce99f4a4200f2faa4f6. + See: https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/schemas/src/v3/connection.type.ts#L3 +*/ +-- CreateEnum +CREATE TYPE "ConnectionType" AS ENUM ('github', 'gitlab', 'gitea', 'gerrit', 'bitbucket', 'azuredevops', 'git'); + +-- AlterTable - Convert existing column to enum type without dropping data +ALTER TABLE "Connection" + ALTER COLUMN "connectionType" TYPE "ConnectionType" + USING "connectionType"::text::"ConnectionType"; diff --git a/packages/db/prisma/migrations/20251031014307_change_repo_code_host_type_to_enum/migration.sql b/packages/db/prisma/migrations/20251031014307_change_repo_code_host_type_to_enum/migration.sql new file mode 100644 index 00000000..1d3fb23b --- /dev/null +++ b/packages/db/prisma/migrations/20251031014307_change_repo_code_host_type_to_enum/migration.sql @@ -0,0 +1,22 @@ +/* + Migrates the `external_codeHostType` column from text to a enum. The values in this field are known to + be one of the following: github, gitlab, gitea, gerrit, bitbucket-server, bitbucket-cloud, generic-git-host, azuredevops. + + This is occording to what we would expect to be in the database written as of commit 4899c9fbc755851af2ddcce99f4a4200f2faa4f6. + See: + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L57 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L135 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L208 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L291 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L407 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L510 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L574 + - https://github.com/sourcebot-dev/sourcebot/blob/4899c9fbc755851af2ddcce99f4a4200f2faa4f6/packages/backend/src/repoCompileUtils.ts#L642 +*/ +-- CreateEnum +CREATE TYPE "CodeHostType" AS ENUM ('github', 'gitlab', 'gitea', 'gerrit', 'bitbucket-server', 'bitbucket-cloud', 'generic-git-host', 'azuredevops'); + +-- AlterTable - Convert existing column to enum type without dropping data +ALTER TABLE "Repo" + ALTER COLUMN "external_codeHostType" TYPE "CodeHostType" + USING "external_codeHostType"::text::"CodeHostType"; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 93adb717..313de621 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -29,6 +29,21 @@ enum ChatVisibility { PUBLIC } +/// @note: The @map annotation is required to maintain backwards compatibility +/// with the existing database. +/// @note: In the generated client, these mapped values will be in pascalCase. +/// This behaviour will change in prisma v7. See: https://github.com/prisma/prisma/issues/8446#issuecomment-3356119713 +enum CodeHostType { + github + gitlab + gitea + gerrit + bitbucketServer @map("bitbucket-server") + bitbucketCloud @map("bitbucket-cloud") + genericGitHost @map("generic-git-host") + azuredevops +} + model Repo { id Int @id @default(autoincrement()) name String /// Full repo name, including the vcs hostname (ex. github.com/sourcebot-dev/sourcebot) @@ -53,7 +68,7 @@ model Repo { indexedCommitHash String? /// The commit hash of the last indexed commit (on HEAD). external_id String /// The id of the repo in the external service - external_codeHostType String /// The type of the external service (e.g., github, gitlab, etc.) + external_codeHostType CodeHostType /// The type of the external service (e.g., github, gitlab, etc.) external_codeHostUrl String /// The base url of the external service (e.g., https://github.com) org Org @relation(fields: [orgId], references: [id], onDelete: Cascade) @@ -125,6 +140,18 @@ model SearchContext { @@unique([name, orgId]) } +/// Matches the union of `type` fields in the schema. +/// @see: schemas/v3/connection.type.ts +enum ConnectionType { + github + gitlab + gitea + gerrit + bitbucket + azuredevops + git +} + model Connection { id Int @id @default(autoincrement()) name String @@ -135,7 +162,7 @@ model Connection { repos RepoToConnection[] // The type of connection (e.g., github, gitlab, etc.) - connectionType String + connectionType ConnectionType syncJobs ConnectionSyncJob[] /// When the connection was last synced successfully. From d7f8af20b0628f453263b5053b1284f987981895 Mon Sep 17 00:00:00 2001 From: bkellam Date: Thu, 30 Oct 2025 21:47:51 -0700 Subject: [PATCH 2/5] fix types --- packages/backend/src/constants.ts | 5 +- packages/backend/src/repoCompileUtils.ts | 11 ++-- packages/backend/src/utils.ts | 2 +- .../[domain]/chat/components/demoCards.tsx | 3 +- .../app/[domain]/components/configEditor.tsx | 2 +- .../components/importSecretDialog.tsx | 7 +-- .../repos/components/repoBranchesTable.tsx | 7 +-- .../[domain]/repos/components/reposTable.tsx | 9 ++-- .../settings/connections/[id]/page.tsx | 21 ++++---- .../components/connectionsTable.tsx | 9 ++-- .../[domain]/settings/connections/page.tsx | 4 +- .../secrets/components/importSecretCard.tsx | 3 +- .../chat/components/searchScopeIcon.tsx | 3 +- packages/web/src/features/fileTree/actions.ts | 2 +- packages/web/src/lib/utils.ts | 53 ++++++++----------- 15 files changed, 72 insertions(+), 69 deletions(-) diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 9ba858de..cc52ef79 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -1,11 +1,12 @@ +import { CodeHostType } from "@sourcebot/db"; import { env } from "./env.js"; import path from "path"; export const SINGLE_TENANT_ORG_ID = 1; export const PERMISSION_SYNC_SUPPORTED_CODE_HOST_TYPES = [ - 'github', - 'gitlab', + CodeHostType.github, + CodeHostType.gitlab, ]; export const REPOS_CACHE_DIR = path.join(env.DATA_CACHE_DIR, 'repos'); diff --git a/packages/backend/src/repoCompileUtils.ts b/packages/backend/src/repoCompileUtils.ts index 8e8b1f26..d3172543 100644 --- a/packages/backend/src/repoCompileUtils.ts +++ b/packages/backend/src/repoCompileUtils.ts @@ -7,7 +7,7 @@ import { BitbucketRepository, getBitbucketReposFromConfig } from "./bitbucket.js import { getAzureDevOpsReposFromConfig } from "./azuredevops.js"; import { SchemaRestRepository as BitbucketServerRepository } from "@coderabbitai/bitbucket/server/openapi"; import { SchemaRepository as BitbucketCloudRepository } from "@coderabbitai/bitbucket/cloud/openapi"; -import { Prisma, PrismaClient } from '@sourcebot/db'; +import { CodeHostType, Prisma, PrismaClient } from '@sourcebot/db'; import { WithRequired } from "./types.js" import { marshalBool } from "./utils.js"; import { createLogger } from '@sourcebot/logger'; @@ -392,7 +392,7 @@ export const compileBitbucketConfig = async ( const repos = bitbucketRepos.map((repo) => { const isServer = config.deploymentType === 'server'; - const codeHostType = isServer ? 'bitbucket-server' : 'bitbucket-cloud'; // zoekt expects bitbucket-server + const codeHostType: CodeHostType = isServer ? 'bitbucketServer' : 'bitbucketCloud'; const displayName = isServer ? (repo as BitbucketServerRepository).name! : (repo as BitbucketCloudRepository).full_name!; const externalId = isServer ? (repo as BitbucketServerRepository).id!.toString() : (repo as BitbucketCloudRepository).uuid!; const isPublic = isServer ? (repo as BitbucketServerRepository).public : (repo as BitbucketCloudRepository).is_private === false; @@ -425,7 +425,8 @@ export const compileBitbucketConfig = async ( }, metadata: { gitConfig: { - 'zoekt.web-url-type': codeHostType, + // zoekt expects bitbucket-server and bitbucket-cloud + 'zoekt.web-url-type': codeHostType === 'bitbucketServer' ? 'bitbucket-server' : 'bitbucket-cloud', 'zoekt.web-url': webUrl, 'zoekt.name': repoName, 'zoekt.archived': marshalBool(isArchived), @@ -507,7 +508,7 @@ export const compileGenericGitHostConfig_file = async ( const repoName = path.join(remoteUrl.host, remoteUrl.pathname.replace(/\.git$/, '')); const repo: RepoData = { - external_codeHostType: 'generic-git-host', + external_codeHostType: 'genericGitHost', external_codeHostUrl: remoteUrl.resource, external_id: remoteUrl.toString(), cloneUrl: `file://${repoPath}`, @@ -571,7 +572,7 @@ export const compileGenericGitHostConfig_url = async ( const repoName = path.join(remoteUrl.host, remoteUrl.pathname.replace(/\.git$/, '')); const repo: RepoData = { - external_codeHostType: 'generic-git-host', + external_codeHostType: 'genericGitHost', external_codeHostUrl: remoteUrl.origin, external_id: remoteUrl.toString(), cloneUrl: remoteUrl.toString(), diff --git a/packages/backend/src/utils.ts b/packages/backend/src/utils.ts index 4bb18549..0fe98098 100644 --- a/packages/backend/src/utils.ts +++ b/packages/backend/src/utils.ts @@ -59,7 +59,7 @@ export const getRepoPath = (repo: Repo): { path: string, isReadOnly: boolean } = // If we are dealing with a local repository, then use that as the path. // Mark as read-only since we aren't guaranteed to have write access to the local filesystem. const cloneUrl = new URL(repo.cloneUrl); - if (repo.external_codeHostType === 'generic-git-host' && cloneUrl.protocol === 'file:') { + if (repo.external_codeHostType === 'genericGitHost' && cloneUrl.protocol === 'file:') { return { path: cloneUrl.pathname, isReadOnly: true, diff --git a/packages/web/src/app/[domain]/chat/components/demoCards.tsx b/packages/web/src/app/[domain]/chat/components/demoCards.tsx index 5c8d4e5c..7c4fe316 100644 --- a/packages/web/src/app/[domain]/chat/components/demoCards.tsx +++ b/packages/web/src/app/[domain]/chat/components/demoCards.tsx @@ -7,9 +7,10 @@ import { Badge } from "@/components/ui/badge"; import { Card } from "@/components/ui/card"; import { CardContent } from "@/components/ui/card"; import { DemoExamples, DemoSearchExample, DemoSearchScope } from "@/types"; -import { cn, CodeHostType, getCodeHostIcon } from "@/lib/utils"; +import { cn, getCodeHostIcon } from "@/lib/utils"; import useCaptureEvent from "@/hooks/useCaptureEvent"; import { SearchScopeInfoCard } from "@/features/chat/components/chatBox/searchScopeInfoCard"; +import { CodeHostType } from "@sourcebot/db"; interface DemoCards { demoExamples: DemoExamples; diff --git a/packages/web/src/app/[domain]/components/configEditor.tsx b/packages/web/src/app/[domain]/components/configEditor.tsx index 772350d9..63ba1acd 100644 --- a/packages/web/src/app/[domain]/components/configEditor.tsx +++ b/packages/web/src/app/[domain]/components/configEditor.tsx @@ -12,8 +12,8 @@ import { Separator } from "@/components/ui/separator"; import { Schema } from "ajv"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import useCaptureEvent from "@/hooks/useCaptureEvent"; -import { CodeHostType } from "@/lib/utils"; import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme"; +import { CodeHostType } from "@sourcebot/db"; export type QuickActionFn = (previous: T) => T; export type QuickAction = { diff --git a/packages/web/src/app/[domain]/components/importSecretDialog.tsx b/packages/web/src/app/[domain]/components/importSecretDialog.tsx index b67fea7a..23d8aefe 100644 --- a/packages/web/src/app/[domain]/components/importSecretDialog.tsx +++ b/packages/web/src/app/[domain]/components/importSecretDialog.tsx @@ -9,10 +9,11 @@ import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import useCaptureEvent from "@/hooks/useCaptureEvent"; import { useDomain } from "@/hooks/useDomain"; -import { CodeHostType, isServiceError } from "@/lib/utils"; +import { isServiceError } from "@/lib/utils"; import githubPatCreation from "@/public/github_pat_creation.png"; import gitlabPatCreation from "@/public/gitlab_pat_creation.png"; import giteaPatCreation from "@/public/gitea_pat_creation.png"; +import { CodeHostType } from "@sourcebot/db"; import { zodResolver } from "@hookform/resolvers/zod"; import { Eye, EyeOff, Loader2 } from "lucide-react"; import Image from "next/image"; @@ -88,9 +89,9 @@ export const ImportSecretDialog = ({ open, onOpenChange, onSecretCreated, codeHo return ; case 'gitlab': return ; - case 'bitbucket-cloud': + case 'bitbucketCloud': return ; - case 'bitbucket-server': + case 'bitbucketServer': return ; case 'gitea': return ; diff --git a/packages/web/src/app/[domain]/repos/components/repoBranchesTable.tsx b/packages/web/src/app/[domain]/repos/components/repoBranchesTable.tsx index bc186acd..eea5e102 100644 --- a/packages/web/src/app/[domain]/repos/components/repoBranchesTable.tsx +++ b/packages/web/src/app/[domain]/repos/components/repoBranchesTable.tsx @@ -15,13 +15,14 @@ import { import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" -import { CodeHostType, getCodeHostBrowseAtBranchUrl } from "@/lib/utils" +import { getCodeHostBrowseAtBranchUrl } from "@/lib/utils" import Link from "next/link" +import { CodeHostType } from "@sourcebot/db"; type RepoBranchesTableProps = { indexRevisions: string[]; repoWebUrl: string | null; - repoCodeHostType: string; + repoCodeHostType: CodeHostType; } export const RepoBranchesTable = ({ indexRevisions, repoWebUrl, repoCodeHostType }: RepoBranchesTableProps) => { @@ -39,7 +40,7 @@ export const RepoBranchesTable = ({ indexRevisions, repoWebUrl, repoCodeHostType const branchUrl = getCodeHostBrowseAtBranchUrl({ webUrl: repoWebUrl, - codeHostType: repoCodeHostType as CodeHostType, + codeHostType: repoCodeHostType, branchName: refName, }); diff --git a/packages/web/src/app/[domain]/repos/components/reposTable.tsx b/packages/web/src/app/[domain]/repos/components/reposTable.tsx index c767acbc..77d03883 100644 --- a/packages/web/src/app/[domain]/repos/components/reposTable.tsx +++ b/packages/web/src/app/[domain]/repos/components/reposTable.tsx @@ -14,7 +14,7 @@ import { Input } from "@/components/ui/input" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" -import { cn, CodeHostType, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils" +import { cn, getCodeHostCommitUrl, getCodeHostIcon, getCodeHostInfoForRepo, getRepoImageSrc } from "@/lib/utils" import { type ColumnDef, type ColumnFiltersState, @@ -38,6 +38,7 @@ import { useToast } from "@/components/hooks/use-toast"; import { DisplayDate } from "../../components/DisplayDate" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { NotificationDot } from "../../components/notificationDot" +import { CodeHostType } from "@sourcebot/db" // @see: https://v0.app/chat/repo-indexing-status-uhjdDim8OUS @@ -50,7 +51,7 @@ export type Repo = { indexedAt: Date | null createdAt: Date webUrl: string | null - codeHostType: string + codeHostType: CodeHostType imageUrl: string | null indexedCommitHash: string | null latestJobStatus: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" | null @@ -97,7 +98,7 @@ export const columns: ColumnDef[] = [ }, cell: ({ row }) => { const repo = row.original; - const codeHostIcon = getCodeHostIcon(repo.codeHostType as CodeHostType); + const codeHostIcon = getCodeHostIcon(repo.codeHostType); const repoImageSrc = repo.imageUrl ? getRepoImageSrc(repo.imageUrl, repo.id) : undefined; return ( @@ -192,7 +193,7 @@ export const columns: ColumnDef[] = [ const smallHash = hash.slice(0, 7); const repo = row.original; - const codeHostType = repo.codeHostType as CodeHostType; + const codeHostType = repo.codeHostType; const webUrl = repo.webUrl; const commitUrl = getCodeHostCommitUrl({ diff --git a/packages/web/src/app/[domain]/settings/connections/[id]/page.tsx b/packages/web/src/app/[domain]/settings/connections/[id]/page.tsx index 5db0d28c..c36a3342 100644 --- a/packages/web/src/app/[domain]/settings/connections/[id]/page.tsx +++ b/packages/web/src/app/[domain]/settings/connections/[id]/page.tsx @@ -7,7 +7,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip import { env } from "@/env.mjs"; import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants"; import { notFound, ServiceErrorException } from "@/lib/serviceError"; -import { CodeHostType, isServiceError } from "@/lib/utils"; +import { isServiceError } from "@/lib/utils"; import { withAuthV2 } from "@/withAuthV2"; import { AzureDevOpsConnectionConfig, BitbucketConnectionConfig, GenericGitHostConnectionConfig, GerritConnectionConfig, GiteaConnectionConfig, GithubConnectionConfig, GitlabConnectionConfig } from "@sourcebot/schemas/v3/index.type"; import { getConfigSettings } from "@sourcebot/shared"; @@ -47,8 +47,9 @@ export default async function ConnectionDetailPage(props: ConnectionDetailPagePr return undefined; })(); - const codeHostUrl = (() => { - const connectionType = connection.connectionType as CodeHostType; + // Extracts the code host URL from the connection config. + const codeHostUrl: string = (() => { + const connectionType = connection.connectionType; switch (connectionType) { case 'github': { const config = connection.config as unknown as GithubConnectionConfig; @@ -66,19 +67,19 @@ export default async function ConnectionDetailPage(props: ConnectionDetailPagePr const config = connection.config as unknown as GerritConnectionConfig; return config.url; } - case 'bitbucket-server': { + case 'bitbucket': { const config = connection.config as unknown as BitbucketConnectionConfig; - return config.url!; - } - case 'bitbucket-cloud': { - const config = connection.config as unknown as BitbucketConnectionConfig; - return config.url ?? 'https://bitbucket.org'; + if (config.deploymentType === 'cloud') { + return config.url ?? 'https://bitbucket.org'; + } else { + return config.url!; + } } case 'azuredevops': { const config = connection.config as unknown as AzureDevOpsConnectionConfig; return config.url ?? 'https://dev.azure.com'; } - case 'generic-git-host': { + case 'git': { const config = connection.config as unknown as GenericGitHostConnectionConfig; return config.url; } diff --git a/packages/web/src/app/[domain]/settings/connections/components/connectionsTable.tsx b/packages/web/src/app/[domain]/settings/connections/components/connectionsTable.tsx index 42897659..3285c71f 100644 --- a/packages/web/src/app/[domain]/settings/connections/components/connectionsTable.tsx +++ b/packages/web/src/app/[domain]/settings/connections/components/connectionsTable.tsx @@ -10,7 +10,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { SINGLE_TENANT_ORG_DOMAIN } from "@/lib/constants" -import { CodeHostType, getCodeHostIcon } from "@/lib/utils" +import { getCodeHostIcon } from "@/lib/utils" +import { ConnectionType } from "@sourcebot/db" import { type ColumnDef, type ColumnFiltersState, @@ -35,7 +36,7 @@ export type Connection = { id: number name: string syncedAt: Date | null - codeHostType: CodeHostType + connectionType: ConnectionType latestJobStatus: "PENDING" | "IN_PROGRESS" | "COMPLETED" | "FAILED" | null isFirstTimeSync: boolean } @@ -80,13 +81,13 @@ export const columns: ColumnDef[] = [ }, cell: ({ row }) => { const connection = row.original; - const codeHostIcon = getCodeHostIcon(connection.codeHostType); + const codeHostIcon = getCodeHostIcon(connection.connectionType); return (
{`${connection.codeHostType} ({ id: connection.id, name: connection.name, - codeHostType: connection.connectionType as CodeHostType, + connectionType: connection.connectionType, syncedAt: connection.syncedAt, latestJobStatus: connection.latestJobStatus, isFirstTimeSync: connection.isFirstTimeSync, diff --git a/packages/web/src/app/[domain]/settings/secrets/components/importSecretCard.tsx b/packages/web/src/app/[domain]/settings/secrets/components/importSecretCard.tsx index f92e2712..4d9a5666 100644 --- a/packages/web/src/app/[domain]/settings/secrets/components/importSecretCard.tsx +++ b/packages/web/src/app/[domain]/settings/secrets/components/importSecretCard.tsx @@ -3,7 +3,8 @@ import { CodeHostIconButton } from "@/app/[domain]/components/codeHostIconButton"; import { Card, CardTitle, CardDescription, CardHeader, CardContent } from "@/components/ui/card"; import { getCodeHostIcon } from "@/lib/utils"; -import { cn, CodeHostType } from "@/lib/utils"; +import { cn } from "@/lib/utils"; +import { CodeHostType } from "@sourcebot/db"; import { useState } from "react"; import { ImportSecretDialog } from "@/app/[domain]/components/importSecretDialog"; import { useRouter } from "next/navigation"; diff --git a/packages/web/src/features/chat/components/searchScopeIcon.tsx b/packages/web/src/features/chat/components/searchScopeIcon.tsx index 67170dca..b134f8d4 100644 --- a/packages/web/src/features/chat/components/searchScopeIcon.tsx +++ b/packages/web/src/features/chat/components/searchScopeIcon.tsx @@ -1,4 +1,5 @@ -import { cn, CodeHostType, getCodeHostIcon } from "@/lib/utils"; +import { cn, getCodeHostIcon } from "@/lib/utils"; +import { CodeHostType } from "@sourcebot/db"; import { LibraryBigIcon } from "lucide-react"; import Image from "next/image"; import { SearchScope } from "../types"; diff --git a/packages/web/src/features/fileTree/actions.ts b/packages/web/src/features/fileTree/actions.ts index 003b82b2..6111ea70 100644 --- a/packages/web/src/features/fileTree/actions.ts +++ b/packages/web/src/features/fileTree/actions.ts @@ -263,7 +263,7 @@ const getRepoPath = (repo: Repo): { path: string, isReadOnly: boolean } => { // If we are dealing with a local repository, then use that as the path. // Mark as read-only since we aren't guaranteed to have write access to the local filesystem. const cloneUrl = new URL(repo.cloneUrl); - if (repo.external_codeHostType === 'generic-git-host' && cloneUrl.protocol === 'file:') { + if (repo.external_codeHostType === 'genericGitHost' && cloneUrl.protocol === 'file:') { return { path: cloneUrl.pathname, isReadOnly: true, diff --git a/packages/web/src/lib/utils.ts b/packages/web/src/lib/utils.ts index d3b450a0..5a48ce71 100644 --- a/packages/web/src/lib/utils.ts +++ b/packages/web/src/lib/utils.ts @@ -15,9 +15,10 @@ import { ServiceError } from "./serviceError"; import { StatusCodes } from "http-status-codes"; import { ErrorCode } from "./errorCodes"; import { NextRequest } from "next/server"; -import { Org } from "@sourcebot/db"; +import { ConnectionType, Org } from "@sourcebot/db"; import { OrgMetadata, orgMetadataSchema } from "@/types"; import { SINGLE_TENANT_ORG_DOMAIN } from "./constants"; +import { CodeHostType } from "@sourcebot/db"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) @@ -64,16 +65,6 @@ export const createPathWithQueryParams = (path: string, ...queryParams: [string, return `${path}?${queryString}`; } -export type CodeHostType = - "github" | - "gitlab" | - "gitea" | - "gerrit" | - "bitbucket-cloud" | - "bitbucket-server" | - "azuredevops" | - "generic-git-host"; - export type AuthProviderType = "github" | "gitlab" | @@ -248,9 +239,9 @@ export const getCodeHostInfoForRepo = (repo: { } } case "bitbucket-server": { - const { src, className } = getCodeHostIcon('bitbucket-server')!; + const { src, className } = getCodeHostIcon('bitbucketServer')!; return { - type: "bitbucket-server", + type: "bitbucketServer", displayName: displayName ?? name, codeHostName: "Bitbucket", repoLink: webUrl, @@ -259,9 +250,9 @@ export const getCodeHostInfoForRepo = (repo: { } } case "bitbucket-cloud": { - const { src, className } = getCodeHostIcon('bitbucket-cloud')!; + const { src, className } = getCodeHostIcon('bitbucketCloud')!; return { - type: "bitbucket-cloud", + type: "bitbucketCloud", displayName: displayName ?? name, codeHostName: "Bitbucket", repoLink: webUrl, @@ -270,9 +261,9 @@ export const getCodeHostInfoForRepo = (repo: { } } case "generic-git-host": { - const { src, className } = getCodeHostIcon('generic-git-host')!; + const { src, className } = getCodeHostIcon('genericGitHost')!; return { - type: "generic-git-host", + type: "genericGitHost", displayName: displayName ?? name, codeHostName: "Git Host", repoLink: webUrl, @@ -283,7 +274,7 @@ export const getCodeHostInfoForRepo = (repo: { } } -export const getCodeHostIcon = (codeHostType: CodeHostType): { src: string, className?: string } => { +export const getCodeHostIcon = (codeHostType: CodeHostType | ConnectionType): { src: string, className?: string } => { switch (codeHostType) { case "github": return { @@ -302,8 +293,9 @@ export const getCodeHostIcon = (codeHostType: CodeHostType): { src: string, clas return { src: gerritLogo, } - case "bitbucket-cloud": - case "bitbucket-server": + case "bitbucket": + case "bitbucketCloud": + case "bitbucketServer": return { src: bitbucketLogo, } @@ -311,7 +303,8 @@ export const getCodeHostIcon = (codeHostType: CodeHostType): { src: string, clas return { src: azuredevopsLogo, } - case "generic-git-host": + case "git": + case "genericGitHost": return { src: gitLogo, } @@ -340,13 +333,13 @@ export const getCodeHostCommitUrl = ({ return `${webUrl}/commit/${commitHash}`; case 'azuredevops': return `${webUrl}/commit/${commitHash}`; - case 'bitbucket-cloud': + case 'bitbucketCloud': return `${webUrl}/commits/${commitHash}`; - case 'bitbucket-server': + case 'bitbucketServer': return `${webUrl}/commits/${commitHash}`; case 'gerrit': return `${webUrl}/+/${commitHash}`; - case 'generic-git-host': + case 'genericGitHost': return undefined; } } @@ -373,13 +366,13 @@ export const getCodeHostBrowseAtBranchUrl = ({ return `${webUrl}/src/branch/${branchName}`; case 'azuredevops': return `${webUrl}?branch=${branchName}`; - case 'bitbucket-cloud': + case 'bitbucketCloud': return `${webUrl}?at=${branchName}`; - case 'bitbucket-server': + case 'bitbucketServer': return `${webUrl}?at=${branchName}`; case 'gerrit': return `${webUrl}/+/${branchName}`; - case 'generic-git-host': + case 'genericGitHost': return undefined; } } @@ -389,11 +382,11 @@ export const isAuthSupportedForCodeHost = (codeHostType: CodeHostType): boolean case "github": case "gitlab": case "gitea": - case "bitbucket-cloud": - case "bitbucket-server": + case "bitbucketCloud": + case "bitbucketServer": case "azuredevops": return true; - case "generic-git-host": + case "genericGitHost": case "gerrit": return false; } From 276a802a1e06acce070b4b54c91eeea92037029a Mon Sep 17 00:00:00 2001 From: bkellam Date: Thu, 30 Oct 2025 22:18:22 -0700 Subject: [PATCH 3/5] improved types --- .../[...path]/components/codePreviewPanel.tsx | 2 +- .../navigationMenu/progressIndicator.tsx | 23 ++++++------------- .../app/[domain]/components/pathHeader.tsx | 22 ++++++++---------- .../components/repositoryCarousel.tsx | 20 +++++----------- .../web/src/app/[domain]/repos/[id]/page.tsx | 2 +- .../[domain]/repos/components/reposTable.tsx | 2 +- .../referencedFileSourceListItem.tsx | 3 ++- packages/web/src/features/search/schemas.ts | 5 ++-- packages/web/src/lib/schemas.ts | 3 ++- packages/web/src/lib/utils.ts | 13 +++++------ 10 files changed, 38 insertions(+), 57 deletions(-) diff --git a/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx b/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx index 01a84447..b38d140b 100644 --- a/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx +++ b/packages/web/src/app/[domain]/browse/[...path]/components/codePreviewPanel.tsx @@ -52,7 +52,7 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre branchDisplayName={revisionName} /> - {(fileWebUrl && codeHostInfo) && ( + {fileWebUrl && ( { webUrl: repo.webUrl, }); - if (info) { - return { - repoIcon: {info.codeHostName}, - displayName: info.displayName, - } - } - return { - repoIcon: , - displayName: repo.repoName, + repoIcon: {info.codeHostName}, + displayName: info.displayName, } - - }, [repo.repoName, repo.codeHostType, repo.repoDisplayName, repo.webUrl]); diff --git a/packages/web/src/app/[domain]/components/pathHeader.tsx b/packages/web/src/app/[domain]/components/pathHeader.tsx index 11b5bf1d..d65d2c35 100644 --- a/packages/web/src/app/[domain]/components/pathHeader.tsx +++ b/packages/web/src/app/[domain]/components/pathHeader.tsx @@ -1,7 +1,6 @@ 'use client'; import { cn, getCodeHostInfoForRepo } from "@/lib/utils"; -import { LaptopIcon } from "@radix-ui/react-icons"; import Image from "next/image"; import { getBrowsePath } from "../browse/hooks/utils"; import { ChevronRight, MoreHorizontal } from "lucide-react"; @@ -17,6 +16,7 @@ import { VscodeFileIcon } from "@/app/components/vscodeFileIcon"; import { CopyIconButton } from "./copyIconButton"; import Link from "next/link"; import { useDomain } from "@/hooks/useDomain"; +import { CodeHostType } from "@sourcebot/db"; interface FileHeaderProps { path: string; @@ -27,7 +27,7 @@ interface FileHeaderProps { pathType?: 'blob' | 'tree'; repo: { name: string; - codeHostType: string; + codeHostType: CodeHostType; displayName?: string; webUrl?: string; }, @@ -202,17 +202,13 @@ export const PathHeader = ({
{isCodeHostIconVisible && ( <> - {info?.icon ? ( - - {info.codeHostName} - - ) : ( - - )} + + {info.codeHostName} + )} diff --git a/packages/web/src/app/[domain]/components/repositoryCarousel.tsx b/packages/web/src/app/[domain]/components/repositoryCarousel.tsx index a9d1239f..f5576aba 100644 --- a/packages/web/src/app/[domain]/components/repositoryCarousel.tsx +++ b/packages/web/src/app/[domain]/components/repositoryCarousel.tsx @@ -8,7 +8,6 @@ import { import { captureEvent } from "@/hooks/useCaptureEvent"; import { RepositoryQuery } from "@/lib/types"; import { getCodeHostInfoForRepo } from "@/lib/utils"; -import { FileIcon } from "@radix-ui/react-icons"; import clsx from "clsx"; import Autoscroll from "embla-carousel-auto-scroll"; import Image from "next/image"; @@ -121,20 +120,13 @@ const RepositoryBadge = ({ webUrl: repo.webUrl, }); - if (info) { - return { - repoIcon: {info.codeHostName}, - displayName: info.displayName, - } - } - return { - repoIcon: , - displayName: repo.repoName, + repoIcon: {info.codeHostName}, + displayName: info.displayName, } })(); diff --git a/packages/web/src/app/[domain]/repos/[id]/page.tsx b/packages/web/src/app/[domain]/repos/[id]/page.tsx index db14e50c..a3255c04 100644 --- a/packages/web/src/app/[domain]/repos/[id]/page.tsx +++ b/packages/web/src/app/[domain]/repos/[id]/page.tsx @@ -65,7 +65,7 @@ export default async function RepoDetailPage({ params }: { params: Promise<{ id:

{repo.displayName || repo.name}

{repo.name}

- {(codeHostInfo && codeHostInfo.repoLink) && ( + {codeHostInfo.repoLink && (