Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions apps/webapp/app/components/integrations/VercelLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { VercelLogo } from "./VercelLogo";
import { LinkButton } from "~/components/primitives/Buttons";
import { SimpleTooltip } from "~/components/primitives/Tooltip";

export function VercelLink({ vercelDeploymentUrl }: { vercelDeploymentUrl: string }) {
return (
<SimpleTooltip
button={
<LinkButton
variant="minimal/small"
LeadingIcon={<VercelLogo className="size-3.5" />}
iconSpacing="gap-x-1"
to={vercelDeploymentUrl}
className="pl-1"
>
Vercel
</LinkButton>
}
content="View on Vercel"
/>
);
}
12 changes: 9 additions & 3 deletions apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
import { type User } from "~/models/user.server";
import { processGitMetadata } from "./BranchesPresenter.server";
import { BranchTrackingConfigSchema, getTrackedBranchForEnvironment } from "~/v3/github";
import { VercelProjectIntegrationDataSchema } from "~/v3/vercel/vercelProjectIntegrationSchema";
import {
VercelProjectIntegrationDataSchema,
buildVercelDeploymentUrl,
} from "~/v3/vercel/vercelProjectIntegrationSchema";

const pageSize = 20;

Expand Down Expand Up @@ -232,8 +235,11 @@ LIMIT ${pageSize} OFFSET ${pageSize * (page - 1)};`;

let vercelDeploymentUrl: string | null = null;
if (hasVercelIntegration && deployment.integrationDeploymentId && vercelTeamSlug && vercelProjectName) {
const vercelId = deployment.integrationDeploymentId.replace(/^dpl_/, "");
vercelDeploymentUrl = `https://vercel.com/${vercelTeamSlug}/${vercelProjectName}/${vercelId}`;
vercelDeploymentUrl = buildVercelDeploymentUrl(
vercelTeamSlug,
vercelProjectName,
deployment.integrationDeploymentId
);
}

return {
Expand Down
53 changes: 53 additions & 0 deletions apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
import { type User } from "~/models/user.server";
import { getUsername } from "~/utils/username";
import { processGitMetadata } from "./BranchesPresenter.server";
import {
VercelProjectIntegrationDataSchema,
buildVercelDeploymentUrl,
} from "~/v3/vercel/vercelProjectIntegrationSchema";
import { S2 } from "@s2-dev/streamstore";
import { env } from "~/env.server";
import { createRedisClient } from "~/redis.server";
Expand Down Expand Up @@ -161,6 +165,54 @@ export class DeploymentPresenter {
});

const gitMetadata = processGitMetadata(deployment.git);

// Look up Vercel integration data to construct a deployment URL
let vercelDeploymentUrl: string | undefined;
const vercelProjectIntegration =
await this.#prismaClient.organizationProjectIntegration.findFirst({
where: {
projectId: project.id,
deletedAt: null,
organizationIntegration: {
service: "VERCEL",
deletedAt: null,
},
},
select: {
integrationData: true,
},
});

if (vercelProjectIntegration) {
const parsed = VercelProjectIntegrationDataSchema.safeParse(
vercelProjectIntegration.integrationData
);

if (parsed.success && parsed.data.vercelTeamSlug) {
const integrationDeployment =
await this.#prismaClient.integrationDeployment.findFirst({
where: {
deploymentId: deployment.id,
integrationName: "vercel",
},
select: {
integrationDeploymentId: true,
},
orderBy: {
createdAt: "desc",
},
});

if (integrationDeployment) {
vercelDeploymentUrl = buildVercelDeploymentUrl(
parsed.data.vercelTeamSlug,
parsed.data.vercelProjectName,
integrationDeployment.integrationDeploymentId
);
}
}
}

const externalBuildData = deployment.externalBuildData
? ExternalBuildData.safeParse(deployment.externalBuildData)
: undefined;
Expand Down Expand Up @@ -227,6 +279,7 @@ export class DeploymentPresenter {
type: deployment.type,
git: gitMetadata,
triggeredVia: deployment.triggeredVia,
vercelDeploymentUrl,
},
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from "lucide-react";
import { ExitIcon } from "~/assets/icons/ExitIcon";
import { GitMetadata } from "~/components/GitMetadata";
import { VercelLink } from "~/components/integrations/VercelLink";
import { RuntimeIcon } from "~/components/RuntimeIcon";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
Expand Down Expand Up @@ -516,6 +517,16 @@ export default function Page() {
})()}
</Property.Value>
</Property.Item>
{deployment.vercelDeploymentUrl && (
<Property.Item>
<Property.Label>Linked</Property.Label>
<Property.Value>
<div className="-ml-1 mt-0.5 flex flex-col">
<VercelLink vercelDeploymentUrl={deployment.vercelDeploymentUrl} />
</div>
</Property.Value>
</Property.Item>
)}
</Property.Table>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { useEffect } from "react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { PromoteIcon } from "~/assets/icons/PromoteIcon";
import { VercelLogo } from "~/components/integrations/VercelLogo";
import { VercelLink } from "~/components/integrations/VercelLink";
import { DeploymentsNone, DeploymentsNoneDev } from "~/components/BlankStatePanels";
import { OctoKitty } from "~/components/GitHubLoginButton";
import { GitMetadata } from "~/components/GitMetadata";
Expand Down Expand Up @@ -56,7 +56,6 @@ import {
TableHeaderCell,
TableRow,
} from "~/components/primitives/Table";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import {
DeploymentStatus,
deploymentStatusDescription,
Expand Down Expand Up @@ -314,20 +313,14 @@ export default function Page() {
{hasVercelIntegration && (
<TableCell isSelected={isSelected}>
{deployment.vercelDeploymentUrl ? (
<SimpleTooltip
button={
<a
href={deployment.vercelDeploymentUrl}
target="_blank"
rel="noreferrer noopener"
className="flex items-center text-text-dimmed transition-colors hover:text-text-bright"
onClick={(e) => e.stopPropagation()}
>
<VercelLogo className="size-3.5" />
</a>
}
content="View on Vercel"
/>
<div
className="-ml-1 flex items-center"
onClick={(e) => e.stopPropagation()}
>
<VercelLink
vercelDeploymentUrl={deployment.vercelDeploymentUrl}
/>
</div>
) : (
"–"
)}
Expand Down
9 changes: 9 additions & 0 deletions apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,15 @@ export function shouldSyncEnvVarForAnyEnvironment(
return false;
}

export function buildVercelDeploymentUrl(
vercelTeamSlug: string | undefined,
vercelProjectName: string,
integrationDeploymentId: string
): string {
const vercelId = integrationDeploymentId.replace(/^dpl_/, "");
return `https://vercel.com/${vercelTeamSlug}/${vercelProjectName}/${vercelId}`;
}

export function isPullEnvVarsEnabledForEnvironment(
pullEnvVarsBeforeBuild: EnvSlug[] | null | undefined,
environmentType: TriggerEnvironmentType
Expand Down