|
1 | 1 | import { intro, log, outro } from "@clack/prompts";
|
2 | 2 | import { getBranch, prepareDeploymentError, tryCatch } from "@trigger.dev/core/v3";
|
3 |
| -import { InitializeDeploymentResponseBody } from "@trigger.dev/core/v3/schemas"; |
| 3 | +import { |
| 4 | + InitializeDeploymentRequestBody, |
| 5 | + InitializeDeploymentResponseBody, |
| 6 | +} from "@trigger.dev/core/v3/schemas"; |
4 | 7 | import { Command, Option as CommandOption } from "commander";
|
5 | 8 | import { resolve } from "node:path";
|
6 | 9 | import { isCI } from "std-env";
|
@@ -306,19 +309,17 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
|
306 | 309 | return;
|
307 | 310 | }
|
308 | 311 |
|
309 |
| - const deploymentResponse = await projectClient.client.initializeDeployment({ |
310 |
| - contentHash: buildManifest.contentHash, |
311 |
| - userId: authorization.auth.tokenType === "personal" ? authorization.userId : undefined, |
312 |
| - gitMeta, |
313 |
| - type: features.run_engine_v2 ? "MANAGED" : "V1", |
314 |
| - runtime: buildManifest.runtime, |
315 |
| - }); |
316 |
| - |
317 |
| - if (!deploymentResponse.success) { |
318 |
| - throw new Error(`Failed to start deployment: ${deploymentResponse.error}`); |
319 |
| - } |
320 |
| - |
321 |
| - const deployment = deploymentResponse.data; |
| 312 | + const deployment = await initializeOrAttachDeployment( |
| 313 | + projectClient.client, |
| 314 | + { |
| 315 | + contentHash: buildManifest.contentHash, |
| 316 | + userId: authorization.auth.tokenType === "personal" ? authorization.userId : undefined, |
| 317 | + gitMeta, |
| 318 | + type: features.run_engine_v2 ? "MANAGED" : "V1", |
| 319 | + runtime: buildManifest.runtime, |
| 320 | + }, |
| 321 | + envVars.TRIGGER_EXISTING_DEPLOYMENT_ID |
| 322 | + ); |
322 | 323 | const isLocalBuild = !deployment.externalBuildData;
|
323 | 324 |
|
324 | 325 | // Fail fast if we know local builds will fail
|
@@ -619,7 +620,7 @@ export async function syncEnvVarsWithServer(
|
619 | 620 |
|
620 | 621 | async function failDeploy(
|
621 | 622 | client: CliApiClient,
|
622 |
| - deployment: Deployment, |
| 623 | + deployment: Pick<Deployment, "id" | "shortCode">, |
623 | 624 | error: { name: string; message: string },
|
624 | 625 | logs: string,
|
625 | 626 | $spinner: ReturnType<typeof spinner>,
|
@@ -735,6 +736,60 @@ async function failDeploy(
|
735 | 736 | }
|
736 | 737 | }
|
737 | 738 |
|
| 739 | +async function initializeOrAttachDeployment( |
| 740 | + apiClient: CliApiClient, |
| 741 | + data: InitializeDeploymentRequestBody, |
| 742 | + existingDeploymentId?: string |
| 743 | +): Promise<InitializeDeploymentResponseBody> { |
| 744 | + if (existingDeploymentId) { |
| 745 | + // In the build server we initialize the deployment before installing the project dependencies, |
| 746 | + // so that the status is correctly reflected in the dashboard. In this case, we need to attach |
| 747 | + // to the existing deployment and continue with the remote build process. |
| 748 | + // This is a workaround to avoid major changes in the deploy command and workflow. In the future, |
| 749 | + // we'll likely make the build server the entry point of the flow for building and deploying and also |
| 750 | + // adapt the related deployment API endpoints. |
| 751 | + |
| 752 | + const existingDeploymentOrError = await apiClient.getDeployment(existingDeploymentId); |
| 753 | + |
| 754 | + if (!existingDeploymentOrError.success) { |
| 755 | + throw new Error( |
| 756 | + `Failed to attach to existing deployment: ${existingDeploymentOrError.error}` |
| 757 | + ); |
| 758 | + } |
| 759 | + |
| 760 | + const { imageReference, status } = existingDeploymentOrError.data; |
| 761 | + if (!imageReference) { |
| 762 | + // this is just an artifact of our current DB schema |
| 763 | + // `imageReference` is stored as nullable, but it should always exist |
| 764 | + throw new Error("Existing deployment does not have an image reference"); |
| 765 | + } |
| 766 | + |
| 767 | + if ( |
| 768 | + status === "CANCELED" || |
| 769 | + status === "FAILED" || |
| 770 | + status === "TIMED_OUT" || |
| 771 | + status === "DEPLOYED" |
| 772 | + ) { |
| 773 | + throw new Error(`Existing deployment is in an unexpected state: ${status}`); |
| 774 | + } |
| 775 | + |
| 776 | + return { |
| 777 | + ...existingDeploymentOrError.data, |
| 778 | + imageTag: imageReference, |
| 779 | + }; |
| 780 | + } |
| 781 | + |
| 782 | + const newDeploymentOrError = await apiClient.initializeDeployment({ |
| 783 | + ...data, |
| 784 | + }); |
| 785 | + |
| 786 | + if (!newDeploymentOrError.success) { |
| 787 | + throw new Error(`Failed to start deployment: ${newDeploymentOrError.error}`); |
| 788 | + } |
| 789 | + |
| 790 | + return newDeploymentOrError.data; |
| 791 | +} |
| 792 | + |
738 | 793 | export function verifyDirectory(dir: string, projectPath: string) {
|
739 | 794 | if (dir !== "." && !isDirectory(projectPath)) {
|
740 | 795 | if (dir === "staging" || dir === "prod" || dir === "preview") {
|
|
0 commit comments