diff --git a/apps/studio/components/interfaces/Integrations/VercelGithub/ProjectLinker.tsx b/apps/studio/components/interfaces/Integrations/VercelGithub/ProjectLinker.tsx
index 8ea17f52cf8..2a491957170 100644
--- a/apps/studio/components/interfaces/Integrations/VercelGithub/ProjectLinker.tsx
+++ b/apps/studio/components/interfaces/Integrations/VercelGithub/ProjectLinker.tsx
@@ -117,7 +117,8 @@ export const ProjectLinker = ({
) : showNoEntitiesState && (noSupabaseProjects || noForeignProjects) ? (
No {missingEntity} projects found. Create a {missingEntity} project to link to a{' '}
- {oppositeMissingEntity} project, or skip and connect later.
+ {oppositeMissingEntity} project
+ {onSkip !== undefined ? ', or skip and connect later.' : '.'}
) : (
<>
@@ -192,8 +193,12 @@ export const ProjectLinker = ({
You will need to create a {missingEntity} Project to link to a {oppositeMissingEntity}{' '}
Project.
-
- You can skip this and create a Project Connection later.
+ {onSkip !== undefined && (
+ <>
+
+ You can skip this and create a Project Connection later.
+ >
+ )}
) : (
diff --git a/apps/studio/lib/integrations/vercel-install.utils.ts b/apps/studio/lib/integrations/vercel-install.utils.ts
index 837ffda50dc..8eaac03c080 100644
--- a/apps/studio/lib/integrations/vercel-install.utils.ts
+++ b/apps/studio/lib/integrations/vercel-install.utils.ts
@@ -13,9 +13,33 @@ export function getErrorMessage(error: unknown): string | undefined {
export type VercelInstallSource = 'deploy-button' | 'marketplace' | 'external'
-export function getVercelInstallSource(
+/**
+ * Vercel sometimes sends source=marketplace with Deploy Button params
+ * (currentProjectId + external-id). Treat that as deploy-button for routing.
+ */
+export function hasVercelDeployButtonSignals({
+ currentProjectId,
+ externalId,
+}: {
+ currentProjectId?: string
+ externalId?: string
+}): boolean {
+ return Boolean(currentProjectId && externalId)
+}
+
+export function resolveVercelInstallSource({
+ source,
+ currentProjectId,
+ externalId,
+}: {
source: string | undefined
-): VercelInstallSource | undefined {
+ currentProjectId?: string
+ externalId?: string
+}): VercelInstallSource | undefined {
+ if (hasVercelDeployButtonSignals({ currentProjectId, externalId })) {
+ return 'deploy-button'
+ }
+
switch (source) {
case 'deploy-button':
case 'marketplace':
diff --git a/apps/studio/pages/integrations/vercel/[slug]/marketplace/choose-project.tsx b/apps/studio/pages/integrations/vercel/[slug]/marketplace/choose-project.tsx
index be6674e039f..7febfc533af 100644
--- a/apps/studio/pages/integrations/vercel/[slug]/marketplace/choose-project.tsx
+++ b/apps/studio/pages/integrations/vercel/[slug]/marketplace/choose-project.tsx
@@ -28,6 +28,7 @@ import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
import { withAuth } from '@/hooks/misc/withAuth'
import { BASE_PATH } from '@/lib/constants'
import { getErrorMessage } from '@/lib/get-error-message'
+import { hasVercelDeployButtonSignals } from '@/lib/integrations/vercel-install.utils'
import { buildStudioPageTitle } from '@/lib/page-title'
import { useProfileNameAndPicture } from '@/lib/profile'
import { EMPTY_ARR } from '@/lib/void'
@@ -40,9 +41,10 @@ const PAGE_TITLE = buildStudioPageTitle({
})
const VercelChooseProjectPage: NextPageWithLayout = () => {
- const { slug, configurationId, next, currentProjectId } = useParams()
+ const { slug, configurationId, next, currentProjectId, externalId } = useParams()
const { username, primaryEmail, avatarUrl } = useProfileNameAndPicture()
const displayName = primaryEmail ?? username ?? ''
+ const isDeployButtonFlow = hasVercelDeployButtonSignals({ currentProjectId, externalId })
const {
data: integrationData,
@@ -199,11 +201,15 @@ const VercelChooseProjectPage: NextPageWithLayout = () => {
getForeignProjectIcon={getForeignProjectIcon}
choosePrompt="Choose Vercel project"
defaultForeignProjectId={currentProjectId}
- onSkip={() => {
- if (next && isVercelUrl(next)) {
- window.location.href = next
- }
- }}
+ onSkip={
+ isDeployButtonFlow
+ ? undefined
+ : () => {
+ if (next && isVercelUrl(next)) {
+ window.location.href = next
+ }
+ }
+ }
loadingForeignProjects={isLoadingVercelProjectsData}
mode="Vercel"
/>
diff --git a/apps/studio/pages/integrations/vercel/install.tsx b/apps/studio/pages/integrations/vercel/install.tsx
index 9defd01e649..abc1c711021 100644
--- a/apps/studio/pages/integrations/vercel/install.tsx
+++ b/apps/studio/pages/integrations/vercel/install.tsx
@@ -32,7 +32,7 @@ import { withAuth } from '@/hooks/misc/withAuth'
import {
buildVercelInstallRouteQuery,
getErrorMessage,
- getVercelInstallSource,
+ resolveVercelInstallSource,
} from '@/lib/integrations/vercel-install.utils'
import { buildStudioPageTitle } from '@/lib/page-title'
import { useProfileNameAndPicture } from '@/lib/profile'
@@ -115,7 +115,11 @@ const VercelIntegration: NextPageWithLayout = () => {
*/
function handleRouteChange() {
const orgSlug = selectedOrg?.slug
- const vercelInstallSource = getVercelInstallSource(source)
+ const vercelInstallSource = resolveVercelInstallSource({
+ source,
+ currentProjectId,
+ externalId,
+ })
const query = buildVercelInstallRouteQuery({
source: vercelInstallSource,
organizationSlug: orgSlug,
diff --git a/apps/studio/tests/pages/integrations/vercel/install.utils.test.ts b/apps/studio/tests/pages/integrations/vercel/install.utils.test.ts
index b8109105f60..7d96716d14d 100644
--- a/apps/studio/tests/pages/integrations/vercel/install.utils.test.ts
+++ b/apps/studio/tests/pages/integrations/vercel/install.utils.test.ts
@@ -3,7 +3,8 @@ import { describe, expect, test } from 'vitest'
import {
buildVercelInstallRouteQuery,
getErrorMessage,
- getVercelInstallSource,
+ hasVercelDeployButtonSignals,
+ resolveVercelInstallSource,
} from '@/lib/integrations/vercel-install.utils'
describe('getErrorMessage', () => {
@@ -89,15 +90,46 @@ describe('buildVercelInstallRouteQuery', () => {
})
})
-describe('getVercelInstallSource', () => {
- test('returns supported Vercel install sources', () => {
- expect(getVercelInstallSource('deploy-button')).toBe('deploy-button')
- expect(getVercelInstallSource('marketplace')).toBe('marketplace')
- expect(getVercelInstallSource('external')).toBe('external')
+describe('hasVercelDeployButtonSignals', () => {
+ test('requires both currentProjectId and externalId', () => {
+ expect(
+ hasVercelDeployButtonSignals({
+ currentProjectId: 'prj_123',
+ externalId: 'https://github.com/org/repo',
+ })
+ ).toBe(true)
+ expect(hasVercelDeployButtonSignals({ currentProjectId: 'prj_123' })).toBe(false)
+ expect(hasVercelDeployButtonSignals({ externalId: 'https://github.com/org/repo' })).toBe(false)
+ expect(hasVercelDeployButtonSignals({})).toBe(false)
})
+})
- test('returns undefined for unsupported sources', () => {
- expect(getVercelInstallSource('deploybutton')).toBeUndefined()
- expect(getVercelInstallSource(undefined)).toBeUndefined()
+describe('resolveVercelInstallSource', () => {
+ test('overrides marketplace and external when deploy-button signals are present', () => {
+ expect(
+ resolveVercelInstallSource({
+ source: 'marketplace',
+ currentProjectId: 'prj_123',
+ externalId: 'https://github.com/org/repo',
+ })
+ ).toBe('deploy-button')
+ expect(
+ resolveVercelInstallSource({
+ source: 'external',
+ currentProjectId: 'prj_123',
+ externalId: 'https://github.com/org/repo',
+ })
+ ).toBe('deploy-button')
+ })
+
+ test('keeps the declared source when deploy-button signals are incomplete', () => {
+ expect(
+ resolveVercelInstallSource({
+ source: 'marketplace',
+ currentProjectId: 'prj_123',
+ })
+ ).toBe('marketplace')
+ expect(resolveVercelInstallSource({ source: 'deploy-button' })).toBe('deploy-button')
+ expect(resolveVercelInstallSource({ source: undefined })).toBeUndefined()
})
})