@google-cloud/cloud-run-mcp 1.6.0 → 1.8.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.
- package/.github/workflows/npm-publish.yml +1 -1
- package/CONTRIBUTING.md +1 -1
- package/lib/clients.js +222 -0
- package/lib/cloud-api/billing.js +90 -4
- package/lib/cloud-api/build.js +96 -49
- package/lib/cloud-api/helpers.js +60 -38
- package/lib/cloud-api/projects.js +4 -6
- package/lib/cloud-api/registry.js +5 -5
- package/lib/cloud-api/run.js +14 -33
- package/lib/cloud-api/storage.js +6 -7
- package/lib/deployment/constants.js +54 -0
- package/lib/deployment/deployer.js +251 -149
- package/lib/deployment/helpers.js +194 -0
- package/lib/deployment/source-processor.js +124 -0
- package/lib/util/archive.js +15 -6
- package/package.json +3 -3
- package/test/local/clients.test.js +146 -0
- package/test/local/cloud-api/build.test.js +293 -309
- package/test/local/deployment-helpers.test.js +231 -0
- package/test/local/mcp-server-stdio.test.js +59 -0
- package/test/local/mcp-server-streamable-http.test.js +25 -21
- package/test/local/mock-clients.js +35 -0
- package/test/local/source-processor.test.js +290 -0
- package/test/local/test-utils.js +51 -0
- package/test/need-gcp/test-helpers.js +1 -1
- package/test/need-gcp/workflows/deployment-workflows.test.js +3 -3
|
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { getRunClient } from '../clients.js';
|
|
17
18
|
import { callWithRetry, ensureApisEnabled } from '../cloud-api/helpers.js';
|
|
18
19
|
import { zipFiles } from '../util/archive.js';
|
|
19
20
|
import {
|
|
@@ -24,27 +25,23 @@ import { ensureArtifactRegistryRepoExists } from '../cloud-api/registry.js';
|
|
|
24
25
|
import { triggerCloudBuild } from '../cloud-api/build.js';
|
|
25
26
|
import { logAndProgress } from '../util/helpers.js';
|
|
26
27
|
import { checkCloudRunServiceExists } from '../cloud-api/run.js';
|
|
28
|
+
import {
|
|
29
|
+
canDeployWithoutBuild,
|
|
30
|
+
createDirectSourceDeploymentContainer,
|
|
31
|
+
makeFileDeploymentMetadata,
|
|
32
|
+
} from './helpers.js';
|
|
33
|
+
import {
|
|
34
|
+
prepareSourceDirectory,
|
|
35
|
+
installDependencies,
|
|
36
|
+
cleanupTempDirectory,
|
|
37
|
+
} from './source-processor.js';
|
|
27
38
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const REQUIRED_APIS_FOR_SOURCE_DEPLOY = [
|
|
35
|
-
'iam.googleapis.com',
|
|
36
|
-
'storage.googleapis.com',
|
|
37
|
-
'cloudbuild.googleapis.com',
|
|
38
|
-
'artifactregistry.googleapis.com',
|
|
39
|
-
'run.googleapis.com',
|
|
40
|
-
'serviceusage.googleapis.com',
|
|
41
|
-
];
|
|
42
|
-
|
|
43
|
-
// APIs required for deploying a container image.
|
|
44
|
-
const REQUIRED_APIS_FOR_IMAGE_DEPLOY = [
|
|
45
|
-
'run.googleapis.com',
|
|
46
|
-
'serviceusage.googleapis.com',
|
|
47
|
-
];
|
|
39
|
+
import {
|
|
40
|
+
DEPLOYMENT_CONFIG,
|
|
41
|
+
DEPLOYMENT_TYPES,
|
|
42
|
+
MAX_ALLOWED_DIRECT_SOURCE_SIZE_BYTES,
|
|
43
|
+
REQUIRED_APIS,
|
|
44
|
+
} from './constants.js';
|
|
48
45
|
|
|
49
46
|
/**
|
|
50
47
|
* Deploys or updates a Cloud Run service with the specified container image.
|
|
@@ -62,29 +59,35 @@ const REQUIRED_APIS_FOR_IMAGE_DEPLOY = [
|
|
|
62
59
|
* @throws {Error} If the deployment or update process fails.
|
|
63
60
|
*/
|
|
64
61
|
async function deployToCloudRun(
|
|
65
|
-
context,
|
|
66
62
|
projectId,
|
|
67
63
|
location,
|
|
68
64
|
serviceId,
|
|
69
65
|
imgUrl,
|
|
70
66
|
progressCallback,
|
|
71
|
-
skipIamCheck
|
|
67
|
+
skipIamCheck,
|
|
68
|
+
deploymentType,
|
|
69
|
+
zippedSourceContainer,
|
|
70
|
+
runtime
|
|
72
71
|
) {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
location,
|
|
77
|
-
serviceId
|
|
78
|
-
);
|
|
72
|
+
const runClient = await getRunClient(projectId);
|
|
73
|
+
const parent = runClient.locationPath(projectId, location);
|
|
74
|
+
const servicePath = runClient.servicePath(projectId, location, serviceId);
|
|
79
75
|
const revisionName = `${serviceId}-${Date.now()}`; // Generate a unique revision name
|
|
80
76
|
|
|
77
|
+
const containers =
|
|
78
|
+
deploymentType === DEPLOYMENT_TYPES.NO_BUILD
|
|
79
|
+
? [zippedSourceContainer]
|
|
80
|
+
: [{ image: imgUrl }];
|
|
81
|
+
|
|
81
82
|
const service = {
|
|
82
83
|
template: {
|
|
83
84
|
revision: revisionName,
|
|
84
|
-
containers
|
|
85
|
+
containers,
|
|
85
86
|
},
|
|
86
87
|
labels: {
|
|
87
|
-
'created-by':
|
|
88
|
+
'created-by': DEPLOYMENT_CONFIG.LABEL_CREATED_BY,
|
|
89
|
+
'deployment-type': deploymentType,
|
|
90
|
+
runtime: runtime,
|
|
88
91
|
},
|
|
89
92
|
};
|
|
90
93
|
|
|
@@ -95,7 +98,6 @@ async function deployToCloudRun(
|
|
|
95
98
|
|
|
96
99
|
try {
|
|
97
100
|
const exists = await checkCloudRunServiceExists(
|
|
98
|
-
context,
|
|
99
101
|
projectId,
|
|
100
102
|
location,
|
|
101
103
|
serviceId,
|
|
@@ -115,7 +117,7 @@ async function deployToCloudRun(
|
|
|
115
117
|
dryRunServiceConfig.name = servicePath;
|
|
116
118
|
await callWithRetry(
|
|
117
119
|
() =>
|
|
118
|
-
|
|
120
|
+
runClient.updateService({
|
|
119
121
|
service: dryRunServiceConfig,
|
|
120
122
|
validateOnly: true,
|
|
121
123
|
}),
|
|
@@ -124,7 +126,7 @@ async function deployToCloudRun(
|
|
|
124
126
|
} else {
|
|
125
127
|
await callWithRetry(
|
|
126
128
|
() =>
|
|
127
|
-
|
|
129
|
+
runClient.createService({
|
|
128
130
|
parent: parent,
|
|
129
131
|
service: dryRunServiceConfig,
|
|
130
132
|
serviceId: serviceId,
|
|
@@ -175,7 +177,7 @@ async function deployToCloudRun(
|
|
|
175
177
|
);
|
|
176
178
|
service.name = servicePath;
|
|
177
179
|
[operation] = await callWithRetry(
|
|
178
|
-
() =>
|
|
180
|
+
() => runClient.updateService({ service }),
|
|
179
181
|
`updateService ${serviceId}`
|
|
180
182
|
);
|
|
181
183
|
} else {
|
|
@@ -185,7 +187,7 @@ async function deployToCloudRun(
|
|
|
185
187
|
);
|
|
186
188
|
[operation] = await callWithRetry(
|
|
187
189
|
() =>
|
|
188
|
-
|
|
190
|
+
runClient.createService({
|
|
189
191
|
parent: parent,
|
|
190
192
|
service: service, // 'service' object might have invokerIamDisabled removed
|
|
191
193
|
serviceId: serviceId,
|
|
@@ -213,6 +215,187 @@ async function deployToCloudRun(
|
|
|
213
215
|
}
|
|
214
216
|
}
|
|
215
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Handles deployment directly from source via a zipped archive (no Cloud Build).
|
|
220
|
+
* This function prepares the source code, installs dependencies, zips the code,
|
|
221
|
+
* uploads it to Google Cloud Storage, and then deploys it to Cloud Run
|
|
222
|
+
* using the direct source deployment mechanism.
|
|
223
|
+
*
|
|
224
|
+
* @param {object} context - The context object containing various GCP clients.
|
|
225
|
+
* @param {string} projectId - The Google Cloud project ID.
|
|
226
|
+
* @param {string} region - The Google Cloud region for the deployment.
|
|
227
|
+
* @param {string} serviceName - The name of the Cloud Run service.
|
|
228
|
+
* @param {Array<string|{filename: string, content: Buffer|string}>} files - Files to deploy.
|
|
229
|
+
* @param {string} bucketName - The name of the GCS bucket to use for staging.
|
|
230
|
+
* @param {object} deploymentAttrs - Deployment attributes detected from the source.
|
|
231
|
+
* @param {string} [deploymentAttrs.runtime] - The detected runtime (e.g., 'nodejs').
|
|
232
|
+
* @param {string[]} [deploymentAttrs.cmd] - The command to run the application.
|
|
233
|
+
* @param {string[]} [deploymentAttrs.args] - The arguments for the command.
|
|
234
|
+
* @param {string} [deploymentAttrs.baseImage] - The base image to use for the deployment.
|
|
235
|
+
* @param {function(object): void} [progressCallback] - Optional callback for progress updates.
|
|
236
|
+
* @param {boolean} [skipIamCheck=false] - Whether to skip the IAM check during Cloud Run service creation/update.
|
|
237
|
+
* @returns {Promise<object>} A promise that resolves with the deployed Cloud Run service object.
|
|
238
|
+
* @throws {Error} If any part of the deployment process fails.
|
|
239
|
+
*/
|
|
240
|
+
async function deployWithZip(
|
|
241
|
+
projectId,
|
|
242
|
+
region,
|
|
243
|
+
serviceName,
|
|
244
|
+
files,
|
|
245
|
+
bucketName,
|
|
246
|
+
deploymentAttrs,
|
|
247
|
+
progressCallback,
|
|
248
|
+
skipIamCheck
|
|
249
|
+
) {
|
|
250
|
+
await logAndProgress(
|
|
251
|
+
`Attempting direct source deployment...`,
|
|
252
|
+
progressCallback
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
let tempDir;
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
tempDir = await prepareSourceDirectory(files);
|
|
259
|
+
await installDependencies(tempDir, deploymentAttrs);
|
|
260
|
+
|
|
261
|
+
const archiveBuffer = await zipFiles([tempDir], true, progressCallback);
|
|
262
|
+
const archiveName = DEPLOYMENT_CONFIG.TARGZ_FILE_NAME;
|
|
263
|
+
|
|
264
|
+
if (archiveBuffer.length > MAX_ALLOWED_DIRECT_SOURCE_SIZE_BYTES) {
|
|
265
|
+
const errorMsg = `Warning: Source archive size (${(
|
|
266
|
+
archiveBuffer.length /
|
|
267
|
+
(1024 * 1024)
|
|
268
|
+
).toFixed(
|
|
269
|
+
2
|
|
270
|
+
)} MiB) exceeds the ${MAX_ALLOWED_DIRECT_SOURCE_SIZE_BYTES / (1024 * 1024)} MiB limit.`;
|
|
271
|
+
await logAndProgress(errorMsg, progressCallback, 'warn');
|
|
272
|
+
throw new Error('Archive size exceeds limit');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const bucket = await ensureStorageBucketExists(
|
|
276
|
+
projectId,
|
|
277
|
+
bucketName,
|
|
278
|
+
region,
|
|
279
|
+
progressCallback
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await uploadToStorageBucket(
|
|
283
|
+
bucket,
|
|
284
|
+
archiveBuffer,
|
|
285
|
+
archiveName,
|
|
286
|
+
progressCallback
|
|
287
|
+
);
|
|
288
|
+
await logAndProgress('Source code uploaded successfully', progressCallback);
|
|
289
|
+
|
|
290
|
+
const container = createDirectSourceDeploymentContainer({
|
|
291
|
+
bucketName,
|
|
292
|
+
fileName: archiveName,
|
|
293
|
+
deploymentAttrs,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const service = await deployToCloudRun(
|
|
297
|
+
projectId,
|
|
298
|
+
region,
|
|
299
|
+
serviceName,
|
|
300
|
+
undefined,
|
|
301
|
+
progressCallback,
|
|
302
|
+
skipIamCheck,
|
|
303
|
+
DEPLOYMENT_TYPES.NO_BUILD,
|
|
304
|
+
container,
|
|
305
|
+
deploymentAttrs.runtime
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
return service;
|
|
309
|
+
} finally {
|
|
310
|
+
await cleanupTempDirectory(tempDir);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Handles deployment using Google Cloud Build.
|
|
316
|
+
* This function zips the source code, uploads it to Google Cloud Storage,
|
|
317
|
+
* triggers a Cloud Build job to build a container image, pushes the image
|
|
318
|
+
* to Artifact Registry, and then deploys the image to Cloud Run.
|
|
319
|
+
*
|
|
320
|
+
* @param {object} context - The context object containing various GCP clients.
|
|
321
|
+
* @param {string} projectId - The Google Cloud project ID.
|
|
322
|
+
* @param {string} region - The Google Cloud region for the deployment.
|
|
323
|
+
* @param {string} serviceName - The name of the Cloud Run service.
|
|
324
|
+
* @param {Array<string|{filename: string, content: Buffer|string}>} files - Files to deploy.
|
|
325
|
+
* @param {boolean} hasDockerfile - Whether a Dockerfile is present in the files.
|
|
326
|
+
* @param {string} bucketName - The name of the GCS bucket to use for staging.
|
|
327
|
+
* @param {function(object): void} [progressCallback] - Optional callback for progress updates.
|
|
328
|
+
* @param {boolean} [skipIamCheck=false] - Whether to skip the IAM check during Cloud Run service creation/update.
|
|
329
|
+
* @returns {Promise<object>} A promise that resolves with the deployed Cloud Run service object.
|
|
330
|
+
* @throws {Error} If any part of the build or deployment process fails.
|
|
331
|
+
*/
|
|
332
|
+
async function deployWithBuild(
|
|
333
|
+
projectId,
|
|
334
|
+
region,
|
|
335
|
+
serviceName,
|
|
336
|
+
files,
|
|
337
|
+
hasDockerfile,
|
|
338
|
+
bucketName,
|
|
339
|
+
progressCallback,
|
|
340
|
+
skipIamCheck
|
|
341
|
+
) {
|
|
342
|
+
const imageUrl = `${region}-docker.pkg.dev/${projectId}/${DEPLOYMENT_CONFIG.REPO_NAME}/${serviceName}:${DEPLOYMENT_CONFIG.IMAGE_TAG}`;
|
|
343
|
+
|
|
344
|
+
const bucket = await ensureStorageBucketExists(
|
|
345
|
+
projectId,
|
|
346
|
+
bucketName,
|
|
347
|
+
region,
|
|
348
|
+
progressCallback
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const archiveBuffer = await zipFiles(files, false, progressCallback);
|
|
352
|
+
const archiveName = DEPLOYMENT_CONFIG.ZIP_FILE_NAME;
|
|
353
|
+
|
|
354
|
+
await uploadToStorageBucket(
|
|
355
|
+
bucket,
|
|
356
|
+
archiveBuffer,
|
|
357
|
+
archiveName,
|
|
358
|
+
progressCallback
|
|
359
|
+
);
|
|
360
|
+
await logAndProgress('Source code uploaded successfully', progressCallback);
|
|
361
|
+
|
|
362
|
+
await ensureArtifactRegistryRepoExists(
|
|
363
|
+
projectId,
|
|
364
|
+
region,
|
|
365
|
+
DEPLOYMENT_CONFIG.REPO_NAME,
|
|
366
|
+
'DOCKER',
|
|
367
|
+
progressCallback
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
await logAndProgress(`Dockerfile: ${hasDockerfile}`, progressCallback);
|
|
371
|
+
|
|
372
|
+
const buildResult = await triggerCloudBuild(
|
|
373
|
+
projectId,
|
|
374
|
+
region,
|
|
375
|
+
bucketName,
|
|
376
|
+
archiveName,
|
|
377
|
+
DEPLOYMENT_CONFIG.REPO_NAME,
|
|
378
|
+
imageUrl,
|
|
379
|
+
hasDockerfile,
|
|
380
|
+
progressCallback
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
const builtImageUrl = buildResult.results.images[0].name;
|
|
384
|
+
|
|
385
|
+
const service = await deployToCloudRun(
|
|
386
|
+
projectId,
|
|
387
|
+
region,
|
|
388
|
+
serviceName,
|
|
389
|
+
builtImageUrl,
|
|
390
|
+
progressCallback,
|
|
391
|
+
skipIamCheck,
|
|
392
|
+
DEPLOYMENT_TYPES.WITH_BUILD
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
await logAndProgress(`Deployment completed successfully`, progressCallback);
|
|
396
|
+
return service;
|
|
397
|
+
}
|
|
398
|
+
|
|
216
399
|
/**
|
|
217
400
|
* Deploys a service to Google Cloud Run.
|
|
218
401
|
* @param {object} config - The deployment configuration.
|
|
@@ -257,128 +440,57 @@ export async function deploy({
|
|
|
257
440
|
}
|
|
258
441
|
}
|
|
259
442
|
|
|
260
|
-
const path = await import('path');
|
|
261
|
-
const fs = await import('fs');
|
|
262
|
-
const { Storage } = await import('@google-cloud/storage');
|
|
263
|
-
const { CloudBuildClient } = await import('@google-cloud/cloudbuild');
|
|
264
|
-
const { ArtifactRegistryClient } = await import(
|
|
265
|
-
'@google-cloud/artifact-registry'
|
|
266
|
-
);
|
|
267
|
-
const { v2: CloudRunV2Module } = await import('@google-cloud/run');
|
|
268
|
-
const { ServicesClient } = CloudRunV2Module;
|
|
269
|
-
const { ServiceUsageClient } = await import('@google-cloud/service-usage');
|
|
270
|
-
const { Logging } = await import('@google-cloud/logging');
|
|
271
|
-
|
|
272
443
|
try {
|
|
273
|
-
const context = {
|
|
274
|
-
storage: new Storage({ projectId }),
|
|
275
|
-
cloudBuildClient: new CloudBuildClient({ projectId }),
|
|
276
|
-
artifactRegistryClient: new ArtifactRegistryClient({ projectId }),
|
|
277
|
-
runClient: new ServicesClient({ projectId }),
|
|
278
|
-
serviceUsageClient: new ServiceUsageClient({ projectId }),
|
|
279
|
-
loggingClient: new Logging({ projectId }),
|
|
280
|
-
};
|
|
281
|
-
|
|
282
444
|
await ensureApisEnabled(
|
|
283
|
-
context,
|
|
284
445
|
projectId,
|
|
285
|
-
|
|
446
|
+
REQUIRED_APIS.SOURCE_DEPLOY,
|
|
286
447
|
progressCallback
|
|
287
448
|
);
|
|
288
449
|
|
|
289
450
|
const bucketName = `${projectId}-source-bucket`;
|
|
290
|
-
const imageUrl = `${region}-docker.pkg.dev/${projectId}/${REPO_NAME}/${serviceName}:${IMAGE_TAG}`;
|
|
291
451
|
|
|
292
452
|
await logAndProgress(`Project: ${projectId}`, progressCallback);
|
|
293
453
|
await logAndProgress(`Region: ${region}`, progressCallback);
|
|
294
454
|
await logAndProgress(`Service Name: ${serviceName}`, progressCallback);
|
|
295
455
|
await logAndProgress(`Files to deploy: ${files.length}`, progressCallback);
|
|
296
456
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
files
|
|
300
|
-
typeof files[0] === 'string' &&
|
|
301
|
-
fs.statSync(files[0]).isDirectory()
|
|
302
|
-
) {
|
|
303
|
-
// Handle folder deployment: check for Dockerfile inside the folder
|
|
304
|
-
const dockerfilePath = path.join(files[0], 'Dockerfile');
|
|
305
|
-
const dockerfilePathLowerCase = path.join(files[0], 'dockerfile');
|
|
306
|
-
if (
|
|
307
|
-
fs.existsSync(dockerfilePath) ||
|
|
308
|
-
fs.existsSync(dockerfilePathLowerCase)
|
|
309
|
-
) {
|
|
310
|
-
hasDockerfile = true;
|
|
311
|
-
}
|
|
312
|
-
} else {
|
|
313
|
-
// Handle file list deployment or file content deployment
|
|
314
|
-
for (const file of files) {
|
|
315
|
-
if (typeof file === 'string') {
|
|
316
|
-
if (path.basename(file).toLowerCase() === 'dockerfile') {
|
|
317
|
-
hasDockerfile = true;
|
|
318
|
-
break;
|
|
319
|
-
}
|
|
320
|
-
} else if (typeof file === 'object' && file.filename) {
|
|
321
|
-
if (path.basename(file.filename).toLowerCase() === 'dockerfile') {
|
|
322
|
-
hasDockerfile = true;
|
|
323
|
-
break;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
await logAndProgress(`Dockerfile: ${hasDockerfile}`, progressCallback);
|
|
329
|
-
|
|
330
|
-
const bucket = await ensureStorageBucketExists(
|
|
331
|
-
context,
|
|
332
|
-
bucketName,
|
|
333
|
-
region,
|
|
334
|
-
progressCallback
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
const zipBuffer = await zipFiles(files, progressCallback);
|
|
338
|
-
await uploadToStorageBucket(
|
|
339
|
-
context,
|
|
340
|
-
bucket,
|
|
341
|
-
zipBuffer,
|
|
342
|
-
ZIP_FILE_NAME,
|
|
343
|
-
progressCallback
|
|
344
|
-
);
|
|
345
|
-
await logAndProgress('Source code uploaded successfully', progressCallback);
|
|
457
|
+
// Determines if we can use the direct source deployment (no Cloud Build)
|
|
458
|
+
const { hasDockerfile, deploymentAttrs } =
|
|
459
|
+
makeFileDeploymentMetadata(files);
|
|
346
460
|
|
|
347
|
-
await
|
|
348
|
-
context,
|
|
349
|
-
projectId,
|
|
350
|
-
region,
|
|
351
|
-
REPO_NAME,
|
|
352
|
-
'DOCKER',
|
|
353
|
-
progressCallback
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
const buildResult = await triggerCloudBuild(
|
|
357
|
-
context,
|
|
358
|
-
projectId,
|
|
359
|
-
region,
|
|
360
|
-
bucketName,
|
|
361
|
-
ZIP_FILE_NAME,
|
|
362
|
-
REPO_NAME,
|
|
363
|
-
imageUrl,
|
|
364
|
-
hasDockerfile,
|
|
365
|
-
progressCallback
|
|
366
|
-
);
|
|
461
|
+
await logAndProgress(`Dockerfile: ${hasDockerfile}`, progressCallback);
|
|
367
462
|
|
|
368
|
-
|
|
463
|
+
if (canDeployWithoutBuild({ hasDockerfile, deploymentAttrs })) {
|
|
464
|
+
try {
|
|
465
|
+
return await deployWithZip(
|
|
466
|
+
projectId,
|
|
467
|
+
region,
|
|
468
|
+
serviceName,
|
|
469
|
+
files,
|
|
470
|
+
bucketName,
|
|
471
|
+
deploymentAttrs,
|
|
472
|
+
progressCallback,
|
|
473
|
+
skipIamCheck
|
|
474
|
+
);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
await logAndProgress(
|
|
477
|
+
`Failed to deploy directly from source: ${error.message}. Retrying to deploy using Cloud Build...`,
|
|
478
|
+
progressCallback,
|
|
479
|
+
'warn'
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
369
483
|
|
|
370
|
-
|
|
371
|
-
context,
|
|
484
|
+
return await deployWithBuild(
|
|
372
485
|
projectId,
|
|
373
486
|
region,
|
|
374
487
|
serviceName,
|
|
375
|
-
|
|
488
|
+
files,
|
|
489
|
+
hasDockerfile,
|
|
490
|
+
bucketName,
|
|
376
491
|
progressCallback,
|
|
377
492
|
skipIamCheck
|
|
378
493
|
);
|
|
379
|
-
|
|
380
|
-
await logAndProgress(`Deployment Completed Successfully`, progressCallback);
|
|
381
|
-
return service;
|
|
382
494
|
} catch (error) {
|
|
383
495
|
const deployFailedMessage = `Deployment Failed: ${error.message}`;
|
|
384
496
|
console.error(`Deployment Failed`, error);
|
|
@@ -431,20 +543,10 @@ export async function deployImage({
|
|
|
431
543
|
}
|
|
432
544
|
}
|
|
433
545
|
|
|
434
|
-
const { v2: CloudRunV2Module } = await import('@google-cloud/run');
|
|
435
|
-
const { ServicesClient } = CloudRunV2Module;
|
|
436
|
-
const { ServiceUsageClient } = await import('@google-cloud/service-usage');
|
|
437
|
-
|
|
438
546
|
try {
|
|
439
|
-
const context = {
|
|
440
|
-
runClient: new ServicesClient({ projectId }),
|
|
441
|
-
serviceUsageClient: new ServiceUsageClient({ projectId }),
|
|
442
|
-
};
|
|
443
|
-
|
|
444
547
|
await ensureApisEnabled(
|
|
445
|
-
context,
|
|
446
548
|
projectId,
|
|
447
|
-
|
|
549
|
+
REQUIRED_APIS.IMAGE_DEPLOY,
|
|
448
550
|
progressCallback
|
|
449
551
|
);
|
|
450
552
|
|
|
@@ -454,16 +556,16 @@ export async function deployImage({
|
|
|
454
556
|
await logAndProgress(`Image URL: ${imageUrl}`, progressCallback);
|
|
455
557
|
|
|
456
558
|
const service = await deployToCloudRun(
|
|
457
|
-
context,
|
|
458
559
|
projectId,
|
|
459
560
|
region,
|
|
460
561
|
serviceName,
|
|
461
562
|
imageUrl,
|
|
462
563
|
progressCallback,
|
|
463
|
-
skipIamCheck
|
|
564
|
+
skipIamCheck,
|
|
565
|
+
DEPLOYMENT_TYPES.IMAGE
|
|
464
566
|
);
|
|
465
567
|
|
|
466
|
-
await logAndProgress(`Deployment
|
|
568
|
+
await logAndProgress(`Deployment completed successfully`, progressCallback);
|
|
467
569
|
return service;
|
|
468
570
|
} catch (error) {
|
|
469
571
|
const deployFailedMessage = `Deployment Failed: ${error.message}`;
|