@@ -8,7 +8,20 @@ import { env } from "~/env.server";
88import { createRemoteImageBuild } from "../remoteImageBuilder.server" ;
99
1010export 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 (
1225 authenticatedEnv : AuthenticatedEnvironment ,
1326 friendlyId : string ,
1427 updates : Partial < Pick < WorkerDeployment , "contentHash" | "runtime" > & { git : GitMeta } >
@@ -37,37 +50,26 @@ export class DeploymentService extends BaseService {
3750 } ) ;
3851
3952 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 } ) ;
4561 }
4662
4763 return okAsync ( deployment ) ;
4864 } ;
4965
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" > ) =>
6467 fromPromise (
6568 this . _prisma . workerDeployment . updateMany ( {
6669 where : { id : deployment . id , status : "PENDING" } , // status could've changed in the meantime, we're not locking the row
6770 data : {
6871 ...updates ,
69- externalBuildData : deployment . externalBuildData ,
70- status : "BUILDING" ,
72+ status : "INSTALLING" ,
7173 startedAt : new Date ( ) ,
7274 } ,
7375 } ) ,
@@ -77,17 +79,51 @@ export class DeploymentService extends BaseService {
7779 } )
7880 ) . andThen ( ( result ) => {
7981 if ( result . count === 0 ) {
80- return errAsync ( { type : "deployment_not_pending " as const } ) ;
82+ return errAsync ( { type : "deployment_cannot_be_progressed " as const } ) ;
8183 }
82- return okAsync ( { id : deployment . id } ) ;
84+ return okAsync ( { id : deployment . id , status : "INSTALLING" as const } ) ;
8385 } ) ;
8486
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" > ) =>
86120 fromPromise (
87121 TimeoutDeploymentService . enqueue (
88122 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" ,
91127 new Date ( Date . now ( ) + env . DEPLOY_TIMEOUT_MS )
92128 ) ,
93129 ( error ) => ( {
@@ -98,8 +134,12 @@ export class DeploymentService extends BaseService {
98134
99135 return getDeployment ( )
100136 . 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+ } )
103143 . andThen ( extendTimeout )
104144 . map ( ( ) => undefined ) ;
105145 }
0 commit comments