Skip to content

Commit 738ce5c

Browse files
committed
feat(vercel): refine integration defaults and surface settings
Change default Vercel integration data to enable atomic builds only for production and keep pull env vars enabled for all non-dev environments. This tightens build behavior to match expected defaults and avoids enabling atomic builds for staging/preview by default. Add optional integrationDeployments field to deployment schema and include it in the deployment API response. This introduces a typed array of integration deployment records (id, integrationName, integrationDeploymentId, commitSHA, createdAt) so downstream clients can surface related integration deployment metadata. Expose Vercel project setting autoAssignCustomDomains in presenter by fetching it alongside custom environments. Combine fetching of custom environments and the auto-assign setting into a single helper that returns both values (or null when unknown), and thread the new autoAssignCustomDomains flag through the presenter types. Also rename a UI label from "Pull from Vercel" to "Sync" for clarity.
1 parent 423f985 commit 738ce5c

File tree

9 files changed

+374
-107
lines changed

9 files changed

+374
-107
lines changed

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1678,6 +1678,71 @@ export class VercelIntegrationRepository {
16781678
}
16791679
}
16801680

1681+
/**
1682+
* Get the autoAssignCustomDomains setting for a Vercel project.
1683+
* Returns true if auto-assign is enabled, false if disabled, null on error.
1684+
*/
1685+
static async getAutoAssignCustomDomains(
1686+
client: Vercel,
1687+
vercelProjectId: string,
1688+
teamId?: string | null
1689+
): Promise<boolean | null> {
1690+
try {
1691+
// The Vercel SDK doesn't have a getProject method, so we use updateProject
1692+
// with an empty requestBody to read the project data without changing anything.
1693+
const project = await client.projects.updateProject({
1694+
idOrName: vercelProjectId,
1695+
...(teamId && { teamId }),
1696+
requestBody: {},
1697+
});
1698+
1699+
return project.autoAssignCustomDomains ?? null;
1700+
} catch (error) {
1701+
logger.error("Failed to get Vercel project autoAssignCustomDomains", {
1702+
vercelProjectId,
1703+
teamId,
1704+
error,
1705+
});
1706+
return null;
1707+
}
1708+
}
1709+
1710+
/**
1711+
* Disable autoAssignCustomDomains on a Vercel project.
1712+
* This is required for atomic deployments — prevents Vercel from auto-promoting
1713+
* deployments before TRIGGER_VERSION is set.
1714+
*/
1715+
static async disableAutoAssignCustomDomains(
1716+
client: Vercel,
1717+
vercelProjectId: string,
1718+
teamId?: string | null
1719+
): Promise<{ success: boolean; error?: string }> {
1720+
try {
1721+
await client.projects.updateProject({
1722+
idOrName: vercelProjectId,
1723+
...(teamId && { teamId }),
1724+
requestBody: {
1725+
autoAssignCustomDomains: false,
1726+
},
1727+
});
1728+
1729+
logger.info("Disabled autoAssignCustomDomains on Vercel project", {
1730+
vercelProjectId,
1731+
teamId,
1732+
});
1733+
1734+
return { success: true };
1735+
} catch (error) {
1736+
const errorMessage = `Failed to disable autoAssignCustomDomains: ${error instanceof Error ? error.message : "Unknown error"}`;
1737+
logger.error(errorMessage, {
1738+
vercelProjectId,
1739+
teamId,
1740+
error,
1741+
});
1742+
return { success: false, error: errorMessage };
1743+
}
1744+
}
1745+
16811746
static async uninstallVercelIntegration(
16821747
integration: OrganizationIntegration & { tokenReference: SecretReference }
16831748
): Promise<{ authInvalid: boolean }> {

apps/webapp/app/presenters/v3/VercelSettingsPresenter.server.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ export type VercelSettingsResult = {
3535
hasStagingEnvironment: boolean;
3636
hasPreviewEnvironment: boolean;
3737
customEnvironments: VercelCustomEnvironment[];
38+
/** Whether autoAssignCustomDomains is enabled on the Vercel project. null if unknown. */
39+
autoAssignCustomDomains?: boolean | null;
3840
};
3941

4042
export type VercelAvailableProject = {
@@ -234,26 +236,41 @@ export class VercelSettingsPresenter extends BasePresenter {
234236
checkPreviewEnvironment(),
235237
getVercelProjectIntegration(),
236238
]).andThen(([hasOrgIntegration, isGitHubConnected, hasStagingEnvironment, hasPreviewEnvironment, connectedProject]) => {
237-
const fetchCustomEnvs = async (): Promise<VercelCustomEnvironment[]> => {
238-
if (!connectedProject || !orgIntegration) return [];
239+
const fetchCustomEnvsAndProjectSettings = async (): Promise<{
240+
customEnvironments: VercelCustomEnvironment[];
241+
autoAssignCustomDomains: boolean | null;
242+
}> => {
243+
if (!connectedProject || !orgIntegration) {
244+
return { customEnvironments: [], autoAssignCustomDomains: null };
245+
}
239246
try {
240247
const client = await VercelIntegrationRepository.getVercelClient(orgIntegration);
241248
const teamId = await VercelIntegrationRepository.getTeamIdFromIntegration(orgIntegration);
242-
const result = await VercelIntegrationRepository.getVercelCustomEnvironments(
243-
client,
244-
connectedProject.vercelProjectId,
245-
teamId
246-
);
247-
return result.success ? result.data : [];
249+
const [customEnvsResult, autoAssign] = await Promise.all([
250+
VercelIntegrationRepository.getVercelCustomEnvironments(
251+
client,
252+
connectedProject.vercelProjectId,
253+
teamId
254+
),
255+
VercelIntegrationRepository.getAutoAssignCustomDomains(
256+
client,
257+
connectedProject.vercelProjectId,
258+
teamId
259+
),
260+
]);
261+
return {
262+
customEnvironments: customEnvsResult.success ? customEnvsResult.data : [],
263+
autoAssignCustomDomains: autoAssign,
264+
};
248265
} catch {
249-
return [];
266+
return { customEnvironments: [], autoAssignCustomDomains: null };
250267
}
251268
};
252269

253270
return fromPromise(
254-
fetchCustomEnvs(),
271+
fetchCustomEnvsAndProjectSettings(),
255272
(error) => ({ type: "other" as const, cause: error })
256-
).map((customEnvironments) => ({
273+
).map(({ customEnvironments, autoAssignCustomDomains }) => ({
257274
enabled: true,
258275
hasOrgIntegration,
259276
authInvalid: false,
@@ -262,6 +279,7 @@ export class VercelSettingsPresenter extends BasePresenter {
262279
hasStagingEnvironment,
263280
hasPreviewEnvironment,
264281
customEnvironments,
282+
autoAssignCustomDomains,
265283
} as VercelSettingsResult));
266284
}).mapErr((error) => {
267285
// Log the error and return a safe fallback

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ export default function Page() {
330330
<SimpleTooltip
331331
button={
332332
<span className="flex items-center gap-1">
333-
Pull from Vercel
333+
Sync
334334
<InformationCircleIcon className="size-4 text-text-dimmed" />
335335
</span>
336336
}

apps/webapp/app/routes/api.v1.deployments.$deploymentId.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
3939
tasks: true,
4040
},
4141
},
42+
integrationDeployments: true,
4243
},
4344
});
4445

@@ -69,5 +70,15 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
6970
})),
7071
}
7172
: undefined,
73+
integrationDeployments:
74+
deployment.integrationDeployments.length > 0
75+
? deployment.integrationDeployments.map((id) => ({
76+
id: id.id,
77+
integrationName: id.integrationName,
78+
integrationDeploymentId: id.integrationDeploymentId,
79+
commitSHA: id.commitSHA,
80+
createdAt: id.createdAt,
81+
}))
82+
: undefined,
7283
} satisfies GetDeploymentResponseBody);
7384
}

0 commit comments

Comments
 (0)