Skip to content

Commit 0e2171e

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/controller-rework
2 parents 71cd80e + 1816115 commit 0e2171e

File tree

13 files changed

+253
-29
lines changed

13 files changed

+253
-29
lines changed

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,52 @@ LIMIT ${pageSize} OFFSET ${pageSize * (page - 1)};`;
168168
}),
169169
};
170170
}
171+
172+
public async findPageForVersion({
173+
userId,
174+
projectSlug,
175+
organizationSlug,
176+
environmentSlug,
177+
version,
178+
}: {
179+
userId: User["id"];
180+
projectSlug: Project["slug"];
181+
organizationSlug: Organization["slug"];
182+
environmentSlug: string;
183+
version: string;
184+
}) {
185+
const project = await this.#prismaClient.project.findFirstOrThrow({
186+
select: {
187+
id: true,
188+
},
189+
where: {
190+
slug: projectSlug,
191+
organization: {
192+
slug: organizationSlug,
193+
members: {
194+
some: {
195+
userId,
196+
},
197+
},
198+
},
199+
},
200+
});
201+
202+
const environment = await findEnvironmentBySlug(project.id, environmentSlug, userId);
203+
if (!environment) {
204+
throw new Error(`Environment not found`);
205+
}
206+
207+
// Find how many deployments have been made since this version
208+
const deploymentsSinceVersion = await this.#prismaClient.$queryRaw<{ count: BigInt }[]>`
209+
SELECT COUNT(*) as count
210+
FROM ${sqlDatabaseSchema}."WorkerDeployment"
211+
WHERE "projectId" = ${project.id}
212+
AND "environmentId" = ${environment.id}
213+
AND string_to_array(version, '.')::int[] > string_to_array(${version}, '.')::int[]
214+
`;
215+
216+
const count = Number(deploymentsSinceVersion[0].count);
217+
return Math.floor(count / pageSize) + 1;
218+
}
171219
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class TestPresenter extends BasePresenter {
5656
JOIN ${sqlDatabaseSchema}."BackgroundWorkerTask" bwt ON bwt."workerId" = latest_workers.id
5757
ORDER BY slug ASC;`;
5858
} else {
59-
const currentDeployment = await findCurrentWorkerDeployment(envId);
59+
const currentDeployment = await findCurrentWorkerDeployment({ environmentId: envId });
6060
return currentDeployment?.worker?.tasks ?? [];
6161
}
6262
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export class TestTaskPresenter {
8888
}: TestTaskOptions): Promise<TestTaskResult> {
8989
let task: BackgroundWorkerTaskSlim | null = null;
9090
if (environment.type !== "DEVELOPMENT") {
91-
const deployment = await findCurrentWorkerDeployment(environment.id);
91+
const deployment = await findCurrentWorkerDeployment({ environmentId: environment.id });
9292
if (deployment) {
9393
task = deployment.worker?.tasks.find((t) => t.slug === taskIdentifier) ?? null;
9494
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import { useUser } from "~/hooks/useUser";
2828
import { DeploymentPresenter } from "~/presenters/v3/DeploymentPresenter.server";
2929
import { requireUserId } from "~/services/session.server";
3030
import { cn } from "~/utils/cn";
31-
import { v3DeploymentParams, v3DeploymentsPath } from "~/utils/pathBuilder";
31+
import { v3DeploymentParams, v3DeploymentsPath, v3RunsPath } from "~/utils/pathBuilder";
3232
import { capitalizeWord } from "~/utils/string";
3333

3434
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
@@ -231,16 +231,19 @@ export default function Page() {
231231
</TableHeader>
232232
<TableBody>
233233
{deployment.tasks.map((t) => {
234+
const path = v3RunsPath(organization, project, environment, {
235+
tasks: [t.slug],
236+
});
234237
return (
235238
<TableRow key={t.slug}>
236-
<TableCell>
239+
<TableCell to={path}>
237240
<div className="inline-flex flex-col gap-0.5">
238241
<Paragraph variant="extra-small" className="text-text-dimmed">
239242
{t.slug}
240243
</Paragraph>
241244
</div>
242245
</TableCell>
243-
<TableCell>{t.filePath}</TableCell>
246+
<TableCell to={path}>{t.filePath}</TableCell>
244247
</TableRow>
245248
);
246249
})}

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

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { ArrowPathIcon, ArrowUturnLeftIcon, BookOpenIcon } from "@heroicons/react/20/solid";
2-
import { type MetaFunction, Outlet, useLocation, useParams } from "@remix-run/react";
2+
import { type MetaFunction, Outlet, useLocation, useParams, useNavigate } from "@remix-run/react";
33
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
44
import { typedjson, useTypedLoaderData } from "remix-typedjson";
55
import { z } from "zod";
66
import { PromoteIcon } from "~/assets/icons/PromoteIcon";
77
import { DeploymentsNone, DeploymentsNoneDev } from "~/components/BlankStatePanels";
88
import { UserAvatar } from "~/components/UserProfilePhoto";
9-
import { EnvironmentCombo } from "~/components/environments/EnvironmentLabel";
109
import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout";
1110
import { Badge } from "~/components/primitives/Badge";
1211
import { Button, LinkButton } from "~/components/primitives/Buttons";
@@ -53,6 +52,7 @@ import { EnvironmentParamSchema, docsPath, v3DeploymentPath } from "~/utils/path
5352
import { createSearchParams } from "~/utils/searchParams";
5453
import { deploymentIndexingIsRetryable } from "~/v3/deploymentStatus";
5554
import { compareDeploymentVersions } from "~/v3/utils/deploymentVersions";
55+
import { useEffect } from "react";
5656

5757
export const meta: MetaFunction = () => {
5858
return [
@@ -64,17 +64,37 @@ export const meta: MetaFunction = () => {
6464

6565
const SearchParams = z.object({
6666
page: z.coerce.number().optional(),
67+
version: z.string().optional(),
6768
});
6869

6970
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
7071
const userId = await requireUserId(request);
7172
const { organizationSlug, projectParam, envParam } = EnvironmentParamSchema.parse(params);
7273

7374
const searchParams = createSearchParams(request.url, SearchParams);
74-
const page = searchParams.success ? searchParams.params.get("page") ?? 1 : 1;
75+
76+
let page = searchParams.success ? Number(searchParams.params.get("page") ?? 1) : 1;
77+
const version = searchParams.success ? searchParams.params.get("version")?.toString() : undefined;
78+
79+
const presenter = new DeploymentListPresenter();
80+
81+
// If we have a version, find its page
82+
if (version) {
83+
try {
84+
page = await presenter.findPageForVersion({
85+
userId,
86+
organizationSlug,
87+
projectSlug: projectParam,
88+
environmentSlug: envParam,
89+
version,
90+
});
91+
} catch (error) {
92+
console.error("Error finding page for version", error);
93+
// Carry on, we'll just show the selected page
94+
}
95+
}
7596

7697
try {
77-
const presenter = new DeploymentListPresenter();
7898
const result = await presenter.call({
7999
userId,
80100
organizationSlug,
@@ -83,7 +103,12 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
83103
page,
84104
});
85105

86-
return typedjson(result);
106+
// If we have a version, find the deployment
107+
const selectedDeployment = version
108+
? result.deployments.find((d) => d.version === version)
109+
: undefined;
110+
111+
return typedjson({ ...result, selectedDeployment });
87112
} catch (error) {
88113
console.error(error);
89114
throw new Response(undefined, {
@@ -97,10 +122,23 @@ export default function Page() {
97122
const organization = useOrganization();
98123
const project = useProject();
99124
const environment = useEnvironment();
100-
const { deployments, currentPage, totalPages } = useTypedLoaderData<typeof loader>();
125+
const { deployments, currentPage, totalPages, selectedDeployment } =
126+
useTypedLoaderData<typeof loader>();
101127
const hasDeployments = totalPages > 0;
102128

103129
const { deploymentParam } = useParams();
130+
const location = useLocation();
131+
const navigate = useNavigate();
132+
133+
// If we have a selected deployment from the version param, show it
134+
useEffect(() => {
135+
if (selectedDeployment && !deploymentParam) {
136+
const searchParams = new URLSearchParams(location.search);
137+
searchParams.delete("version");
138+
searchParams.set("page", currentPage.toString());
139+
navigate(`${location.pathname}/${selectedDeployment.shortCode}?${searchParams.toString()}`);
140+
}
141+
}, [selectedDeployment, deploymentParam, location.search]);
104142

105143
const currentDeployment = deployments.find((d) => d.isCurrent);
106144

apps/webapp/app/routes/projects.v3.$projectRef.deployments.$deploymentParam.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
3333
return new Response("Not found", { status: 404 });
3434
}
3535

36-
// Redirect to the project's runs page
36+
// Redirect to the project's deployments page
3737
return redirect(
3838
`/orgs/${project.organization.slug}/projects/${project.slug}/deployments/${validatedParams.deploymentParam}`
3939
);

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import { formatCurrencyAccurate } from "~/utils/numberFormatter";
5959
import {
6060
docsPath,
6161
v3BatchPath,
62+
v3DeploymentVersionPath,
6263
v3RunDownloadLogsPath,
6364
v3RunPath,
6465
v3RunSpanPath,
@@ -527,7 +528,26 @@ function RunBody({
527528
<Property.Label>Version</Property.Label>
528529
<Property.Value>
529530
{run.version ? (
530-
run.version
531+
environment.type === "DEVELOPMENT" ? (
532+
run.version
533+
) : (
534+
<SimpleTooltip
535+
button={
536+
<TextLink
537+
to={v3DeploymentVersionPath(
538+
organization,
539+
project,
540+
environment,
541+
run.version
542+
)}
543+
className="group flex flex-wrap items-center gap-x-1 gap-y-0"
544+
>
545+
{run.version}
546+
</TextLink>
547+
}
548+
content={"Jump to deployment"}
549+
/>
550+
)
531551
) : (
532552
<span className="flex items-center gap-1">
533553
<span>Never started</span>

apps/webapp/app/utils/pathBuilder.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,15 @@ export function v3DeploymentPath(
395395
return `${v3DeploymentsPath(organization, project, environment)}/${deployment.shortCode}${query}`;
396396
}
397397

398+
export function v3DeploymentVersionPath(
399+
organization: OrgForPath,
400+
project: ProjectForPath,
401+
environment: EnvironmentForPath,
402+
version: string
403+
) {
404+
return `${v3DeploymentsPath(organization, project, environment)}?version=${version}`;
405+
}
406+
398407
export function v3BillingPath(organization: OrgForPath, message?: string) {
399408
return `${organizationPath(organization)}/settings/billing${
400409
message ? `?message=${encodeURIComponent(message)}` : ""

apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,10 @@ export class SharedQueueConsumer {
612612
? await getWorkerDeploymentFromWorkerTask(existingTaskRun.lockedById)
613613
: existingTaskRun.lockedToVersionId
614614
? await getWorkerDeploymentFromWorker(existingTaskRun.lockedToVersionId)
615-
: await findCurrentWorkerDeployment(existingTaskRun.runtimeEnvironmentId);
615+
: await findCurrentWorkerDeployment({
616+
environmentId: existingTaskRun.runtimeEnvironmentId,
617+
type: "V1",
618+
});
616619
});
617620

618621
const worker = deployment?.worker;

apps/webapp/app/v3/models/workerDeployment.server.ts

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Prettify } from "@trigger.dev/core";
2-
import { BackgroundWorker, RunEngineVersion, WorkerDeployment } from "@trigger.dev/database";
2+
import { BackgroundWorker, RunEngineVersion, WorkerDeploymentType } from "@trigger.dev/database";
33
import {
44
CURRENT_DEPLOYMENT_LABEL,
55
CURRENT_UNMANAGED_DEPLOYMENT_LABEL,
@@ -56,10 +56,23 @@ type WorkerDeploymentWithWorkerTasks = Prisma.WorkerDeploymentGetPayload<{
5656
};
5757
}>;
5858

59-
export async function findCurrentWorkerDeployment(
60-
environmentId: string,
61-
label = CURRENT_DEPLOYMENT_LABEL
62-
): Promise<WorkerDeploymentWithWorkerTasks | undefined> {
59+
/**
60+
* Finds the current worker deployment for a given environment.
61+
*
62+
* @param environmentId - The ID of the environment to find the current worker deployment for.
63+
* @param label - The label of the current worker deployment to find.
64+
* @param type - The type of worker deployment to find. If the current deployment is NOT of this type,
65+
* we will return the latest deployment of the given type.
66+
*/
67+
export async function findCurrentWorkerDeployment({
68+
environmentId,
69+
label = CURRENT_DEPLOYMENT_LABEL,
70+
type,
71+
}: {
72+
environmentId: string;
73+
label?: string;
74+
type?: WorkerDeploymentType;
75+
}): Promise<WorkerDeploymentWithWorkerTasks | undefined> {
6376
const promotion = await prisma.workerDeploymentPromotion.findFirst({
6477
where: {
6578
environmentId,
@@ -71,6 +84,7 @@ export async function findCurrentWorkerDeployment(
7184
id: true,
7285
imageReference: true,
7386
version: true,
87+
type: true,
7488
worker: {
7589
select: {
7690
id: true,
@@ -88,7 +102,52 @@ export async function findCurrentWorkerDeployment(
88102
},
89103
});
90104

91-
return promotion?.deployment;
105+
if (!promotion) {
106+
return undefined;
107+
}
108+
109+
if (!type) {
110+
return promotion.deployment;
111+
}
112+
113+
if (promotion.deployment.type === type) {
114+
return promotion.deployment;
115+
}
116+
117+
// We need to get the latest deployment of the given type
118+
const latestDeployment = await prisma.workerDeployment.findFirst({
119+
where: {
120+
environmentId,
121+
type,
122+
},
123+
orderBy: {
124+
id: "desc",
125+
},
126+
select: {
127+
id: true,
128+
imageReference: true,
129+
version: true,
130+
type: true,
131+
worker: {
132+
select: {
133+
id: true,
134+
friendlyId: true,
135+
version: true,
136+
sdkVersion: true,
137+
cliVersion: true,
138+
supportsLazyAttempts: true,
139+
tasks: true,
140+
engine: true,
141+
},
142+
},
143+
},
144+
});
145+
146+
if (!latestDeployment) {
147+
return undefined;
148+
}
149+
150+
return latestDeployment;
92151
}
93152

94153
export async function getCurrentWorkerDeploymentEngineVersion(
@@ -119,7 +178,11 @@ export async function getCurrentWorkerDeploymentEngineVersion(
119178
export async function findCurrentUnmanagedWorkerDeployment(
120179
environmentId: string
121180
): Promise<WorkerDeploymentWithWorkerTasks | undefined> {
122-
return await findCurrentWorkerDeployment(environmentId, CURRENT_UNMANAGED_DEPLOYMENT_LABEL);
181+
return await findCurrentWorkerDeployment({
182+
environmentId,
183+
label: CURRENT_UNMANAGED_DEPLOYMENT_LABEL,
184+
type: "UNMANAGED",
185+
});
123186
}
124187

125188
export async function findCurrentWorkerFromEnvironment(
@@ -140,7 +203,10 @@ export async function findCurrentWorkerFromEnvironment(
140203
});
141204
return latestDevWorker;
142205
} else {
143-
const deployment = await findCurrentWorkerDeployment(environment.id, label);
206+
const deployment = await findCurrentWorkerDeployment({
207+
environmentId: environment.id,
208+
label,
209+
});
144210
return deployment?.worker ?? null;
145211
}
146212
}

0 commit comments

Comments
 (0)