@@ -120,6 +120,11 @@ func acquireLock(ctx context.Context, b *bundle.Bundle, svc *tmpdms.DeploymentMe
120120 if createErr != nil {
121121 return "" , "" , fmt .Errorf ("failed to create deployment: %w" , createErr )
122122 }
123+ // Write the deployment ID to workspace only after the server-side
124+ // record is created. This avoids leaving a dangling ID if creation fails.
125+ if err := writeDeploymentID (ctx , b , deploymentID ); err != nil {
126+ return "" , "" , err
127+ }
123128 versionID = "1"
124129 } else {
125130 // Existing deployment: get the last version ID to determine the next one.
@@ -157,8 +162,10 @@ func acquireLock(ctx context.Context, b *bundle.Bundle, svc *tmpdms.DeploymentMe
157162
158163// resolveDeploymentID reads the deployment ID from resources.json in the
159164// workspace state directory. If the file doesn't exist or has no deployment ID,
160- // a new UUID is generated and written. The boolean return indicates whether
161- // this is a fresh deployment (true) or an existing one (false).
165+ // a new UUID is generated. The boolean return indicates whether this is a fresh
166+ // deployment (true) or an existing one (false). For fresh deployments, the
167+ // caller is responsible for writing the deployment ID to workspace after the
168+ // server-side deployment record is created successfully.
162169func resolveDeploymentID (ctx context.Context , b * bundle.Bundle ) (string , bool , error ) {
163170 f , err := deploy .StateFiler (b )
164171 if err != nil {
@@ -184,18 +191,28 @@ func resolveDeploymentID(ctx context.Context, b *bundle.Bundle) (string, bool, e
184191 return "" , false , fmt .Errorf ("failed to read resources.json: %w" , readErr )
185192 }
186193
187- // Fresh deployment: generate a new ID and write resources.json.
188- deploymentID := uuid .New ().String ()
194+ // Fresh deployment: generate a new ID but don't write yet.
195+ return uuid .New ().String (), true , nil
196+ }
197+
198+ // writeDeploymentID writes the deployment ID to resources.json in the workspace
199+ // state directory. This should be called after the server-side deployment record
200+ // is created successfully.
201+ func writeDeploymentID (ctx context.Context , b * bundle.Bundle , deploymentID string ) error {
202+ f , err := deploy .StateFiler (b )
203+ if err != nil {
204+ return fmt .Errorf ("failed to create state filer: %w" , err )
205+ }
189206 rj := statemgmt.ResourcesJSON {DeploymentID : deploymentID }
190207 data , err := json .Marshal (rj )
191208 if err != nil {
192- return "" , false , fmt .Errorf ("failed to marshal resources.json: %w" , err )
209+ return fmt .Errorf ("failed to marshal resources.json: %w" , err )
193210 }
194211 err = f .Write (ctx , "resources.json" , bytes .NewReader (data ), filer .CreateParentDirectories , filer .OverwriteIfExists )
195212 if err != nil {
196- return "" , false , fmt .Errorf ("failed to write resources.json: %w" , err )
213+ return fmt .Errorf ("failed to write resources.json: %w" , err )
197214 }
198- return deploymentID , true , nil
215+ return nil
199216}
200217
201218// makeOperationReporter returns an OperationReporter that reports each resource
0 commit comments