diff --git a/.server-changes/external-deployment-id-dashboard.md b/.server-changes/external-deployment-id-dashboard.md new file mode 100644 index 0000000000..b337b1fc12 --- /dev/null +++ b/.server-changes/external-deployment-id-dashboard.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: feature +--- + +Deployments and runs now show the external ID they were deployed or pinned with, so you can trace a run back to the release of your app that triggered it. diff --git a/apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts b/apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts index f73366a5da..9968324052 100644 --- a/apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/DeploymentListPresenter.server.ts @@ -153,6 +153,7 @@ export class DeploymentListPresenter { userDisplayName: string | null; userAvatarUrl: string | null; type: WorkerInstanceGroupType; + externalId: string | null; git: Prisma.JsonValue | null; integrationDeploymentId: string | null; }[] @@ -173,6 +174,7 @@ export class DeploymentListPresenter { wd."builtAt", wd."deployedAt", wd."type", + wd."externalId", wd."git" ${vercelSelect} FROM @@ -258,6 +260,7 @@ LIMIT ${pageSize} OFFSET ${pageSize * (page - 1)};`; avatarUrl: deployment.userAvatarUrl, } : undefined, + externalId: deployment.externalId, git: processGitMetadata(deployment.git), vercelDeploymentUrl, }; diff --git a/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts b/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts index a990957588..3e5df60525 100644 --- a/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts @@ -101,6 +101,7 @@ export class DeploymentPresenter { externalBuildData: true, projectId: true, type: true, + externalId: true, environment: { select: { id: true, @@ -272,6 +273,7 @@ export class DeploymentPresenter { errorData: DeploymentPresenter.prepareErrorData(deployment.errorData), isBuilt: !!deployment.builtAt, type: deployment.type, + externalId: deployment.externalId, git: gitMetadata, triggeredVia: deployment.triggeredVia, vercelDeploymentUrl, diff --git a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts index 11b923566c..8482bb39dc 100644 --- a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts @@ -1,3 +1,4 @@ +import { readExternalDeploymentIdAnnotation } from "@internal/run-engine"; import { type MachinePreset, prettyPrintPacket, @@ -399,6 +400,7 @@ export class SpanPresenter extends BasePresenter { ttl: run.ttl, taskIdentifier: run.taskIdentifier, version: lockedWorker?.lockedToVersion?.version, + externalDeploymentId: readExternalDeploymentIdAnnotation(run.annotations), sdkVersion: lockedWorker?.lockedToVersion?.sdkVersion, runtime: lockedWorker?.lockedToVersion?.runtime, runtimeVersion: lockedWorker?.lockedToVersion?.runtimeVersion, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx index 320a3e66d0..15a9c3915e 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx @@ -180,6 +180,21 @@ function getTriggeredViaDisplay(triggeredVia: string | null | undefined): { } } +const EXTERNAL_ID_DISPLAY_LENGTH = 24; + +function ExternalIdValue({ externalId }: { externalId: string | null }) { + if (!externalId) { + return <>–; + } + + const display = + externalId.length > EXTERNAL_ID_DISPLAY_LENGTH + ? `${externalId.slice(0, EXTERNAL_ID_DISPLAY_LENGTH)}…` + : externalId; + + return ; +} + export default function Page() { const { deployment, eventStream } = useTypedLoaderData(); const organization = useOrganization(); @@ -445,6 +460,12 @@ export default function Page() { Worker type {capitalizeWord(deployment.type)} + + External ID + + + + Started at diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx index 66fdc0d964..06f4ded69d 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments/route.tsx @@ -42,6 +42,7 @@ import { collapsibleHandleClassName, } from "~/components/primitives/Resizable"; import { + CopyableTableCell, Table, TableBlankRow, TableBody, @@ -256,8 +257,9 @@ export default function Page() { Tasks Deployed at Deployed by - Git + External ID {hasVercelIntegration && Linked} + Git Go to page @@ -332,11 +334,11 @@ export default function Page() { "–" )} - -
- -
-
+ {hasVercelIntegration && ( {deployment.vercelDeploymentUrl ? ( @@ -353,6 +355,11 @@ export default function Page() { )} )} + +
+ +
+
+ No deploys match your filters @@ -602,6 +609,30 @@ function DeploymentActionsCell({ ); } +function DeploymentExternalIdCell({ + externalId, + path, + isSelected, +}: { + externalId: string | null; + path: string; + isSelected: boolean; +}) { + if (!externalId) { + return ( + + – + + ); + } + + return ( + + {externalId.length > 12 ? `${externalId.slice(0, 12)}…` : externalId} + + ); +} + type RollbackDeploymentDialogProps = { projectId: string; deploymentShortCode: string; diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx index 807e527793..bcfabb8449 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx @@ -872,6 +872,12 @@ function RunBody({ )}
+ + External deployment ID + + + + SDK version @@ -1274,6 +1280,27 @@ function MiniStat({ ); } +const EXTERNAL_DEPLOYMENT_ID_DISPLAY_LENGTH = 24; + +function ExternalDeploymentIdValue({ + externalDeploymentId, +}: { + externalDeploymentId: string | undefined; +}) { + if (!externalDeploymentId) { + return <>–; + } + + const display = + externalDeploymentId.length > EXTERNAL_DEPLOYMENT_ID_DISPLAY_LENGTH + ? `${externalDeploymentId.slice(0, EXTERNAL_DEPLOYMENT_ID_DISPLAY_LENGTH)}…` + : externalDeploymentId; + + return ( + + ); +} + // A compact "mini queue" for a waiting run: the queue page's stat blocks + charts, scaled down. function WaitingInQueueBlock({ queueName, diff --git a/apps/webapp/app/v3/mollifier/syntheticSpanRun.server.ts b/apps/webapp/app/v3/mollifier/syntheticSpanRun.server.ts index f36d5a5d28..11aa7c85ba 100644 --- a/apps/webapp/app/v3/mollifier/syntheticSpanRun.server.ts +++ b/apps/webapp/app/v3/mollifier/syntheticSpanRun.server.ts @@ -1,3 +1,4 @@ +import { readExternalDeploymentIdAnnotation } from "@internal/run-engine"; import { prettyPrintPacket, RunAnnotations } from "@trigger.dev/core/v3"; import { getMaxDuration } from "@trigger.dev/core/v3/isomorphic"; import { @@ -124,6 +125,7 @@ export async function buildSyntheticSpanRun(args: { ttl: run.ttl ?? null, taskIdentifier: run.taskIdentifier ?? "", version: undefined, + externalDeploymentId: readExternalDeploymentIdAnnotation(run.annotations), sdkVersion: undefined, runtime: undefined, runtimeVersion: undefined, diff --git a/internal-packages/run-engine/src/index.ts b/internal-packages/run-engine/src/index.ts index b7898e6433..2c54e4c20c 100644 --- a/internal-packages/run-engine/src/index.ts +++ b/internal-packages/run-engine/src/index.ts @@ -13,6 +13,7 @@ export type { PendingVersionRunIdLookupResult, } from "./engine/services/pendingVersionLookup.js"; export { NoopPendingVersionRunIdLookup } from "./engine/services/pendingVersionLookup.js"; +export { readExternalDeploymentIdAnnotation } from "./engine/systems/pendingVersionSystem.js"; export { PassthroughControlPlaneResolver } from "./engine/controlPlaneResolver.js"; export type { ControlPlaneResolver,