Skip to content

Commit ae66a6a

Browse files
SaxonFdjhi
andauthored
Connect GitHub during project creation (supabase#44884)
<img width="1289" height="863" alt="image" src="https://github.com/user-attachments/assets/d661f107-b358-4894-8531-80441d60ab91" /> GitHub integration is now available on the free plan and so we'd like to start promoting code-first workflows as much as possible. One way to do that is to set the tone straight away by asking a user to connecting their GitHub repository to a project as part of project creation. This PR: - decouples GitHub connection and repo selection into a separate component we can make use of in integration settings and project creation. - Adds new GitHub fields to project creation form and sends them off to project creation endpoint - Pre-fills project name based on repo selection To test locally: - Ensure you have GitHub integration set up locally (using ngrok etc) - Ensure you are on the connected platform branch - Open create a new project page - Connect GitHub as part of the creation form and select a repo - Create the project and wait for status to be healthy - Check project settings integrations page and ensure repo is connected Note: - this requires changes on the management api end to accept new GitHub fields - it might make sense to pull out GitHub connection/authorization from GitHub repository selection but in the current state they are tied together. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * GitHub repository selection now available during project creation with integrated authorization flow * GitHub connection status and compute availability indicators now displayed on project dashboard * Project name auto-populates from selected GitHub repository name when available <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
1 parent 4452e0a commit ae66a6a

10 files changed

Lines changed: 594 additions & 268 deletions

File tree

apps/studio/components/interfaces/ProjectCreation/ProjectCreation.schema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export const FormSchema = z
3434
dbPassStrengthMessage: z.string().default(''),
3535
dbPassStrengthWarning: z.string().default(''),
3636
instanceSize: z.string().optional(),
37+
githubRepositoryId: z.string().optional().default(''),
38+
githubInstallationId: z.number().optional(),
39+
githubRepositoryName: z.string().optional().default(''),
3740
dataApi: z.boolean(),
3841
dataApiDefaultPrivileges: z.boolean(),
3942
enableRlsEventTrigger: z.boolean(),

apps/studio/components/interfaces/ProjectHome/ActivityStats.tsx

Lines changed: 100 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
11
import { useParams } from 'common'
22
import dayjs from 'dayjs'
3-
import { Archive, Database, GitBranch } from 'lucide-react'
3+
import { Archive, Cpu, Database, GitBranch, Github } from 'lucide-react'
44
import { useMemo } from 'react'
55
import { cn, Skeleton } from 'ui'
66
import { TimestampInfo } from 'ui-patterns'
77

8+
import { HighAvailabilityBadge } from './HighAvailabilityBadge'
89
import { ServiceStatus } from './ServiceStatus'
10+
import { ComputeBadgeWrapper } from '@/components/ui/ComputeBadgeWrapper'
911
import { SingleStat } from '@/components/ui/SingleStat'
1012
import { useBranchesQuery } from '@/data/branches/branches-query'
1113
import { useBackupsQuery } from '@/data/database/backups-query'
1214
import { DatabaseMigration, useMigrationsQuery } from '@/data/database/migrations-query'
15+
import { useGitHubConnectionsQuery } from '@/data/integrations/github-connections-query'
16+
import { useResourceWarningsQuery } from '@/data/usage/resource-warnings-query'
17+
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
1318
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
19+
import { PROJECT_STATUS } from '@/lib/constants'
1420
import { EMPTY_ARR } from '@/lib/void'
1521

1622
export const ActivityStats = () => {
1723
const { ref } = useParams()
1824
const { data: project } = useSelectedProjectQuery()
25+
const { data: organization } = useSelectedOrganizationQuery()
26+
const { data: resourceWarnings } = useResourceWarningsQuery({ slug: organization?.slug })
27+
const projectResourceWarnings = resourceWarnings?.find((warning) => warning.project === ref)
28+
const parentProjectRef = project?.parent_project_ref ?? project?.ref
1929

2030
const { data: branchesData, isPending: isLoadingBranches } = useBranchesQuery({
21-
projectRef: project?.parent_project_ref ?? project?.ref,
31+
projectRef: parentProjectRef,
2232
})
2333
const isDefaultProject = project?.parent_project_ref === undefined
2434
const currentBranch = useMemo(
@@ -61,52 +71,63 @@ export const ActivityStats = () => {
6171
.sort((a, b) => new Date(b.inserted_at).valueOf() - new Date(a.inserted_at).valueOf())[0]
6272
}, [backupsData])
6373

74+
const { data: githubConnections, isPending: isLoadingGithubConnections } =
75+
useGitHubConnectionsQuery({ organizationId: organization?.id }, { enabled: !!organization?.id })
76+
const githubConnection = githubConnections?.find(
77+
(connection) => connection.project.ref === parentProjectRef
78+
)
79+
const isProjectComingUp = [PROJECT_STATUS.COMING_UP, PROJECT_STATUS.UNKNOWN].includes(
80+
project?.status ?? PROJECT_STATUS.UNKNOWN
81+
)
82+
const githubLabelText = githubConnection?.repository.name
83+
? githubConnection.repository.name
84+
: isProjectComingUp
85+
? 'Waiting for project...'
86+
: 'No repository connected'
87+
const integrationsPath = parentProjectRef
88+
? `/project/${parentProjectRef}/settings/integrations`
89+
: undefined
90+
6491
return (
6592
<div className="@container">
6693
<div className="grid grid-cols-1 @md:grid-cols-2 gap-2 @md:gap-6 flex-wrap">
6794
<ServiceStatus />
6895

6996
<SingleStat
70-
href={`/project/${ref}/database/migrations`}
71-
icon={<Database size={18} strokeWidth={1.5} className="text-foreground" />}
72-
label={<span>Last migration</span>}
73-
trackingProperties={{
74-
stat_type: 'migrations',
75-
stat_value: migrationsData?.length ?? 0,
76-
}}
97+
icon={<Cpu size={18} strokeWidth={1.5} className="text-foreground" />}
98+
label={<span>Compute</span>}
7799
value={
78-
isLoadingMigrations ? (
79-
<Skeleton className="h-6 w-24" />
80-
) : (
81-
<p className={!!latestMigration ? 'text-foreground' : 'text-foreground-lighter'}>
82-
{migrationLabelText}
83-
</p>
84-
)
100+
<div className="flex items-center gap-2 flex-wrap">
101+
{project?.infra_compute_size ? (
102+
<ComputeBadgeWrapper
103+
projectRef={project?.ref}
104+
slug={organization?.slug}
105+
cloudProvider={project?.cloud_provider}
106+
computeSize={project.infra_compute_size}
107+
resourceWarnings={projectResourceWarnings}
108+
/>
109+
) : (
110+
<p className="text-foreground-lighter">Unknown</p>
111+
)}
112+
{project?.high_availability && <HighAvailabilityBadge />}
113+
</div>
85114
}
86115
/>
87116

88117
<SingleStat
89-
href={`/project/${ref}/database/backups/scheduled`}
90-
icon={<Archive size={18} strokeWidth={1.5} className="text-foreground" />}
91-
label={<span>Last backup</span>}
92-
trackingProperties={{
93-
stat_type: 'backups',
94-
stat_value: backupsData?.backups?.length ?? 0,
95-
}}
118+
href={integrationsPath}
119+
icon={<Github size={18} strokeWidth={1.5} className="text-foreground" />}
120+
label={<span>GitHub</span>}
96121
value={
97-
isLoadingBackups ? (
122+
isLoadingGithubConnections ? (
98123
<Skeleton className="h-6 w-24" />
99-
) : backupsData?.pitr_enabled ? (
100-
<p>PITR enabled</p>
101-
) : latestBackup ? (
102-
<TimestampInfo
103-
className="text-base"
104-
displayAs="utc"
105-
label={dayjs(latestBackup.inserted_at).fromNow()}
106-
utcTimestamp={latestBackup.inserted_at}
107-
/>
108124
) : (
109-
<p className="text-foreground-lighter">No backups</p>
125+
<p
126+
className={cn('truncate', !githubConnection && 'text-foreground-lighter')}
127+
title={githubLabelText}
128+
>
129+
{githubLabelText}
130+
</p>
110131
)
111132
}
112133
/>
@@ -143,6 +164,51 @@ export const ActivityStats = () => {
143164
)
144165
}
145166
/>
167+
168+
<SingleStat
169+
href={`/project/${ref}/database/migrations`}
170+
icon={<Database size={18} strokeWidth={1.5} className="text-foreground" />}
171+
label={<span>Last migration</span>}
172+
trackingProperties={{
173+
stat_type: 'migrations',
174+
stat_value: migrationsData?.length ?? 0,
175+
}}
176+
value={
177+
isLoadingMigrations ? (
178+
<Skeleton className="h-6 w-24" />
179+
) : (
180+
<p className={!!latestMigration ? 'text-foreground' : 'text-foreground-lighter'}>
181+
{migrationLabelText}
182+
</p>
183+
)
184+
}
185+
/>
186+
187+
<SingleStat
188+
href={`/project/${ref}/database/backups/scheduled`}
189+
icon={<Archive size={18} strokeWidth={1.5} className="text-foreground" />}
190+
label={<span>Last backup</span>}
191+
trackingProperties={{
192+
stat_type: 'backups',
193+
stat_value: backupsData?.backups?.length ?? 0,
194+
}}
195+
value={
196+
isLoadingBackups ? (
197+
<Skeleton className="h-6 w-24" />
198+
) : backupsData?.pitr_enabled ? (
199+
<p>PITR enabled</p>
200+
) : latestBackup ? (
201+
<TimestampInfo
202+
className="text-base"
203+
displayAs="utc"
204+
label={dayjs(latestBackup.inserted_at).fromNow()}
205+
utcTimestamp={latestBackup.inserted_at}
206+
/>
207+
) : (
208+
<p className="text-foreground-lighter">No backups</p>
209+
)
210+
}
211+
/>
146212
</div>
147213
</div>
148214
)

apps/studio/components/interfaces/ProjectHome/HighAvailabilityBadge.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@ export function HighAvailabilityBadge({ size = 'default' }: HighAvailabilityBadg
1515
<HoverCardTrigger asChild>
1616
<div
1717
className={cn(
18-
'inline-flex items-center justify-center rounded-md text-center font-mono uppercase',
18+
'relative inline-flex items-center justify-center overflow-hidden rounded-md text-center font-mono uppercase',
1919
'cursor-default whitespace-nowrap font-medium tracking-[0.06em] text-[11px] leading-[1.1] px-[5.5px] py-[3px]',
2020
'transition-all',
2121
'border border-purple-700 dark:border-purple-600/50',
2222
'bg-purple-400 text-purple-1100 dark:bg-purple-100'
2323
)}
2424
>
2525
{size === 'small' ? 'HA' : 'High Availability'}
26+
<span className="animate-badge-shimmer pointer-events-none absolute inset-0 bg-gradient-to-br from-transparent via-white/35 to-transparent blur-md" />
2627
</div>
2728
</HoverCardTrigger>
2829
<HoverCardContent side="bottom" align="start" className="w-72 overflow-hidden p-0">
29-
<div className="p-2 px-5 text-xs text-foreground-lighter">Multigres</div>
30-
<Separator />
3130
<div className="h-24 bg-surface-75">
3231
<ServerLightGrid />
3332
</div>
3433
<Separator />
3534
<div className="flex flex-col gap-1 p-3 px-5">
3635
<p className="text-sm text-foreground-light">
37-
A horizontally scalable Postgres architecture that supports highly-available and
38-
globally distributed deployments.
36+
Driven by <span className="text-foreground">Multigres</span>, a horizontally scalable
37+
Postgres architecture that supports highly-available and globally distributed
38+
deployments.
3939
</p>
4040
<Link
4141
href={`${DOCS_URL}/guides/deployment/high-availability`}

apps/studio/components/interfaces/ProjectHome/TopSection.tsx

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,19 @@ import Link from 'next/link'
33
import { Badge, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
44

55
import { InstanceConfiguration } from '../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration'
6-
import { HighAvailabilityBadge } from './HighAvailabilityBadge'
76
import { ActivityStats } from '@/components/interfaces/ProjectHome/ActivityStats'
87
import { ProjectConnectionPopover } from '@/components/interfaces/ProjectHome/ProjectConnectionPopover'
98
import { ProjectPausedState } from '@/components/layouts/ProjectLayout/PausedState/ProjectPausedState'
10-
import { ComputeBadgeWrapper } from '@/components/ui/ComputeBadgeWrapper'
119
import { InlineLink } from '@/components/ui/InlineLink'
1210
import { ProjectUpgradeFailedBanner } from '@/components/ui/ProjectUpgradeFailedBanner'
1311
import { useBranchesQuery } from '@/data/branches/branches-query'
1412
import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
15-
import { useResourceWarningsQuery } from '@/data/usage/resource-warnings-query'
16-
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
1713
import { useIsOrioleDb, useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
1814
import { DOCS_URL, PROJECT_STATUS } from '@/lib/constants'
1915

2016
export const TopSection = () => {
2117
const isOrioleDb = useIsOrioleDb()
2218
const { data: project } = useSelectedProjectQuery()
23-
const { data: organization } = useSelectedOrganizationQuery()
24-
const { data: resourceWarnings } = useResourceWarningsQuery({ slug: organization?.slug })
25-
const projectResourceWarnings = resourceWarnings?.find((w) => w.project === project?.ref)
2619
const { data: parentProject } = useProjectDetailQuery({ ref: project?.parent_project_ref })
2720

2821
const { data: branches } = useBranchesQuery({
@@ -61,31 +54,21 @@ export const TopSection = () => {
6154
)}
6255
<div className="flex items-center gap-x-2">
6356
<h1 className="text-3xl">{projectName}</h1>
64-
<div className="flex items-center gap-x-2">
65-
{isOrioleDb && (
66-
<Tooltip>
67-
<TooltipTrigger asChild>
68-
<Badge variant="warning">OrioleDB</Badge>
69-
</TooltipTrigger>
70-
<TooltipContent side="bottom" align="start" className="max-w-80 text-center">
71-
This project is using Postgres with OrioleDB which is currently in preview
72-
and not suitable for production workloads. View our{' '}
73-
<InlineLink href={`${DOCS_URL}/guides/database/orioledb`}>
74-
documentation
75-
</InlineLink>{' '}
76-
for all limitations.
77-
</TooltipContent>
78-
</Tooltip>
79-
)}
80-
<ComputeBadgeWrapper
81-
projectRef={project?.ref}
82-
slug={organization?.slug}
83-
cloudProvider={project?.cloud_provider}
84-
computeSize={project?.infra_compute_size}
85-
resourceWarnings={projectResourceWarnings}
86-
/>
87-
{project?.high_availability && <HighAvailabilityBadge />}
88-
</div>
57+
{isOrioleDb && (
58+
<Tooltip>
59+
<TooltipTrigger asChild>
60+
<Badge variant="warning">OrioleDB</Badge>
61+
</TooltipTrigger>
62+
<TooltipContent side="bottom" align="start" className="max-w-80 text-center">
63+
This project is using Postgres with OrioleDB which is currently in preview and
64+
not suitable for production workloads. View our{' '}
65+
<InlineLink href={`${DOCS_URL}/guides/database/orioledb`}>
66+
documentation
67+
</InlineLink>{' '}
68+
for all limitations.
69+
</TooltipContent>
70+
</Tooltip>
71+
)}
8972
</div>
9073
<ProjectConnectionPopover projectRef={project?.ref} />
9174
</div>

0 commit comments

Comments
 (0)