@@ -8,7 +8,20 @@ import { env } from "~/env.server";
8
8
import { createRemoteImageBuild } from "../remoteImageBuilder.server" ;
9
9
10
10
export class DeploymentService extends BaseService {
11
- public startDeployment (
11
+ /**
12
+ * Progresses a deployment from PENDING to INSTALLING and then to BUILDING.
13
+ * Also extends the deployment timeout.
14
+ *
15
+ * When progressing to BUILDING, the remote Depot build is also created.
16
+ *
17
+ * Only acts when the current status allows. Not idempotent.
18
+ *
19
+ * @param authenticatedEnv The environment which the deployment belongs to.
20
+ * @param friendlyId The friendly deployment ID.
21
+ * @param updates Optional deployment details to persist.
22
+ */
23
+
24
+ public progressDeployment (
12
25
authenticatedEnv : AuthenticatedEnvironment ,
13
26
friendlyId : string ,
14
27
updates : Partial < Pick < WorkerDeployment , "contentHash" | "runtime" > & { git : GitMeta } >
@@ -37,37 +50,26 @@ export class DeploymentService extends BaseService {
37
50
} ) ;
38
51
39
52
const validateDeployment = ( deployment : Pick < WorkerDeployment , "id" | "status" > ) => {
40
- if ( deployment . status !== "PENDING" ) {
41
- logger . warn ( "Attempted starting deployment that is not in PENDING status" , {
42
- deployment,
43
- } ) ;
44
- return errAsync ( { type : "deployment_not_pending" as const } ) ;
53
+ if ( deployment . status !== "PENDING" && deployment . status !== "INSTALLING" ) {
54
+ logger . warn (
55
+ "Attempted progressing deployment that is not in PENDING or INSTALLING status" ,
56
+ {
57
+ deployment,
58
+ }
59
+ ) ;
60
+ return errAsync ( { type : "deployment_cannot_be_progressed" as const } ) ;
45
61
}
46
62
47
63
return okAsync ( deployment ) ;
48
64
} ;
49
65
50
- const createRemoteBuild = ( deployment : Pick < WorkerDeployment , "id" > ) =>
51
- fromPromise ( createRemoteImageBuild ( authenticatedEnv . project ) , ( error ) => ( {
52
- type : "failed_to_create_remote_build" as const ,
53
- cause : error ,
54
- } ) ) . map ( ( build ) => ( {
55
- id : deployment . id ,
56
- externalBuildData : build ,
57
- } ) ) ;
58
-
59
- const updateDeployment = (
60
- deployment : Pick < WorkerDeployment , "id" > & {
61
- externalBuildData : ExternalBuildData | undefined ;
62
- }
63
- ) =>
66
+ const progressToInstalling = ( deployment : Pick < WorkerDeployment , "id" > ) =>
64
67
fromPromise (
65
68
this . _prisma . workerDeployment . updateMany ( {
66
69
where : { id : deployment . id , status : "PENDING" } , // status could've changed in the meantime, we're not locking the row
67
70
data : {
68
71
...updates ,
69
- externalBuildData : deployment . externalBuildData ,
70
- status : "BUILDING" ,
72
+ status : "INSTALLING" ,
71
73
startedAt : new Date ( ) ,
72
74
} ,
73
75
} ) ,
@@ -77,17 +79,51 @@ export class DeploymentService extends BaseService {
77
79
} )
78
80
) . andThen ( ( result ) => {
79
81
if ( result . count === 0 ) {
80
- return errAsync ( { type : "deployment_not_pending " as const } ) ;
82
+ return errAsync ( { type : "deployment_cannot_be_progressed " as const } ) ;
81
83
}
82
- return okAsync ( { id : deployment . id } ) ;
84
+ return okAsync ( { id : deployment . id , status : "INSTALLING" as const } ) ;
83
85
} ) ;
84
86
85
- const extendTimeout = ( deployment : Pick < WorkerDeployment , "id" > ) =>
87
+ const createRemoteBuild = ( deployment : Pick < WorkerDeployment , "id" > ) =>
88
+ fromPromise ( createRemoteImageBuild ( authenticatedEnv . project ) , ( error ) => ( {
89
+ type : "failed_to_create_remote_build" as const ,
90
+ cause : error ,
91
+ } ) ) ;
92
+
93
+ const progressToBuilding = ( deployment : Pick < WorkerDeployment , "id" > ) =>
94
+ createRemoteBuild ( deployment )
95
+ . andThen ( ( externalBuildData ) =>
96
+ fromPromise (
97
+ this . _prisma . workerDeployment . updateMany ( {
98
+ where : { id : deployment . id , status : "INSTALLING" } , // status could've changed in the meantime, we're not locking the row
99
+ data : {
100
+ ...updates ,
101
+ externalBuildData,
102
+ status : "BUILDING" ,
103
+ installedAt : new Date ( ) ,
104
+ } ,
105
+ } ) ,
106
+ ( error ) => ( {
107
+ type : "other" as const ,
108
+ cause : error ,
109
+ } )
110
+ )
111
+ )
112
+ . andThen ( ( result ) => {
113
+ if ( result . count === 0 ) {
114
+ return errAsync ( { type : "deployment_cannot_be_progressed" as const } ) ;
115
+ }
116
+ return okAsync ( { id : deployment . id , status : "BUILDING" as const } ) ;
117
+ } ) ;
118
+
119
+ const extendTimeout = ( deployment : Pick < WorkerDeployment , "id" | "status" > ) =>
86
120
fromPromise (
87
121
TimeoutDeploymentService . enqueue (
88
122
deployment . id ,
89
- "BUILDING" satisfies WorkerDeploymentStatus ,
90
- "Building timed out" ,
123
+ deployment . status ,
124
+ deployment . status === "INSTALLING"
125
+ ? "Installing dependencies timed out"
126
+ : "Building timed out" ,
91
127
new Date ( Date . now ( ) + env . DEPLOY_TIMEOUT_MS )
92
128
) ,
93
129
( error ) => ( {
@@ -98,8 +134,12 @@ export class DeploymentService extends BaseService {
98
134
99
135
return getDeployment ( )
100
136
. andThen ( validateDeployment )
101
- . andThen ( createRemoteBuild )
102
- . andThen ( updateDeployment )
137
+ . andThen ( ( deployment ) => {
138
+ if ( deployment . status === "PENDING" ) {
139
+ return progressToInstalling ( deployment ) ;
140
+ }
141
+ return progressToBuilding ( deployment ) ;
142
+ } )
103
143
. andThen ( extendTimeout )
104
144
. map ( ( ) => undefined ) ;
105
145
}
0 commit comments