@google-cloud/cloud-run-mcp 1.7.0 → 1.9.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/.env.example +13 -0
- package/.env.gcloud-sdk-oauth +13 -0
- package/README.md +38 -0
- package/constants.js +8 -0
- package/lib/clients.js +292 -0
- package/lib/cloud-api/auth.js +1 -0
- package/lib/cloud-api/billing.js +21 -14
- package/lib/cloud-api/build.js +98 -49
- package/lib/cloud-api/helpers.js +10 -17
- package/lib/cloud-api/projects.js +16 -13
- package/lib/cloud-api/registry.js +9 -5
- package/lib/cloud-api/run.js +20 -38
- package/lib/cloud-api/storage.js +7 -7
- package/lib/deployment/deployer.js +33 -61
- package/lib/deployment/helpers.js +1 -1
- package/lib/middleware/oauth.js +92 -0
- package/lib/util/helpers.js +12 -0
- package/mcp-server.js +51 -7
- package/package.json +3 -3
- package/test/local/clients.test.js +176 -0
- package/test/local/cloud-api/build.test.js +293 -309
- package/test/local/mcp-server-stdio.test.js +0 -1
- package/test/local/mcp-server-streamable-http.test.js +69 -0
- package/test/local/mcp-server.test.js +1 -0
- package/test/local/middleware/oauth.test.js +156 -0
- package/test/local/mock-clients.js +35 -0
- package/test/local/tools.test.js +2 -2
- package/test/need-gcp/test-helpers.js +1 -1
- package/tools/register-tools.js +18 -5
- package/.kokoro/kokoro_build.sh +0 -22
- package/.kokoro/presubmit.cfg +0 -21
- package/.kokoro/run_tests.sh +0 -31
|
@@ -13,7 +13,6 @@ describe('MCP Server stdio startup', () => {
|
|
|
13
13
|
stderr = '';
|
|
14
14
|
serverProcess = spawn('node', ['mcp-server.js'], {
|
|
15
15
|
cwd: process.cwd(),
|
|
16
|
-
env: { ...process.env, GCP_STDIO: 'true' },
|
|
17
16
|
});
|
|
18
17
|
stderr = await waitForString(serverProcess.stderr, stdioMsg);
|
|
19
18
|
});
|
|
@@ -4,6 +4,11 @@ import { test, describe, before, after } from 'node:test';
|
|
|
4
4
|
import assert from 'node:assert/strict';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import { waitForString } from './test-utils.js';
|
|
7
|
+
import {
|
|
8
|
+
SCOPES,
|
|
9
|
+
BEARER_METHODS_SUPPORTED,
|
|
10
|
+
RESPONSE_TYPES_SUPPORTED,
|
|
11
|
+
} from '../../constants.js';
|
|
7
12
|
|
|
8
13
|
class MCPClient {
|
|
9
14
|
client = null;
|
|
@@ -59,3 +64,67 @@ describe('MCP Server in Streamble HTTP mode', () => {
|
|
|
59
64
|
});
|
|
60
65
|
});
|
|
61
66
|
});
|
|
67
|
+
|
|
68
|
+
describe('OAuth Endpoints', () => {
|
|
69
|
+
let serverProcess;
|
|
70
|
+
let stdout = '';
|
|
71
|
+
const httpMsg = 'Cloud Run MCP server listening on port 3001';
|
|
72
|
+
const oauthProtectedResource = 'https://example.com/resource';
|
|
73
|
+
const oauthAuthorizationServer = 'https://example.com/auth';
|
|
74
|
+
const oauthAuthorizationEndpoint = 'https://example.com/auth/authorize';
|
|
75
|
+
const oauthTokenEndpoint = 'https://example.com/auth/token';
|
|
76
|
+
|
|
77
|
+
before(async () => {
|
|
78
|
+
stdout = '';
|
|
79
|
+
serverProcess = spawn('node', ['mcp-server.js'], {
|
|
80
|
+
cwd: process.cwd(),
|
|
81
|
+
env: {
|
|
82
|
+
...process.env,
|
|
83
|
+
GCP_STDIO: 'false',
|
|
84
|
+
PORT: '3001',
|
|
85
|
+
OAUTH_PROTECTED_RESOURCE: oauthProtectedResource,
|
|
86
|
+
OAUTH_AUTHORIZATION_SERVER: oauthAuthorizationServer,
|
|
87
|
+
OAUTH_AUTHORIZATION_ENDPOINT: oauthAuthorizationEndpoint,
|
|
88
|
+
OAUTH_TOKEN_ENDPOINT: oauthTokenEndpoint,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
stdout = await waitForString(serverProcess.stdout, httpMsg);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
after(() => {
|
|
95
|
+
if (serverProcess) {
|
|
96
|
+
serverProcess.kill();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('should return correct OAuth protected resource configuration', async () => {
|
|
101
|
+
const response = await fetch(
|
|
102
|
+
'http://localhost:3001/.well-known/oauth-protected-resource'
|
|
103
|
+
);
|
|
104
|
+
assert.equal(response.status, 200);
|
|
105
|
+
const data = await response.json();
|
|
106
|
+
|
|
107
|
+
assert.deepStrictEqual(data, {
|
|
108
|
+
resource: oauthProtectedResource,
|
|
109
|
+
authorization_servers: [oauthAuthorizationServer],
|
|
110
|
+
scopes_supported: [SCOPES.OPENID, SCOPES.EMAIL, SCOPES.CLOUD_PLATFORM],
|
|
111
|
+
bearer_methods_supported: [...BEARER_METHODS_SUPPORTED],
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('should return correct OAuth authorization server configuration', async () => {
|
|
116
|
+
const response = await fetch(
|
|
117
|
+
'http://localhost:3001/.well-known/oauth-authorization-server'
|
|
118
|
+
);
|
|
119
|
+
assert.equal(response.status, 200);
|
|
120
|
+
const data = await response.json();
|
|
121
|
+
|
|
122
|
+
assert.deepStrictEqual(data, {
|
|
123
|
+
issuer: oauthProtectedResource,
|
|
124
|
+
authorization_endpoint: oauthAuthorizationEndpoint,
|
|
125
|
+
token_endpoint: oauthTokenEndpoint,
|
|
126
|
+
scopes_supported: [SCOPES.OPENID, SCOPES.EMAIL, SCOPES.CLOUD_PLATFORM],
|
|
127
|
+
response_types_supported: [...RESPONSE_TYPES_SUPPORTED],
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, mock, beforeEach, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import esmock from 'esmock';
|
|
4
|
+
|
|
5
|
+
describe('oauthMiddleware', () => {
|
|
6
|
+
let req;
|
|
7
|
+
let res;
|
|
8
|
+
let next;
|
|
9
|
+
let originalEnv;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
originalEnv = process.env;
|
|
13
|
+
process.env = { ...originalEnv };
|
|
14
|
+
req = {
|
|
15
|
+
headers: {},
|
|
16
|
+
body: {},
|
|
17
|
+
};
|
|
18
|
+
res = {
|
|
19
|
+
headersSent: false,
|
|
20
|
+
status: mock.fn(() => res),
|
|
21
|
+
json: mock.fn(),
|
|
22
|
+
};
|
|
23
|
+
next = mock.fn();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
process.env = originalEnv;
|
|
28
|
+
mock.restoreAll();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should call next() if OAUTH_ENABLED is not "true"', async () => {
|
|
32
|
+
process.env.OAUTH_ENABLED = 'false';
|
|
33
|
+
const { oauthMiddleware } = await esmock(
|
|
34
|
+
'../../../lib/middleware/oauth.js',
|
|
35
|
+
{}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
await oauthMiddleware(req, res, next);
|
|
39
|
+
|
|
40
|
+
assert.strictEqual(next.mock.callCount(), 1);
|
|
41
|
+
assert.strictEqual(res.status.mock.callCount(), 0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should call next() if method is not tools/call', async () => {
|
|
45
|
+
process.env.OAUTH_ENABLED = 'true';
|
|
46
|
+
req.body.method = 'other/method';
|
|
47
|
+
|
|
48
|
+
// We don't verify token if it's not a tool call (based on current implementation)
|
|
49
|
+
// So we don't need to mock successful verification here
|
|
50
|
+
const { oauthMiddleware } = await esmock(
|
|
51
|
+
'../../../lib/middleware/oauth.js',
|
|
52
|
+
{}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await oauthMiddleware(req, res, next);
|
|
56
|
+
|
|
57
|
+
assert.strictEqual(next.mock.callCount(), 1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should return 401 if Authorization header is missing for tool call', async () => {
|
|
61
|
+
process.env.OAUTH_ENABLED = 'true';
|
|
62
|
+
req.body.method = 'tools/call';
|
|
63
|
+
|
|
64
|
+
const { oauthMiddleware } = await esmock(
|
|
65
|
+
'../../../lib/middleware/oauth.js',
|
|
66
|
+
{}
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
await oauthMiddleware(req, res, next);
|
|
70
|
+
|
|
71
|
+
assert.strictEqual(next.mock.callCount(), 0);
|
|
72
|
+
assert.strictEqual(res.status.mock.callCount(), 1);
|
|
73
|
+
assert.deepStrictEqual(res.status.mock.calls[0].arguments, [401]);
|
|
74
|
+
assert.strictEqual(res.json.mock.callCount(), 1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should verify token and call next() for valid tool call', async () => {
|
|
78
|
+
process.env.OAUTH_ENABLED = 'true';
|
|
79
|
+
req.body.method = 'tools/call';
|
|
80
|
+
req.headers.authorization = 'Bearer valid-token';
|
|
81
|
+
|
|
82
|
+
const mockGetTokenInfo = mock.fn(async () => ({ aud: 'valid-audience' }));
|
|
83
|
+
const mockGetOAuthClient = mock.fn(async () => ({
|
|
84
|
+
getTokenInfo: mockGetTokenInfo,
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
const { oauthMiddleware } = await esmock(
|
|
88
|
+
'../../../lib/middleware/oauth.js',
|
|
89
|
+
{
|
|
90
|
+
'../../../lib/clients.js': {
|
|
91
|
+
getOAuthClient: mockGetOAuthClient,
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await oauthMiddleware(req, res, next);
|
|
97
|
+
|
|
98
|
+
assert.strictEqual(next.mock.callCount(), 1);
|
|
99
|
+
assert.strictEqual(mockGetOAuthClient.mock.callCount(), 1);
|
|
100
|
+
assert.strictEqual(mockGetTokenInfo.mock.callCount(), 1);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should return 401 if token verification fails', async () => {
|
|
104
|
+
process.env.OAUTH_ENABLED = 'true';
|
|
105
|
+
req.body.method = 'tools/call';
|
|
106
|
+
req.headers.authorization = 'Bearer invalid-token';
|
|
107
|
+
|
|
108
|
+
const mockGetOAuthClient = mock.fn(async () => ({
|
|
109
|
+
getTokenInfo: async () => {
|
|
110
|
+
throw new Error('Invalid token');
|
|
111
|
+
},
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
const { oauthMiddleware } = await esmock(
|
|
115
|
+
'../../../lib/middleware/oauth.js',
|
|
116
|
+
{
|
|
117
|
+
'../../../lib/clients.js': {
|
|
118
|
+
getOAuthClient: mockGetOAuthClient,
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
await oauthMiddleware(req, res, next);
|
|
124
|
+
|
|
125
|
+
assert.strictEqual(next.mock.callCount(), 0);
|
|
126
|
+
assert.strictEqual(res.status.mock.callCount(), 1);
|
|
127
|
+
assert.deepStrictEqual(res.status.mock.calls[0].arguments, [401]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should return 401 if audience does not match', async () => {
|
|
131
|
+
process.env.OAUTH_ENABLED = 'true';
|
|
132
|
+
process.env.GOOGLE_OAUTH_AUDIENCE = 'expected-audience';
|
|
133
|
+
req.body.method = 'tools/call';
|
|
134
|
+
req.headers.authorization = 'Bearer valid-token-wrong-audience';
|
|
135
|
+
|
|
136
|
+
const mockGetTokenInfo = mock.fn(async () => ({ aud: 'wrong-audience' }));
|
|
137
|
+
const mockGetOAuthClient = mock.fn(async () => ({
|
|
138
|
+
getTokenInfo: mockGetTokenInfo,
|
|
139
|
+
}));
|
|
140
|
+
|
|
141
|
+
const { oauthMiddleware } = await esmock(
|
|
142
|
+
'../../../lib/middleware/oauth.js',
|
|
143
|
+
{
|
|
144
|
+
'../../../lib/clients.js': {
|
|
145
|
+
getOAuthClient: mockGetOAuthClient,
|
|
146
|
+
},
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
await oauthMiddleware(req, res, next);
|
|
151
|
+
|
|
152
|
+
assert.strictEqual(next.mock.callCount(), 0);
|
|
153
|
+
assert.strictEqual(res.status.mock.callCount(), 1);
|
|
154
|
+
assert.deepStrictEqual(res.status.mock.calls[0].arguments, [401]);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { mock } from 'node:test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a mock Cloud Build Client.
|
|
5
|
+
* @param {object} [overrides] - Optional overrides for methods.
|
|
6
|
+
* @returns {object} A mock Cloud Build Client.
|
|
7
|
+
*/
|
|
8
|
+
export function getMockCloudBuildClient(overrides = {}) {
|
|
9
|
+
return {
|
|
10
|
+
createBuild:
|
|
11
|
+
overrides.createBuild ||
|
|
12
|
+
mock.fn(() =>
|
|
13
|
+
Promise.resolve([
|
|
14
|
+
{
|
|
15
|
+
metadata: {
|
|
16
|
+
build: {
|
|
17
|
+
id: 'default-mock-build-id',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
])
|
|
22
|
+
),
|
|
23
|
+
getBuild:
|
|
24
|
+
overrides.getBuild ||
|
|
25
|
+
mock.fn(() =>
|
|
26
|
+
Promise.resolve([
|
|
27
|
+
{
|
|
28
|
+
id: 'default-mock-build-id',
|
|
29
|
+
status: 'SUCCESS',
|
|
30
|
+
},
|
|
31
|
+
])
|
|
32
|
+
),
|
|
33
|
+
...overrides,
|
|
34
|
+
};
|
|
35
|
+
}
|
package/test/local/tools.test.js
CHANGED
|
@@ -42,7 +42,7 @@ describe('registerTools', () => {
|
|
|
42
42
|
{},
|
|
43
43
|
{
|
|
44
44
|
'../../lib/cloud-api/projects.js': {
|
|
45
|
-
listProjects: () =>
|
|
45
|
+
listProjects: (token) =>
|
|
46
46
|
Promise.resolve([{ id: 'project1' }, { id: 'project2' }]),
|
|
47
47
|
},
|
|
48
48
|
}
|
|
@@ -433,7 +433,7 @@ describe('registerTools', () => {
|
|
|
433
433
|
content: [
|
|
434
434
|
{
|
|
435
435
|
type: 'text',
|
|
436
|
-
text: 'GCP credentials are not available. Please configure your environment
|
|
436
|
+
text: 'GCP credentials are not available. Please configure your environment using OAuth or `gcloud auth`.',
|
|
437
437
|
},
|
|
438
438
|
],
|
|
439
439
|
});
|
|
@@ -163,7 +163,7 @@ export async function setSourceDeployProjectPermissions(projectId) {
|
|
|
163
163
|
const context = {
|
|
164
164
|
serviceUsageClient: serviceUsageClient,
|
|
165
165
|
};
|
|
166
|
-
await ensureApisEnabled(
|
|
166
|
+
await ensureApisEnabled(projectId, ['run.googleapis.com']);
|
|
167
167
|
console.log('Adding editor role to Compute SA...');
|
|
168
168
|
const projectNumber = await getProjectNumber(projectId);
|
|
169
169
|
const member = `serviceAccount:${projectNumber}-compute@developer.gserviceaccount.com`;
|
package/tools/register-tools.js
CHANGED
|
@@ -41,7 +41,7 @@ function gcpTool(gcpCredentialsAvailable, fn) {
|
|
|
41
41
|
content: [
|
|
42
42
|
{
|
|
43
43
|
type: 'text',
|
|
44
|
-
text: 'GCP credentials are not available. Please configure your environment
|
|
44
|
+
text: 'GCP credentials are not available. Please configure your environment using OAuth or `gcloud auth`.',
|
|
45
45
|
},
|
|
46
46
|
],
|
|
47
47
|
});
|
|
@@ -59,7 +59,7 @@ function registerListProjectsTool(server, options) {
|
|
|
59
59
|
},
|
|
60
60
|
gcpTool(options.gcpCredentialsAvailable, async () => {
|
|
61
61
|
try {
|
|
62
|
-
const projects = await listProjects();
|
|
62
|
+
const projects = await listProjects(options.accessToken);
|
|
63
63
|
return {
|
|
64
64
|
content: [
|
|
65
65
|
{
|
|
@@ -113,7 +113,11 @@ function registerCreateProjectTool(server, options) {
|
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
115
|
try {
|
|
116
|
-
const result = await createProjectAndAttachBilling(
|
|
116
|
+
const result = await createProjectAndAttachBilling(
|
|
117
|
+
projectId,
|
|
118
|
+
undefined,
|
|
119
|
+
options.accessToken
|
|
120
|
+
);
|
|
117
121
|
return {
|
|
118
122
|
content: [
|
|
119
123
|
{
|
|
@@ -161,7 +165,7 @@ function registerListServicesTool(server, options) {
|
|
|
161
165
|
};
|
|
162
166
|
}
|
|
163
167
|
try {
|
|
164
|
-
const allServices = await listServices(project);
|
|
168
|
+
const allServices = await listServices(project, options.accessToken);
|
|
165
169
|
const content = [];
|
|
166
170
|
for (const region of Object.keys(allServices)) {
|
|
167
171
|
const serviceList = allServices[region];
|
|
@@ -230,7 +234,12 @@ function registerGetServiceTool(server, options) {
|
|
|
230
234
|
};
|
|
231
235
|
}
|
|
232
236
|
try {
|
|
233
|
-
const serviceDetails = await getService(
|
|
237
|
+
const serviceDetails = await getService(
|
|
238
|
+
project,
|
|
239
|
+
region,
|
|
240
|
+
service,
|
|
241
|
+
options.accessToken
|
|
242
|
+
);
|
|
234
243
|
if (serviceDetails) {
|
|
235
244
|
return {
|
|
236
245
|
content: [
|
|
@@ -299,6 +308,7 @@ function registerGetServiceLogTool(server, options) {
|
|
|
299
308
|
project,
|
|
300
309
|
region,
|
|
301
310
|
service,
|
|
311
|
+
options.accessToken,
|
|
302
312
|
requestOptions
|
|
303
313
|
);
|
|
304
314
|
|
|
@@ -394,6 +404,7 @@ function registerDeployLocalFolderTool(server, options) {
|
|
|
394
404
|
region: region,
|
|
395
405
|
files: [folderPath],
|
|
396
406
|
skipIamCheck: options.skipIamCheck, // Pass the new flag
|
|
407
|
+
accessToken: options.accessToken,
|
|
397
408
|
progressCallback,
|
|
398
409
|
});
|
|
399
410
|
return {
|
|
@@ -495,6 +506,7 @@ function registerDeployFileContentsTool(server, options) {
|
|
|
495
506
|
region: region,
|
|
496
507
|
files: files,
|
|
497
508
|
skipIamCheck: options.skipIamCheck, // Pass the new flag
|
|
509
|
+
accessToken: options.accessToken,
|
|
498
510
|
progressCallback,
|
|
499
511
|
});
|
|
500
512
|
return {
|
|
@@ -575,6 +587,7 @@ function registerDeployContainerImageTool(server, options) {
|
|
|
575
587
|
region: region,
|
|
576
588
|
imageUrl: imageUrl,
|
|
577
589
|
skipIamCheck: options.skipIamCheck,
|
|
590
|
+
accessToken: options.accessToken,
|
|
578
591
|
progressCallback,
|
|
579
592
|
});
|
|
580
593
|
return {
|
package/.kokoro/kokoro_build.sh
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Copyright 2022 Google LLC
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
# Code under repo is checked out to ${KOKORO_ARTIFACTS_DIR}/git.
|
|
18
|
-
# The final directory name in this path is determined by the scm name specified
|
|
19
|
-
# in the job configuration.
|
|
20
|
-
|
|
21
|
-
cd "${KOKORO_ARTIFACTS_DIR}/github/cloud-run-mcp"
|
|
22
|
-
.kokoro/run_tests.sh
|
package/.kokoro/presubmit.cfg
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# -*- protobuffer -*-
|
|
2
|
-
# proto-file: google3/devtools/kokoro/config/proto/build.proto
|
|
3
|
-
# proto-message: BuildConfig
|
|
4
|
-
|
|
5
|
-
# Location of the bash script. Should have value <github_scm.name>/<path_from_repository_root>.
|
|
6
|
-
# github_scm.name is specified in the job configuration (next section).
|
|
7
|
-
build_file: "cloud-run-mcp/.kokoro/kokoro_build.sh"
|
|
8
|
-
|
|
9
|
-
container_properties {
|
|
10
|
-
docker_image: "us-central1-docker.pkg.dev/kokoro-container-bakery/kokoro/ubuntu/ubuntu2204/full:next"
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
env_vars {
|
|
14
|
-
key: "GOOGLE_CLOUD_PROJECT"
|
|
15
|
-
value: "cloud-run-mcp-server-testing"
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
env_vars {
|
|
19
|
-
key: "GCP_PARENT"
|
|
20
|
-
value: "folders/138095058155"
|
|
21
|
-
}
|
package/.kokoro/run_tests.sh
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Copyright 2022 Google LLC
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
# Fail on any error.
|
|
18
|
-
set -xe
|
|
19
|
-
|
|
20
|
-
# cd to project root
|
|
21
|
-
cd "$(dirname "$0")"/..
|
|
22
|
-
|
|
23
|
-
# Install dependencies
|
|
24
|
-
npm install
|
|
25
|
-
|
|
26
|
-
# Run tests
|
|
27
|
-
npm run test:workflows # Run tests related to all workflows
|
|
28
|
-
npm run test:projects # Run tests related to GCP projects
|
|
29
|
-
npm run test:services # Run tests related to services
|
|
30
|
-
npm run test:deploy # Run tests related to deployments
|
|
31
|
-
npm run test:gcp-auth # Run tests related to GCP authentication
|