Skip to content

Commit 56f1ca5

Browse files
committed
fix(Vercel): Initial deployment fix
1 parent c00dae0 commit 56f1ca5

File tree

6 files changed

+45
-43
lines changed

6 files changed

+45
-43
lines changed

apps/webapp/app/components/integrations/VercelOnboardingModal.tsx

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ export function VercelOnboardingModal({
146146
}
147147
return "project-selection";
148148
}
149+
// If onboarding was already completed but GitHub is not connected,
150+
// go directly to the github-connection step (e.g., returning from GitHub App installation)
151+
if (onboardingData?.isOnboardingComplete && !onboardingData?.isGitHubConnected) {
152+
return "github-connection";
153+
}
149154
// For marketplace origin, skip env-mapping step and go directly to env-var-sync
150155
if (!fromMarketplaceContext) {
151156
const customEnvs = (onboardingData?.customEnvironments?.length ?? 0) > 0 && hasStagingEnvironment;
@@ -1159,26 +1164,7 @@ export function VercelOnboardingModal({
11591164
>
11601165
Complete
11611166
</Button>
1162-
) : (
1163-
<Button
1164-
variant="tertiary/medium"
1165-
onClick={() => {
1166-
trackOnboarding("vercel onboarding github skipped");
1167-
setState("completed");
1168-
if (fromMarketplaceContext && nextUrl) {
1169-
const validUrl = safeRedirectUrl(nextUrl);
1170-
if (validUrl) {
1171-
window.location.href = validUrl;
1172-
}
1173-
}
1174-
}}
1175-
>
1176-
Skip for now
1177-
</Button>
1178-
)
1179-
}
1180-
cancelButton={
1181-
isGitHubConnectedForOnboarding && fromMarketplaceContext && nextUrl ? (
1167+
) : !fromMarketplaceContext ? (
11821168
<Button
11831169
variant="tertiary/medium"
11841170
onClick={() => {

apps/webapp/app/routes/login._index/route.tsx

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
8080
showGoogleAuth: isGoogleAuthSupported,
8181
lastAuthMethod,
8282
authError: null,
83+
isVercelMarketplace: redirectTo.startsWith("/vercel/callback"),
8384
},
8485
{
8586
headers: {
@@ -106,6 +107,7 @@ export async function loader({ request }: LoaderFunctionArgs) {
106107
showGoogleAuth: isGoogleAuthSupported,
107108
lastAuthMethod,
108109
authError,
110+
isVercelMarketplace: false,
109111
});
110112
}
111113
}
@@ -164,19 +166,21 @@ export default function LoginPage() {
164166
</Form>
165167
</div>
166168
)}
167-
<div className="relative w-full">
168-
{data.lastAuthMethod === "email" && <LastUsedBadge />}
169-
<LinkButton
170-
to={data.redirectTo ? `/login/magic?redirectTo=${encodeURIComponent(data.redirectTo)}` : "/login/magic"}
171-
variant="secondary/extra-large"
172-
fullWidth
173-
data-action="continue with email"
174-
className="text-text-bright"
175-
>
176-
<EnvelopeIcon className="mr-2 size-5 text-text-bright" />
177-
Continue with Email
178-
</LinkButton>
179-
</div>
169+
{!data.isVercelMarketplace && (
170+
<div className="relative w-full">
171+
{data.lastAuthMethod === "email" && <LastUsedBadge />}
172+
<LinkButton
173+
to={data.redirectTo ? `/login/magic?redirectTo=${encodeURIComponent(data.redirectTo)}` : "/login/magic"}
174+
variant="secondary/extra-large"
175+
fullWidth
176+
data-action="continue with email"
177+
className="text-text-bright"
178+
>
179+
<EnvelopeIcon className="mr-2 size-5 text-text-bright" />
180+
Continue with Email
181+
</LinkButton>
182+
</div>
183+
)}
180184
{data.authError && <FormError>{data.authError}</FormError>}
181185
</div>
182186
<Paragraph variant="extra-small" className="mt-2 text-center">

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
4141
import { ProjectSettingsService } from "~/services/projectSettings.server";
4242
import { logger } from "~/services/logger.server";
4343
import { triggerInitialDeployment } from "~/services/platform.v3.server";
44+
import { VercelIntegrationService } from "~/services/vercelIntegration.server";
4445
import { requireUserId } from "~/services/session.server";
4546
import {
4647
githubAppInstallPath,
@@ -210,16 +211,22 @@ export async function action({ request, params }: ActionFunctionArgs) {
210211
);
211212

212213
if (resultOrFail.isOk()) {
213-
// Trigger initial deployment for marketplace flows now that GitHub is connected
214-
if (redirectUrl) {
215-
try {
216-
if (redirectUrl.includes("origin=marketplace")) {
217-
await triggerInitialDeployment(projectId, { environment: "prod" });
218-
}
219-
} catch (error) {
220-
logger.error("Invalid redirect URL, skipping initial deployment trigger", { redirectUrl, error });
221-
// Invalid redirectUrl, skip initial deployment check
214+
// Trigger initial deployment for marketplace flows now that GitHub is connected.
215+
// We check the persisted onboardingOrigin on the Vercel integration rather than
216+
// the redirectUrl, because the redirect URL loses the marketplace context when
217+
// the user installs the GitHub App for the first time (full-page redirect cycle).
218+
try {
219+
const vercelService = new VercelIntegrationService();
220+
const vercelIntegration = await vercelService.getVercelProjectIntegration(projectId);
221+
if (
222+
vercelIntegration?.parsedIntegrationData.onboardingCompleted &&
223+
vercelIntegration.parsedIntegrationData.onboardingOrigin === "marketplace"
224+
) {
225+
logger.info("Marketplace flow detected, triggering initial deployment", { projectId });
226+
await triggerInitialDeployment(projectId, { environment: "prod" });
222227
}
228+
} catch (error) {
229+
logger.error("Failed to check Vercel integration or trigger initial deployment", { projectId, error });
223230
}
224231

225232
return redirectWithMessage(

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
293293
syncEnvVarsMapping,
294294
next,
295295
skipRedirect,
296+
origin,
296297
} = submission.value;
297298

298299
const parsedStagingEnv = parseVercelStagingEnvironment(vercelStagingEnvironment);
@@ -306,6 +307,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
306307
atomicBuilds,
307308
discoverEnvVars,
308309
syncEnvVarsMapping: parsedSyncEnvVarsMapping,
310+
origin: origin === "marketplace" ? "marketplace" : "dashboard",
309311
});
310312

311313
if (result) {

apps/webapp/app/services/vercelIntegration.server.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ export class VercelIntegrationService {
526526
atomicBuilds?: EnvSlug[] | null;
527527
discoverEnvVars?: EnvSlug[] | null;
528528
syncEnvVarsMapping?: SyncEnvVarsMapping;
529+
origin?: "marketplace" | "dashboard";
529530
}
530531
): Promise<VercelProjectIntegrationWithParsedData | null> {
531532
const existing = await this.getVercelProjectIntegration(projectId);
@@ -544,8 +545,9 @@ export class VercelIntegrationService {
544545
vercelStagingEnvironment: params.vercelStagingEnvironment ?? null,
545546
},
546547
//This is intentionally not updated here, in case of resetting the onboarding it should not override the existing mapping with an empty one
547-
syncEnvVarsMapping: existing.parsedIntegrationData.syncEnvVarsMapping,
548+
syncEnvVarsMapping: existing.parsedIntegrationData.syncEnvVarsMapping,
548549
onboardingCompleted: true,
550+
onboardingOrigin: params.origin ?? existing.parsedIntegrationData.onboardingOrigin,
549551
};
550552

551553
const updated = await this.#prismaClient.organizationProjectIntegration.update({

apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export const VercelProjectIntegrationDataSchema = z.object({
7272
vercelTeamSlug: z.string().optional(),
7373
vercelProjectId: z.string(),
7474
onboardingCompleted: z.boolean().optional(),
75+
onboardingOrigin: z.enum(["marketplace", "dashboard"]).optional(),
7576
});
7677

7778
export type VercelProjectIntegrationData = z.infer<typeof VercelProjectIntegrationDataSchema>;

0 commit comments

Comments
 (0)