@google-cloud/cloud-run-mcp 1.7.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/lib/clients.js ADDED
@@ -0,0 +1,222 @@
1
+ /*
2
+ Copyright 2025 Google LLC
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ https://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ const AUTHORIZATION_HEADER = 'Authorization';
18
+ const BEARER_PREFIX = 'Bearer';
19
+ const GCLOUD_AUTH = 'gcloud_auth';
20
+
21
+ function keyGenerator(projectId, accessToken) {
22
+ return accessToken !== GCLOUD_AUTH ? projectId + accessToken : projectId;
23
+ }
24
+
25
+ const clients = {
26
+ run: new Map(),
27
+ builds: new Map(),
28
+ serviceUsage: new Map(),
29
+ storage: new Map(),
30
+ cloudBuild: new Map(),
31
+ artifactRegistry: new Map(),
32
+ logging: new Map(),
33
+ billing: new Map(),
34
+ projects: new Map(),
35
+ };
36
+
37
+ function getAuthClient(accessToken) {
38
+ return {
39
+ getRequestHeaders: () => {
40
+ const headers = new Map();
41
+ headers.set(AUTHORIZATION_HEADER, `${BEARER_PREFIX} ${accessToken}`);
42
+ return headers;
43
+ },
44
+ };
45
+ }
46
+
47
+ export async function getClient(
48
+ service,
49
+ key,
50
+ loadClient,
51
+ options = {},
52
+ accessToken = null
53
+ ) {
54
+ if (!clients[service].has(key)) {
55
+ const ClientClass = await loadClient();
56
+ const finalOptions = { ...options };
57
+ if (accessToken && accessToken !== GCLOUD_AUTH) {
58
+ finalOptions.authClient = getAuthClient(accessToken);
59
+ }
60
+ clients[service].set(key, new ClientClass(finalOptions));
61
+ }
62
+ return clients[service].get(key);
63
+ }
64
+
65
+ /**
66
+ * Gets a Cloud Run Services Client for the specified project.
67
+ * @param {string} projectId - The Google Cloud project ID.
68
+ * @returns {Promise<import('@google-cloud/run').v2.ServicesClient>}
69
+ */
70
+ export async function getRunClient(projectId, accessToken = GCLOUD_AUTH) {
71
+ const key = keyGenerator(projectId, accessToken);
72
+ return getClient(
73
+ 'run',
74
+ key,
75
+ async () => (await import('@google-cloud/run')).v2.ServicesClient,
76
+ { projectId },
77
+ accessToken
78
+ );
79
+ }
80
+
81
+ /**
82
+ * Gets a Cloud Run Build Client for the specified project.
83
+ * @param {string} projectId - The Google Cloud project ID.
84
+ * @returns {Promise<import('@google-cloud/run').v2.BuildsClient>}
85
+ */
86
+ export async function getBuildsClient(projectId, accessToken = GCLOUD_AUTH) {
87
+ const key = keyGenerator(projectId, accessToken);
88
+ return getClient(
89
+ 'builds',
90
+ key,
91
+ async () => (await import('@google-cloud/run')).v2.BuildsClient,
92
+ { projectId },
93
+ accessToken
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Gets a Service Usage Client for the specified project.
99
+ * @param {string} projectId - The Google Cloud project ID.
100
+ * @returns {Promise<import('@google-cloud/service-usage').ServiceUsageClient>}
101
+ */
102
+ export async function getServiceUsageClient(
103
+ projectId,
104
+ accessToken = GCLOUD_AUTH
105
+ ) {
106
+ const key = keyGenerator(projectId, accessToken);
107
+ return getClient(
108
+ 'serviceUsage',
109
+ key,
110
+ async () =>
111
+ (await import('@google-cloud/service-usage')).ServiceUsageClient,
112
+ { projectId },
113
+ accessToken
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Gets a Storage Client for the specified project.
119
+ * @param {string} projectId - The Google Cloud project ID.
120
+ * @returns {Promise<import('@google-cloud/storage').Storage>}
121
+ */
122
+ export async function getStorageClient(projectId, accessToken = GCLOUD_AUTH) {
123
+ const key = keyGenerator(projectId, accessToken);
124
+ return getClient(
125
+ 'storage',
126
+ key,
127
+ async () => (await import('@google-cloud/storage')).Storage,
128
+ { projectId },
129
+ accessToken
130
+ );
131
+ }
132
+
133
+ /**
134
+ * Gets a Cloud Build Client for the specified project.
135
+ * @param {string} projectId - The Google Cloud project ID.
136
+ * @returns {Promise<import('@google-cloud/cloudbuild').CloudBuildClient>}
137
+ */
138
+ export async function getCloudBuildClient(
139
+ projectId,
140
+ accessToken = GCLOUD_AUTH
141
+ ) {
142
+ const key = keyGenerator(projectId, accessToken);
143
+ return getClient(
144
+ 'cloudBuild',
145
+ key,
146
+ async () => (await import('@google-cloud/cloudbuild')).CloudBuildClient,
147
+ { projectId },
148
+ accessToken
149
+ );
150
+ }
151
+
152
+ /**
153
+ * Gets an Artifact Registry Client for the specified project.
154
+ * @param {string} projectId - The Google Cloud project ID.
155
+ * @returns {Promise<import('@google-cloud/artifact-registry').ArtifactRegistryClient>}
156
+ */
157
+ export async function getArtifactRegistryClient(
158
+ projectId,
159
+ accessToken = GCLOUD_AUTH
160
+ ) {
161
+ const key = keyGenerator(projectId, accessToken);
162
+ return getClient(
163
+ 'artifactRegistry',
164
+ key,
165
+ async () =>
166
+ (await import('@google-cloud/artifact-registry')).ArtifactRegistryClient,
167
+ { projectId },
168
+ accessToken
169
+ );
170
+ }
171
+
172
+ /**
173
+ * Gets a Logging Client for the specified project.
174
+ * @param {string} projectId - The Google Cloud project ID.
175
+ * @returns {Promise<import('@google-cloud/logging').Logging>}
176
+ */
177
+ export async function getLoggingClient(projectId, accessToken = GCLOUD_AUTH) {
178
+ const key = keyGenerator(projectId, accessToken);
179
+ return getClient(
180
+ 'logging',
181
+ key,
182
+ async () => (await import('@google-cloud/logging')).Logging,
183
+ { projectId },
184
+ accessToken
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Gets a Billing Client for the specified project.
190
+ * Note: BillingClient usually doesn't take projectId in constructor for listing accounts,
191
+ * but might for project billing info. We will cache by projectId anyway or 'global' if projectId is null.
192
+ * @param {string} [projectId] - The Google Cloud project ID (optional).
193
+ * @returns {Promise<import('@google-cloud/billing').CloudBillingClient>}
194
+ */
195
+ export async function getBillingClient(
196
+ projectId = 'global',
197
+ accessToken = GCLOUD_AUTH
198
+ ) {
199
+ const key = keyGenerator(projectId, accessToken);
200
+ return getClient(
201
+ 'billing',
202
+ key,
203
+ async () => (await import('@google-cloud/billing')).CloudBillingClient,
204
+ projectId !== 'global' ? { projectId } : {},
205
+ accessToken
206
+ );
207
+ }
208
+
209
+ /**
210
+ * Gets a Projects Client (Resource Manager).
211
+ * @returns {Promise<import('@google-cloud/resource-manager').ProjectsClient>}
212
+ */
213
+ export async function getProjectsClient(accessToken = GCLOUD_AUTH) {
214
+ const key = keyGenerator('global', accessToken);
215
+ return getClient(
216
+ 'projects',
217
+ key,
218
+ async () => (await import('@google-cloud/resource-manager')).ProjectsClient,
219
+ {},
220
+ accessToken
221
+ );
222
+ }
@@ -14,6 +14,15 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
+ /**
18
+ * Lists all accessible Google Cloud Billing Accounts.
19
+ * @async
20
+ * @function listBillingAccounts
21
+ * @returns {Promise<Array<{name: string, displayName: string, open: boolean}>>} A promise that resolves to an array of billing account objects,
22
+ * each with 'name', 'displayName', and 'open' status. Returns an empty array on error.
23
+ */
24
+ import { getBillingClient } from '../clients.js';
25
+
17
26
  /**
18
27
  * Lists all accessible Google Cloud Billing Accounts.
19
28
  * @async
@@ -22,8 +31,7 @@ limitations under the License.
22
31
  * each with 'name', 'displayName', and 'open' status. Returns an empty array on error.
23
32
  */
24
33
  export async function listBillingAccounts() {
25
- const { CloudBillingClient } = await import('@google-cloud/billing');
26
- const client = new CloudBillingClient();
34
+ const client = await getBillingClient();
27
35
  try {
28
36
  const [accounts] = await client.listBillingAccounts();
29
37
  if (!accounts || accounts.length === 0) {
@@ -53,8 +61,7 @@ export async function attachProjectToBillingAccount(
53
61
  projectId,
54
62
  billingAccountName
55
63
  ) {
56
- const { CloudBillingClient } = await import('@google-cloud/billing');
57
- const client = new CloudBillingClient();
64
+ const client = await getBillingClient();
58
65
  const projectName = `projects/${projectId}`;
59
66
 
60
67
  if (!projectId) {
@@ -105,8 +112,7 @@ export async function attachProjectToBillingAccount(
105
112
  * @returns {Promise<boolean>} A promise that resolves to true if billing is enabled, false otherwise.
106
113
  */
107
114
  export async function isBillingEnabled(projectId) {
108
- const { CloudBillingClient } = await import('@google-cloud/billing');
109
- const client = new CloudBillingClient();
115
+ const client = await getBillingClient();
110
116
  const projectName = `projects/${projectId}`;
111
117
  try {
112
118
  // getProjectBillingInfo requires cloudbilling.googleapis.com API to check billing status
@@ -127,15 +133,10 @@ export async function isBillingEnabled(projectId) {
127
133
  /**
128
134
  * Ensures billing is enabled for a project. If not, attempts to enable it
129
135
  * by attaching the single open billing account if one is found.
130
- * @param {object} context - The context object containing clients and other parameters.
131
136
  * @param {string} projectId The project ID to check billing for.
132
137
  * @param {function} progressCallback A callback function for progress updates.
133
138
  */
134
- export async function ensureBillingEnabled(
135
- context,
136
- projectId,
137
- progressCallback
138
- ) {
139
+ export async function ensureBillingEnabled(projectId, progressCallback) {
139
140
  if (!(await isBillingEnabled(projectId))) {
140
141
  // Billing is disabled, try to fix it.
141
142
  const accounts = await listBillingAccounts();
@@ -14,8 +14,15 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
 
17
+ import {
18
+ getCloudBuildClient,
19
+ getLoggingClient,
20
+ getRunClient,
21
+ getBuildsClient,
22
+ } from '../clients.js';
17
23
  import { callWithRetry } from './helpers.js';
18
24
  import { logAndProgress } from '../util/helpers.js';
25
+ import { checkCloudRunServiceExists } from './run.js';
19
26
 
20
27
  const DELAY_WAIT_FOR_BUILD_LOGS = 10000; // 10 seconds delay to allow logs to propagate
21
28
  const BUILD_LOGS_LINES_TO_FETCH = 100; // Number of log lines to fetch for build logs snippet
@@ -23,10 +30,10 @@ const BUILD_LOGS_LINES_TO_FETCH = 100; // Number of log lines to fetch for build
23
30
  /**
24
31
  * Triggers a Google Cloud Build job to build a container image from source code in a GCS bucket.
25
32
  * It uses either a Dockerfile found in the source or Google Cloud Buildpacks if no Dockerfile is present.
33
+ * It performs a dry-run deployment check before starting the build.
26
34
  * Waits for the build to complete and returns the build result.
27
35
  *
28
36
  * @async
29
- * @param {object} context - The context object containing clients and other parameters.
30
37
  * @param {string} projectId - The Google Cloud project ID.
31
38
  * @param {string} location - The Google Cloud region for the build.
32
39
  * @param {string} sourceBucketName - The GCS bucket name where the source code (zip) is stored.
@@ -39,7 +46,6 @@ const BUILD_LOGS_LINES_TO_FETCH = 100; // Number of log lines to fetch for build
39
46
  * @throws {Error} If the Cloud Build job fails, times out, or encounters an error during initiation or execution.
40
47
  */
41
48
  export async function triggerCloudBuild(
42
- context,
43
49
  projectId,
44
50
  location,
45
51
  sourceBucketName,
@@ -49,65 +55,107 @@ export async function triggerCloudBuild(
49
55
  hasDockerfile,
50
56
  progressCallback
51
57
  ) {
58
+ const serviceName = targetImageUrl.split('/').pop().split(/[:@]/)[0];
59
+ const parent = `projects/${projectId}/locations/${location}`;
60
+ const serviceFullName = `${parent}/services/${serviceName}`;
61
+ const buildsClient = await getBuildsClient(projectId);
62
+ const runClient = await getRunClient(projectId);
63
+ const serviceExists = await checkCloudRunServiceExists(
64
+ projectId,
65
+ location,
66
+ serviceName,
67
+ progressCallback
68
+ );
69
+ const cloudBuildClient = await getCloudBuildClient(projectId);
70
+ const loggingClient = await getLoggingClient(projectId);
52
71
  let buildSteps;
53
72
 
54
- if (hasDockerfile) {
55
- buildSteps = [
56
- {
57
- name: 'gcr.io/cloud-builders/docker',
58
- args: ['build', '-t', targetImageUrl, '.'],
59
- dir: '/workspace',
60
- },
61
- ];
62
- } else {
63
- buildSteps = [
64
- {
65
- name: 'gcr.io/k8s-skaffold/pack',
66
- entrypoint: 'pack',
67
- args: [
68
- 'build',
69
- targetImageUrl,
70
- '--builder',
71
- 'gcr.io/buildpacks/builder:latest',
72
- ],
73
- dir: '/workspace',
74
- },
75
- ];
73
+ const servicePatch = {
74
+ template: {
75
+ containers: [{ image: targetImageUrl }],
76
+ },
77
+ };
78
+
79
+ try {
80
+ if (serviceExists) {
81
+ await logAndProgress(
82
+ `Performing dry-run update for service ${serviceName}...`,
83
+ progressCallback
84
+ );
85
+ servicePatch.name = serviceFullName;
86
+ await callWithRetry(
87
+ () =>
88
+ runClient.updateService({
89
+ service: servicePatch,
90
+ validateOnly: true,
91
+ }),
92
+ 'run.updateService-dryrun'
93
+ );
94
+ } else {
95
+ await logAndProgress(
96
+ `Performing dry-run creation for service ${serviceName}...`,
97
+ progressCallback
98
+ );
99
+ await callWithRetry(
100
+ () =>
101
+ runClient.createService({
102
+ parent: parent,
103
+ service: servicePatch,
104
+ serviceId: serviceName,
105
+ validateOnly: true,
106
+ }),
107
+ 'run.createService-dryrun'
108
+ );
109
+ }
110
+ await logAndProgress(
111
+ `Dry-run validation successful.`,
112
+ progressCallback,
113
+ 'debug'
114
+ );
115
+ } catch (err) {
116
+ await logAndProgress(
117
+ `Dry-run validation failed for service ${serviceName}.`,
118
+ progressCallback,
119
+ 'error'
120
+ );
121
+ throw new Error(`Dry-run deployment failed: ${err.message}`);
76
122
  }
77
123
 
78
- const build = {
79
- source: {
80
- storageSource: {
81
- bucket: sourceBucketName,
82
- object: sourceBlobName,
83
- },
124
+ const request = {
125
+ parent: `projects/${projectId}/locations/${location}`,
126
+ storageSource: {
127
+ bucket: sourceBucketName,
128
+ object: sourceBlobName,
84
129
  },
85
- steps: buildSteps,
86
- images: [targetImageUrl],
130
+ imageUri: targetImageUrl,
131
+ client: 'cloud-run-mcp',
87
132
  };
133
+ if (hasDockerfile) {
134
+ request.dockerBuild = {};
135
+ } else {
136
+ request.buildpackBuild = {};
137
+ }
88
138
 
89
139
  await logAndProgress(
90
140
  `Initiating Cloud Build for gs://${sourceBucketName}/${sourceBlobName} in ${location}...`,
91
141
  progressCallback
92
142
  );
93
- const [operation] = await callWithRetry(
94
- () =>
95
- context.cloudBuildClient.createBuild({
96
- projectId: projectId,
97
- build: build,
98
- }),
99
- 'cloudBuild.createBuild'
100
- );
101
143
 
144
+ const [submitBuildResponse] = await callWithRetry(
145
+ () => buildsClient.submitBuild(request),
146
+ 'builds.submitBuild'
147
+ );
102
148
  await logAndProgress(`Cloud Build job started...`, progressCallback);
103
- const buildId = operation.metadata.build.id;
149
+ const encodedBuildId = submitBuildResponse.buildOperation.name
150
+ .split('/')
151
+ .pop();
152
+ const buildId = Buffer.from(encodedBuildId, 'base64').toString('ascii');
104
153
  let completedBuild;
105
154
  while (true) {
106
155
  const [getBuildOperation] = await callWithRetry(
107
156
  () =>
108
- context.cloudBuildClient.getBuild({
109
- projectId: projectId,
110
- id: buildId,
157
+ cloudBuildClient.getBuild({
158
+ name: `projects/${projectId}/locations/${location}/builds/${buildId}`,
111
159
  }),
112
160
  `cloudBuild.getBuild ${buildId}`
113
161
  );
@@ -132,10 +180,9 @@ export async function triggerCloudBuild(
132
180
  `Cloud Build job ${buildId} completed successfully.`,
133
181
  progressCallback
134
182
  );
135
- await logAndProgress(
136
- `Image built: ${completedBuild.results.images[0].name}`,
137
- progressCallback
138
- );
183
+ const builtImage = completedBuild.results.images[0].name;
184
+ await logAndProgress(`Image built: ${builtImage}`, progressCallback);
185
+
139
186
  return completedBuild;
140
187
  } else {
141
188
  const failureMessage = `Cloud Build job ${buildId} failed with status: ${completedBuild.status}`;
@@ -160,7 +207,7 @@ export async function triggerCloudBuild(
160
207
  // Fetch the most recent N log entries
161
208
  const [entries] = await callWithRetry(
162
209
  () =>
163
- context.loggingClient.getEntries({
210
+ loggingClient.getEntries({
164
211
  filter: logFilter,
165
212
  orderBy: 'timestamp desc', // Get latest logs first
166
213
  pageSize: BUILD_LOGS_LINES_TO_FETCH,
@@ -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 { getServiceUsageClient } from '../clients.js';
17
18
  import { ensureBillingEnabled } from './billing.js';
18
19
 
19
20
  const PREREQUISITE_APIS = [
@@ -93,19 +94,14 @@ async function checkAndEnableApi(
93
94
 
94
95
  /**
95
96
  * Enables a single Google Cloud API with retry logic.
96
- * @param {object} serviceUsageClient - The Service Usage client.
97
97
  * @param {string} projectId - The Google Cloud project ID.
98
98
  * @param {string} api - The API identifier (e.g., 'run.googleapis.com').
99
99
  * @param {function(string, string=): void} progressCallback - A function to call with progress updates.
100
100
  * @returns {Promise<void>} A promise that resolves when the API is enabled.
101
101
  * @throws {Error} If the API fails to enable or if there's an issue checking its status.
102
102
  */
103
- export async function enableApiWithRetry(
104
- serviceUsageClient,
105
- projectId,
106
- api,
107
- progressCallback
108
- ) {
103
+ export async function enableApiWithRetry(projectId, api, progressCallback) {
104
+ const serviceUsageClient = await getServiceUsageClient(projectId);
109
105
  const serviceName = `projects/${projectId}/services/${api}`;
110
106
  try {
111
107
  await checkAndEnableApi(
@@ -145,7 +141,6 @@ export async function enableApiWithRetry(
145
141
  * Throws an error if an API cannot be enabled.
146
142
  *
147
143
  * @async
148
- * @param {object} context - The context object containing clients and other parameters.
149
144
  * @param {string} projectId - The Google Cloud project ID.
150
145
  * @param {string[]} apis - An array of API identifiers to check and enable (e.g., 'run.googleapis.com').
151
146
  * @param {function(string, string=): void} progressCallback - A function to call with progress updates.
@@ -153,35 +148,20 @@ export async function enableApiWithRetry(
153
148
  * @throws {Error} If an API fails to enable or if there's an issue checking its status.
154
149
  * @returns {Promise<void>} A promise that resolves when all specified APIs are enabled.
155
150
  */
156
- export async function ensureApisEnabled(
157
- context,
158
- projectId,
159
- apis,
160
- progressCallback
161
- ) {
151
+ export async function ensureApisEnabled(projectId, apis, progressCallback) {
162
152
  for (const api of PREREQUISITE_APIS) {
163
- await enableApiWithRetry(
164
- context.serviceUsageClient,
165
- projectId,
166
- api,
167
- progressCallback
168
- );
153
+ await enableApiWithRetry(projectId, api, progressCallback);
169
154
  }
170
155
 
171
156
  // Before enabling other APIs, ensure billing is enabled.
172
- await ensureBillingEnabled(context, projectId, progressCallback);
157
+ await ensureBillingEnabled(projectId, progressCallback);
173
158
 
174
159
  const message = 'Checking and enabling required APIs...';
175
160
  console.log(message);
176
161
  if (progressCallback) progressCallback({ level: 'info', data: message });
177
162
 
178
163
  for (const api of apis) {
179
- await enableApiWithRetry(
180
- context.serviceUsageClient,
181
- projectId,
182
- api,
183
- progressCallback
184
- );
164
+ await enableApiWithRetry(projectId, api, progressCallback);
185
165
  }
186
166
  const successMsg = 'All required APIs are enabled.';
187
167
  console.log(successMsg);
@@ -18,6 +18,7 @@ import {
18
18
  listBillingAccounts,
19
19
  attachProjectToBillingAccount,
20
20
  } from './billing.js';
21
+ import { getProjectsClient } from '../clients.js';
21
22
 
22
23
  /**
23
24
  * Lists all accessible Google Cloud Platform projects.
@@ -26,8 +27,7 @@ import {
26
27
  * @returns {Promise<Array<{id: string}>>} A promise that resolves to an array of project objects, each with an 'id' property. Returns an empty array on error.
27
28
  */
28
29
  export async function listProjects() {
29
- const { ProjectsClient } = await import('@google-cloud/resource-manager');
30
- const client = new ProjectsClient();
30
+ const client = await getProjectsClient();
31
31
  try {
32
32
  const [projects] = await client.searchProjects();
33
33
  return projects.map((project) => ({
@@ -72,8 +72,7 @@ export function generateProjectId() {
72
72
  * @returns {Promise<{projectId: string}|null>} A promise that resolves to an object containing the new project's ID.
73
73
  */
74
74
  export async function createProject(projectId, parent) {
75
- const { ProjectsClient } = await import('@google-cloud/resource-manager');
76
- const client = new ProjectsClient();
75
+ const client = await getProjectsClient();
77
76
  let projectIdToUse = projectId;
78
77
 
79
78
  if (!projectIdToUse) {
@@ -173,8 +172,7 @@ export async function createProjectAndAttachBilling(projectIdParam, parent) {
173
172
  * @returns {Promise<void>} A promise that resolves when the delete operation is initiated.
174
173
  */
175
174
  export async function deleteProject(projectId) {
176
- const { ProjectsClient } = await import('@google-cloud/resource-manager');
177
- const client = new ProjectsClient();
175
+ const client = await getProjectsClient();
178
176
  try {
179
177
  console.log(`Attempting to delete project with ID: ${projectId}`);
180
178
  await client.deleteProject({ name: `projects/${projectId}` });
@@ -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 { getArtifactRegistryClient } from '../clients.js';
17
18
  import { callWithRetry } from './helpers.js';
18
19
  import { logAndProgress } from '../util/helpers.js';
19
20
 
@@ -22,7 +23,6 @@ import { logAndProgress } from '../util/helpers.js';
22
23
  * If the repository does not exist, it attempts to create it with the specified format.
23
24
  *
24
25
  * @async
25
- * @param {object} context - The context object containing clients and other parameters.
26
26
  * @param {string} projectId - The Google Cloud project ID.
27
27
  * @param {string} location - The Google Cloud region for the repository.
28
28
  * @param {string} repositoryId - The ID for the Artifact Registry repository.
@@ -32,15 +32,15 @@ import { logAndProgress } from '../util/helpers.js';
32
32
  * @throws {Error} If there's an error checking or creating the repository.
33
33
  */
34
34
  export async function ensureArtifactRegistryRepoExists(
35
- context,
36
35
  projectId,
37
36
  location,
38
37
  repositoryId,
39
38
  format = 'DOCKER',
40
39
  progressCallback
41
40
  ) {
41
+ const artifactRegistryClient = await getArtifactRegistryClient(projectId);
42
42
  const parent = `projects/${projectId}/locations/${location}`;
43
- const repoPath = context.artifactRegistryClient.repositoryPath(
43
+ const repoPath = artifactRegistryClient.repositoryPath(
44
44
  projectId,
45
45
  location,
46
46
  repositoryId
@@ -48,7 +48,7 @@ export async function ensureArtifactRegistryRepoExists(
48
48
 
49
49
  try {
50
50
  const [repository] = await callWithRetry(
51
- () => context.artifactRegistryClient.getRepository({ name: repoPath }),
51
+ () => artifactRegistryClient.getRepository({ name: repoPath }),
52
52
  `artifactRegistry.getRepository ${repositoryId}`
53
53
  );
54
54
  await logAndProgress(
@@ -68,7 +68,7 @@ export async function ensureArtifactRegistryRepoExists(
68
68
 
69
69
  const [operation] = await callWithRetry(
70
70
  () =>
71
- context.artifactRegistryClient.createRepository({
71
+ artifactRegistryClient.createRepository({
72
72
  parent: parent,
73
73
  repository: repositoryToCreate,
74
74
  repositoryId: repositoryId,