@google-cloud/cloud-run-mcp 1.2.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
+ import { callWithRetry, ensureApisEnabled } from './gcp-api-helpers.js';
18
+
17
19
  // Configuration
18
20
  const REPO_NAME = 'mcp-cloud-run-deployments';
19
21
  const ZIP_FILE_NAME = 'source.zip';
@@ -46,7 +48,7 @@ let runClient;
46
48
  * @param {function(object): void} [progressCallback] - Optional callback for progress updates.
47
49
  * @param {'debug' | 'info' | 'warn' | 'error'} [severity='info'] - The severity level of the message.
48
50
  */
49
- function logAndProgress(message, progressCallback, severity = 'info') {
51
+ async function logAndProgress(message, progressCallback, severity = 'info') {
50
52
  switch (severity) {
51
53
  case 'error':
52
54
  console.error(message);
@@ -63,91 +65,6 @@ function logAndProgress(message, progressCallback, severity = 'info') {
63
65
  }
64
66
  }
65
67
 
66
- /**
67
- * Checks if a single Google Cloud API is enabled and enables it if not.
68
- *
69
- * @param {object} serviceUsageClient - The Service Usage client.
70
- * @param {string} serviceName - The full name of the service (e.g., 'projects/my-project/services/run.googleapis.com').
71
- * @param {string} api - The API identifier (e.g., 'run.googleapis.com').
72
- * @param {function(string, string=): void} progressCallback - A function to call with progress updates.
73
- * @returns {Promise<void>} A promise that resolves when the API is enabled.
74
- * @throws {Error} If the API fails to enable or if there's an issue checking its status.
75
- */
76
- async function checkAndEnableApi(
77
- serviceUsageClient,
78
- serviceName,
79
- api,
80
- progressCallback
81
- ) {
82
- const [service] = await serviceUsageClient.getService({
83
- name: serviceName,
84
- });
85
- if (service.state !== 'ENABLED') {
86
- logAndProgress(
87
- `API [${api}] is not enabled. Enabling...`,
88
- progressCallback
89
- );
90
- const [operation] = await serviceUsageClient.enableService({
91
- name: serviceName,
92
- });
93
- await operation.promise();
94
- }
95
- }
96
-
97
- /**
98
- * Ensures that the specified Google Cloud APIs are enabled for the given project.
99
- * If an API is not enabled, it attempts to enable it. Retries any failure once after 1s.
100
- * Throws an error if an API cannot be enabled.
101
- *
102
- * @async
103
- * @param {string} projectId - The Google Cloud project ID.
104
- * @param {string[]} apis - An array of API identifiers to check and enable (e.g., 'run.googleapis.com').
105
- * @param {function(string, string=): void} progressCallback - A function to call with progress updates.
106
- * The first argument is the message, the optional second argument is the type ('error', 'warning', 'info').
107
- * @throws {Error} If an API fails to enable or if there's an issue checking its status.
108
- * @returns {Promise<void>} A promise that resolves when all specified APIs are enabled.
109
- */
110
- async function ensureApisEnabled(projectId, apis, progressCallback) {
111
- const { ServiceUsageClient } = await import('@google-cloud/service-usage');
112
- const serviceUsageClient = new ServiceUsageClient({ projectId });
113
- logAndProgress('Checking and enabling required APIs...', progressCallback);
114
-
115
- for (const api of apis) {
116
- const serviceName = `projects/${projectId}/services/${api}`;
117
- try {
118
- await checkAndEnableApi(
119
- serviceUsageClient,
120
- serviceName,
121
- api,
122
- progressCallback
123
- );
124
- } catch (error) {
125
- // First attempt failed, log a warning and retry once after a delay.
126
- logAndProgress(
127
- `Failed to check/enable ${api}, retrying in 1s...`,
128
- progressCallback,
129
- 'warn'
130
- );
131
- await new Promise((resolve) => setTimeout(resolve, 1000));
132
- try {
133
- await checkAndEnableApi(
134
- serviceUsageClient,
135
- serviceName,
136
- api,
137
- progressCallback
138
- );
139
- } catch (retryError) {
140
- // If the retry also fails, throw an error.
141
- const errorMessage = `Failed to ensure API [${api}] is enabled after retry. Please check manually.`;
142
- console.error(errorMessage, retryError);
143
- logAndProgress(errorMessage, progressCallback, 'error');
144
- throw new Error(errorMessage);
145
- }
146
- }
147
- }
148
- logAndProgress('All required APIs are enabled.', progressCallback);
149
- }
150
-
151
68
  /**
152
69
  * Checks if a Cloud Run service already exists.
153
70
  *
@@ -168,15 +85,18 @@ async function checkCloudRunServiceExists(
168
85
  const parent = runClient.locationPath(projectId, location);
169
86
  const servicePath = runClient.servicePath(projectId, location, serviceId);
170
87
  try {
171
- await runClient.getService({ name: servicePath });
172
- logAndProgress(
88
+ await callWithRetry(
89
+ () => runClient.getService({ name: servicePath }),
90
+ `getService ${serviceId}`
91
+ );
92
+ await logAndProgress(
173
93
  `Cloud Run service ${serviceId} already exists.`,
174
94
  progressCallback
175
95
  );
176
96
  return true;
177
97
  } catch (error) {
178
98
  if (error.code === 5) {
179
- logAndProgress(
99
+ await logAndProgress(
180
100
  `Cloud Run service ${serviceId} does not exist.`,
181
101
  progressCallback
182
102
  );
@@ -184,7 +104,7 @@ async function checkCloudRunServiceExists(
184
104
  }
185
105
  const errorMessage = `Error checking Cloud Run service ${serviceId}: ${error.message}`;
186
106
  console.error(`Error checking Cloud Run service ${serviceId}:`, error);
187
- logAndProgress(errorMessage, progressCallback, 'error');
107
+ await logAndProgress(errorMessage, progressCallback, 'error');
188
108
  throw error;
189
109
  }
190
110
  }
@@ -240,7 +160,7 @@ async function deployToCloudRun(
240
160
 
241
161
  // Always perform a dry run first for general validation
242
162
  try {
243
- logAndProgress(
163
+ await logAndProgress(
244
164
  `Performing dry run for service ${serviceId}...`,
245
165
  progressCallback,
246
166
  'debug'
@@ -249,25 +169,33 @@ async function deployToCloudRun(
249
169
 
250
170
  if (exists) {
251
171
  dryRunServiceConfig.name = servicePath;
252
- await runClient.updateService({
253
- service: dryRunServiceConfig,
254
- validateOnly: true,
255
- });
172
+ await callWithRetry(
173
+ () =>
174
+ runClient.updateService({
175
+ service: dryRunServiceConfig,
176
+ validateOnly: true,
177
+ }),
178
+ `updateService (dry run) ${serviceId}`
179
+ );
256
180
  } else {
257
- await runClient.createService({
258
- parent: parent,
259
- service: dryRunServiceConfig,
260
- serviceId: serviceId,
261
- validateOnly: true,
262
- });
181
+ await callWithRetry(
182
+ () =>
183
+ runClient.createService({
184
+ parent: parent,
185
+ service: dryRunServiceConfig,
186
+ serviceId: serviceId,
187
+ validateOnly: true,
188
+ }),
189
+ `createService (dry run) ${serviceId}`
190
+ );
263
191
  }
264
- logAndProgress(
192
+ await logAndProgress(
265
193
  `Dry run successful for ${serviceId} with current configuration.`,
266
194
  progressCallback,
267
195
  'debug'
268
196
  );
269
197
  } catch (dryRunError) {
270
- logAndProgress(
198
+ await logAndProgress(
271
199
  `Dry run for ${serviceId} failed: ${dryRunError.message}`,
272
200
  progressCallback,
273
201
  'warn'
@@ -281,7 +209,7 @@ async function deployToCloudRun(
281
209
  dryRunError.message.toLowerCase().includes('iam policy violation') ||
282
210
  dryRunError.code === 3) /* INVALID_ARGUMENT */
283
211
  ) {
284
- logAndProgress(
212
+ await logAndProgress(
285
213
  `Dry run suggests 'invokerIamDisabled' is not allowed or invalid. Attempting deployment without it.`,
286
214
  progressCallback,
287
215
  'warn'
@@ -290,32 +218,42 @@ async function deployToCloudRun(
290
218
  } else {
291
219
  // For any other validation errors, rethrow to stop the deployment
292
220
  const errorMessage = `Dry run validation failed for service ${serviceId}: ${dryRunError.message}`;
293
- logAndProgress(errorMessage, progressCallback, 'error');
221
+ await logAndProgress(errorMessage, progressCallback, 'error');
294
222
  throw new Error(errorMessage);
295
223
  }
296
224
  }
297
225
 
298
226
  let operation;
299
227
  if (exists) {
300
- logAndProgress(
228
+ await logAndProgress(
301
229
  `Updating existing service ${serviceId}...`,
302
230
  progressCallback
303
231
  );
304
232
  service.name = servicePath;
305
- [operation] = await runClient.updateService({ service });
233
+ [operation] = await callWithRetry(
234
+ () => runClient.updateService({ service }),
235
+ `updateService ${serviceId}`
236
+ );
306
237
  } else {
307
- logAndProgress(`Creating new service ${serviceId}...`, progressCallback);
308
- [operation] = await runClient.createService({
309
- parent: parent,
310
- service: service, // 'service' object might have invokerIamDisabled removed
311
- serviceId: serviceId,
312
- });
238
+ await logAndProgress(`Creating new service ${serviceId}...`, progressCallback);
239
+ [operation] = await callWithRetry(
240
+ () =>
241
+ runClient.createService({
242
+ parent: parent,
243
+ service: service, // 'service' object might have invokerIamDisabled removed
244
+ serviceId: serviceId,
245
+ }),
246
+ `createService ${serviceId}`
247
+ );
313
248
  }
314
249
 
315
- logAndProgress(`Deploying ${serviceId} to Cloud Run...`, progressCallback);
250
+ await logAndProgress(
251
+ `Deploying ${serviceId} to Cloud Run...`,
252
+ progressCallback
253
+ );
316
254
  const [response] = await operation.promise();
317
255
 
318
- logAndProgress(
256
+ await logAndProgress(
319
257
  `Service deployed/updated successfully: ${response.uri}`,
320
258
  progressCallback
321
259
  );
@@ -323,7 +261,7 @@ async function deployToCloudRun(
323
261
  } catch (error) {
324
262
  const errorMessage = `Error deploying/updating service ${serviceId}: ${error.message}`;
325
263
  console.error(`Error deploying/updating service ${serviceId}:`, error);
326
- logAndProgress(errorMessage, progressCallback, 'error');
264
+ await logAndProgress(errorMessage, progressCallback, 'error');
327
265
  throw error;
328
266
  }
329
267
  }
@@ -346,20 +284,27 @@ async function ensureStorageBucketExists(
346
284
  ) {
347
285
  const bucket = storage.bucket(bucketName);
348
286
  try {
349
- const [exists] = await bucket.exists();
287
+ const [exists] = await callWithRetry(
288
+ () => bucket.exists(),
289
+ `storage.bucket.exists ${bucketName}`
290
+ );
350
291
  if (exists) {
351
- logAndProgress(`Bucket ${bucketName} already exists.`, progressCallback);
292
+ await logAndProgress(
293
+ `Bucket ${bucketName} already exists.`,
294
+ progressCallback
295
+ );
352
296
  return bucket;
353
297
  } else {
354
- logAndProgress(
298
+ await logAndProgress(
355
299
  `Bucket ${bucketName} does not exist. Creating in location ${location}...`,
356
300
  progressCallback
357
301
  );
358
302
  try {
359
- const [createdBucket] = await storage.createBucket(bucketName, {
360
- location: location,
361
- });
362
- logAndProgress(
303
+ const [createdBucket] = await callWithRetry(
304
+ () => storage.createBucket(bucketName, { location: location }),
305
+ `storage.createBucket ${bucketName}`
306
+ );
307
+ await logAndProgress(
363
308
  `Storage bucket ${createdBucket.name} created successfully in ${location}.`,
364
309
  progressCallback
365
310
  );
@@ -370,14 +315,14 @@ async function ensureStorageBucketExists(
370
315
  `Failed to create storage bucket ${bucketName}. Error details:`,
371
316
  createError
372
317
  );
373
- logAndProgress(errorMessage, progressCallback, 'error');
318
+ await logAndProgress(errorMessage, progressCallback, 'error');
374
319
  throw createError;
375
320
  }
376
321
  }
377
322
  } catch (error) {
378
323
  const errorMessage = `Error checking/creating bucket ${bucketName}: ${error.message}`;
379
324
  console.error(`Error checking/creating bucket ${bucketName}:`, error);
380
- logAndProgress(errorMessage, progressCallback, 'error');
325
+ await logAndProgress(errorMessage, progressCallback, 'error');
381
326
  throw error;
382
327
  }
383
328
  }
@@ -478,12 +423,15 @@ async function uploadToStorageBucket(
478
423
  progressCallback
479
424
  ) {
480
425
  try {
481
- logAndProgress(
426
+ await logAndProgress(
482
427
  `Uploading buffer to gs://${bucket.name}/${destinationBlobName}...`,
483
428
  progressCallback
484
429
  );
485
- await bucket.file(destinationBlobName).save(buffer);
486
- logAndProgress(
430
+ await callWithRetry(
431
+ () => bucket.file(destinationBlobName).save(buffer),
432
+ `storage.bucket.file.save ${destinationBlobName}`
433
+ );
434
+ await logAndProgress(
487
435
  `File ${destinationBlobName} uploaded successfully to gs://${bucket.name}/${destinationBlobName}.`,
488
436
  progressCallback
489
437
  );
@@ -491,7 +439,7 @@ async function uploadToStorageBucket(
491
439
  } catch (error) {
492
440
  const errorMessage = `Error uploading buffer: ${error.message}`;
493
441
  console.error(`Error uploading buffer:`, error);
494
- logAndProgress(errorMessage, progressCallback, 'error');
442
+ await logAndProgress(errorMessage, progressCallback, 'error');
495
443
  throw error;
496
444
  }
497
445
  }
@@ -524,70 +472,51 @@ async function ensureArtifactRegistryRepoExists(
524
472
  );
525
473
 
526
474
  try {
527
- const [repository] = await artifactRegistryClient.getRepository({
528
- name: repoPath,
529
- });
530
- logAndProgress(
475
+ const [repository] = await callWithRetry(
476
+ () => artifactRegistryClient.getRepository({ name: repoPath }),
477
+ `artifactRegistry.getRepository ${repositoryId}`
478
+ );
479
+ await logAndProgress(
531
480
  `Repository ${repositoryId} already exists in ${location}.`,
532
481
  progressCallback
533
482
  );
534
483
  return repository;
535
484
  } catch (error) {
536
485
  if (error.code === 5) {
537
- logAndProgress(
486
+ await logAndProgress(
538
487
  `Repository ${repositoryId} does not exist in ${location}. Creating...`,
539
488
  progressCallback
540
489
  );
541
490
  const repositoryToCreate = {
542
491
  format: format,
543
492
  };
544
- const maxRetries = 3;
545
- const retryDelay = 10000; // 10s
546
493
 
547
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
548
- try {
549
- const [operation] = await artifactRegistryClient.createRepository({
494
+ const [operation] = await callWithRetry(
495
+ () =>
496
+ artifactRegistryClient.createRepository({
550
497
  parent: parent,
551
498
  repository: repositoryToCreate,
552
499
  repositoryId: repositoryId,
553
- });
554
- logAndProgress(
555
- `Creating Artifact Registry repository ${repositoryId}...`,
556
- progressCallback
557
- );
558
- const [result] = await operation.promise();
559
- logAndProgress(
560
- `Artifact Registry repository ${result.name} created successfully.`,
561
- progressCallback
562
- );
563
- return result;
564
- } catch (createError) {
565
- // PERMISSION_DENIED is code 7 for gRPC. Retry on this error.
566
- if (createError.code === 7 && attempt < maxRetries) {
567
- logAndProgress(
568
- `Attempt ${attempt} to create repository failed. This can happen on new projects. Retrying in 10s...`,
569
- progressCallback,
570
- 'warn'
571
- );
572
- await new Promise((resolve) => setTimeout(resolve, retryDelay));
573
- } else {
574
- const errorMessage = `Failed to create Artifact Registry repository ${repositoryId}. Error details: ${createError.message}`;
575
- console.error(
576
- `Failed to create Artifact Registry repository ${repositoryId}. Error details:`,
577
- createError
578
- );
579
- logAndProgress(errorMessage, progressCallback, 'error');
580
- throw createError;
581
- }
582
- }
583
- }
500
+ }),
501
+ `artifactRegistry.createRepository ${repositoryId}`
502
+ );
503
+ await logAndProgress(
504
+ `Creating Artifact Registry repository ${repositoryId}...`,
505
+ progressCallback
506
+ );
507
+ const [result] = await operation.promise();
508
+ await logAndProgress(
509
+ `Artifact Registry repository ${result.name} created successfully.`,
510
+ progressCallback
511
+ );
512
+ return result;
584
513
  } else {
585
514
  const errorMessage = `Error checking/creating repository ${repositoryId}: ${error.message}`;
586
515
  console.error(
587
516
  `Error checking/creating repository ${repositoryId}:`,
588
517
  error
589
518
  );
590
- logAndProgress(errorMessage, progressCallback, 'error');
519
+ await logAndProgress(errorMessage, progressCallback, 'error');
591
520
  throw error;
592
521
  }
593
522
  }
@@ -657,23 +586,23 @@ async function triggerCloudBuild(
657
586
  images: [targetImageUrl],
658
587
  };
659
588
 
660
- logAndProgress(
589
+ await logAndProgress(
661
590
  `Initiating Cloud Build for gs://${sourceBucketName}/${sourceBlobName} in ${location}...`,
662
591
  progressCallback
663
592
  );
664
- const [operation] = await cloudBuildClient.createBuild({
665
- projectId: projectId,
666
- build: build,
667
- });
593
+ const [operation] = await callWithRetry(
594
+ () => cloudBuildClient.createBuild({ projectId: projectId, build: build }),
595
+ 'cloudBuild.createBuild'
596
+ );
668
597
 
669
- logAndProgress(`Cloud Build job started...`, progressCallback);
598
+ await logAndProgress(`Cloud Build job started...`, progressCallback);
670
599
  const buildId = operation.metadata.build.id;
671
600
  let completedBuild;
672
601
  while (true) {
673
- const [getBuildOperation] = await cloudBuildClient.getBuild({
674
- projectId: projectId,
675
- id: buildId,
676
- });
602
+ const [getBuildOperation] = await callWithRetry(
603
+ () => cloudBuildClient.getBuild({ projectId: projectId, id: buildId }),
604
+ `cloudBuild.getBuild ${buildId}`
605
+ );
677
606
  if (
678
607
  ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT', 'CANCELLED'].includes(
679
608
  getBuildOperation.status
@@ -682,7 +611,7 @@ async function triggerCloudBuild(
682
611
  completedBuild = getBuildOperation;
683
612
  break;
684
613
  }
685
- logAndProgress(
614
+ await logAndProgress(
686
615
  `Build status: ${getBuildOperation.status}. Waiting...`,
687
616
  progressCallback,
688
617
  'debug'
@@ -691,20 +620,20 @@ async function triggerCloudBuild(
691
620
  }
692
621
 
693
622
  if (completedBuild.status === 'SUCCESS') {
694
- logAndProgress(
623
+ await logAndProgress(
695
624
  `Cloud Build job ${buildId} completed successfully.`,
696
625
  progressCallback
697
626
  );
698
- logAndProgress(
627
+ await logAndProgress(
699
628
  `Image built: ${completedBuild.results.images[0].name}`,
700
629
  progressCallback
701
630
  );
702
631
  return completedBuild;
703
632
  } else {
704
633
  const failureMessage = `Cloud Build job ${buildId} failed with status: ${completedBuild.status}`;
705
- logAndProgress(failureMessage, progressCallback, 'error');
634
+ await logAndProgress(failureMessage, progressCallback, 'error');
706
635
  const logsMessage = `Build logs: ${completedBuild.logUrl}`;
707
- logAndProgress(logsMessage, progressCallback); // Log URL is info, failure is error
636
+ await logAndProgress(logsMessage, progressCallback); // Log URL is info, failure is error
708
637
 
709
638
  let buildLogsSnippet = `\n\nRefer to Log URL for full details: ${completedBuild.logUrl}`; // Default snippet
710
639
  try {
@@ -712,7 +641,7 @@ async function triggerCloudBuild(
712
641
  const loggingClient = new Logging({ projectId });
713
642
 
714
643
  const logFilter = `resource.type="build" AND resource.labels.build_id="${buildId}"`;
715
- logAndProgress(
644
+ await logAndProgress(
716
645
  `Attempting to fetch last ${BUILD_LOGS_LINES_TO_FETCH} log lines for build ${buildId}...`,
717
646
  progressCallback,
718
647
  'debug'
@@ -724,25 +653,29 @@ async function triggerCloudBuild(
724
653
  );
725
654
 
726
655
  // Fetch the most recent N log entries
727
- const [entries] = await loggingClient.getEntries({
728
- filter: logFilter,
729
- orderBy: 'timestamp desc', // Get latest logs first
730
- pageSize: BUILD_LOGS_LINES_TO_FETCH,
731
- });
656
+ const [entries] = await callWithRetry(
657
+ () =>
658
+ loggingClient.getEntries({
659
+ filter: logFilter,
660
+ orderBy: 'timestamp desc', // Get latest logs first
661
+ pageSize: BUILD_LOGS_LINES_TO_FETCH,
662
+ }),
663
+ `logging.getEntries for build ${buildId}`
664
+ );
732
665
 
733
666
  if (entries && entries.length > 0) {
734
667
  // Entries are newest first, reverse for chronological order of the snippet
735
668
  const logLines = entries.reverse().map((entry) => entry.data || '');
736
669
  if (logLines.length > 0) {
737
670
  buildLogsSnippet = `\n\nLast ${logLines.length} log lines from build ${buildId}:\n${logLines.join('\n')}`;
738
- logAndProgress(
671
+ await logAndProgress(
739
672
  `Successfully fetched snippet of build logs for ${buildId}.`,
740
673
  progressCallback,
741
674
  'info'
742
675
  );
743
676
  }
744
677
  } else {
745
- logAndProgress(
678
+ await logAndProgress(
746
679
  `No specific log entries retrieved for build ${buildId}. ${buildLogsSnippet}`,
747
680
  progressCallback,
748
681
  'warn'
@@ -750,7 +683,7 @@ async function triggerCloudBuild(
750
683
  }
751
684
  } catch (logError) {
752
685
  console.error(`Error fetching build logs for ${buildId}:`, logError);
753
- logAndProgress(
686
+ await logAndProgress(
754
687
  `Failed to fetch build logs snippet: ${logError.message}. ${buildLogsSnippet}`,
755
688
  progressCallback,
756
689
  'warn'
@@ -783,14 +716,21 @@ export async function deploy({
783
716
  if (!projectId) {
784
717
  const errorMsg =
785
718
  'Error: projectId is required in the configuration object.';
786
- logAndProgress(errorMsg, progressCallback, 'error');
787
- process.exit(1);
719
+ await logAndProgress(errorMsg, progressCallback, 'error');
720
+ throw new Error(errorMsg);
721
+ }
722
+
723
+ if (!serviceName) {
724
+ const errorMsg =
725
+ 'Error: serviceName is required in the configuration object.';
726
+ await logAndProgress(errorMsg, progressCallback, 'error');
727
+ throw new Error(errorMsg);
788
728
  }
789
729
 
790
730
  if (!files || !Array.isArray(files) || files.length === 0) {
791
731
  const errorMsg =
792
732
  'Error: files array is required in the configuration object.';
793
- logAndProgress(errorMsg, progressCallback, 'error');
733
+ await logAndProgress(errorMsg, progressCallback, 'error');
794
734
  if (typeof process !== 'undefined' && process.exit) {
795
735
  process.exit(1);
796
736
  } else {
@@ -823,10 +763,10 @@ export async function deploy({
823
763
  const bucketName = `${projectId}-source-bucket`;
824
764
  const imageUrl = `${region}-docker.pkg.dev/${projectId}/${REPO_NAME}/${serviceName}:${IMAGE_TAG}`;
825
765
 
826
- logAndProgress(`Project: ${projectId}`, progressCallback);
827
- logAndProgress(`Region: ${region}`, progressCallback);
828
- logAndProgress(`Service Name: ${serviceName}`, progressCallback);
829
- logAndProgress(`Files to deploy: ${files.length}`, progressCallback);
766
+ await logAndProgress(`Project: ${projectId}`, progressCallback);
767
+ await logAndProgress(`Region: ${region}`, progressCallback);
768
+ await logAndProgress(`Service Name: ${serviceName}`, progressCallback);
769
+ await logAndProgress(`Files to deploy: ${files.length}`, progressCallback);
830
770
 
831
771
  let hasDockerfile = false;
832
772
  if (
@@ -859,7 +799,7 @@ export async function deploy({
859
799
  }
860
800
  }
861
801
  }
862
- logAndProgress(`Dockerfile: ${hasDockerfile}`, progressCallback);
802
+ await logAndProgress(`Dockerfile: ${hasDockerfile}`, progressCallback);
863
803
 
864
804
  const bucket = await ensureStorageBucketExists(
865
805
  bucketName,
@@ -874,7 +814,7 @@ export async function deploy({
874
814
  ZIP_FILE_NAME,
875
815
  progressCallback
876
816
  );
877
- logAndProgress('Source code uploaded successfully', progressCallback);
817
+ await logAndProgress('Source code uploaded successfully', progressCallback);
878
818
 
879
819
  await ensureArtifactRegistryRepoExists(
880
820
  projectId,
@@ -906,12 +846,12 @@ export async function deploy({
906
846
  skipIamCheck
907
847
  );
908
848
 
909
- logAndProgress(`Deployment Completed Successfully`, progressCallback);
849
+ await logAndProgress(`Deployment Completed Successfully`, progressCallback);
910
850
  return service;
911
851
  } catch (error) {
912
852
  const deployFailedMessage = `Deployment Failed: ${error.message}`;
913
853
  console.error(`Deployment Failed`, error);
914
- logAndProgress(deployFailedMessage, progressCallback, 'error');
854
+ await logAndProgress(deployFailedMessage, progressCallback, 'error');
915
855
  throw error;
916
856
  }
917
857
  }
@@ -939,13 +879,20 @@ export async function deployImage({
939
879
  if (!projectId) {
940
880
  const errorMsg =
941
881
  'Error: projectId is required in the configuration object.';
942
- logAndProgress(errorMsg, progressCallback, 'error');
943
- process.exit(1);
882
+ await logAndProgress(errorMsg, progressCallback, 'error');
883
+ throw new Error(errorMsg);
884
+ }
885
+
886
+ if (!serviceName) {
887
+ const errorMsg =
888
+ 'Error: serviceName is required in the configuration object.';
889
+ await logAndProgress(errorMsg, progressCallback, 'error');
890
+ throw new Error(errorMsg);
944
891
  }
945
892
 
946
893
  if (!imageUrl) {
947
894
  const errorMsg = 'Error: imageUrl is required in the configuration object.';
948
- logAndProgress(errorMsg, progressCallback, 'error');
895
+ await logAndProgress(errorMsg, progressCallback, 'error');
949
896
  if (typeof process !== 'undefined' && process.exit) {
950
897
  process.exit(1);
951
898
  } else {
@@ -965,10 +912,10 @@ export async function deployImage({
965
912
 
966
913
  runClient = new ServicesClient({ projectId });
967
914
 
968
- logAndProgress(`Project: ${projectId}`, progressCallback);
969
- logAndProgress(`Region: ${region}`, progressCallback);
970
- logAndProgress(`Service Name: ${serviceName}`, progressCallback);
971
- logAndProgress(`Image URL: ${imageUrl}`, progressCallback);
915
+ await logAndProgress(`Project: ${projectId}`, progressCallback);
916
+ await logAndProgress(`Region: ${region}`, progressCallback);
917
+ await logAndProgress(`Service Name: ${serviceName}`, progressCallback);
918
+ await logAndProgress(`Image URL: ${imageUrl}`, progressCallback);
972
919
 
973
920
  const service = await deployToCloudRun(
974
921
  projectId,
@@ -979,12 +926,12 @@ export async function deployImage({
979
926
  skipIamCheck
980
927
  );
981
928
 
982
- logAndProgress(`Deployment Completed Successfully`, progressCallback);
929
+ await logAndProgress(`Deployment Completed Successfully`, progressCallback);
983
930
  return service;
984
931
  } catch (error) {
985
932
  const deployFailedMessage = `Deployment Failed: ${error.message}`;
986
933
  console.error(`Deployment Failed`, error);
987
- logAndProgress(deployFailedMessage, progressCallback, 'error');
934
+ await logAndProgress(deployFailedMessage, progressCallback, 'error');
988
935
  throw error;
989
936
  }
990
937
  }