mlgym-deploy 3.3.1 → 3.3.5
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/index.js +27 -18
- package/package.json +1 -4
package/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
9
9
|
import axios from 'axios';
|
|
10
10
|
import fs from 'fs/promises';
|
|
11
|
+
import fsSync from 'fs';
|
|
11
12
|
import path from 'path';
|
|
12
13
|
import os from 'os';
|
|
13
14
|
import { exec } from 'child_process';
|
|
@@ -17,7 +18,7 @@ import crypto from 'crypto';
|
|
|
17
18
|
const execAsync = promisify(exec);
|
|
18
19
|
|
|
19
20
|
// Current version of this MCP server - INCREMENT FOR WORKFLOW FIXES
|
|
20
|
-
const CURRENT_VERSION = '3.3.
|
|
21
|
+
const CURRENT_VERSION = '3.3.5'; // Fixed mlgym_deploy_logs project lookup (data.projects → data) by importing fsSync for synchronous file operations
|
|
21
22
|
const PACKAGE_NAME = 'mlgym-deploy';
|
|
22
23
|
|
|
23
24
|
// Debug logging configuration - ENABLED BY DEFAULT
|
|
@@ -1302,9 +1303,9 @@ async function initProject(args) {
|
|
|
1302
1303
|
|
|
1303
1304
|
for (const filename of composeFiles) {
|
|
1304
1305
|
const composePath = path.join(local_path, filename);
|
|
1305
|
-
if (
|
|
1306
|
+
if (fsSync.existsSync(composePath)) {
|
|
1306
1307
|
try {
|
|
1307
|
-
composeContent =
|
|
1308
|
+
composeContent = fsSync.readFileSync(composePath, 'utf8');
|
|
1308
1309
|
log.info(`MCP >>> [initProject] Read ${filename}: ${composeContent.length} bytes`);
|
|
1309
1310
|
break;
|
|
1310
1311
|
} catch (err) {
|
|
@@ -1363,6 +1364,10 @@ async function initProject(args) {
|
|
|
1363
1364
|
console.error('⚠️ Warning: Failed to add administrator as member:', memberErr.message);
|
|
1364
1365
|
}
|
|
1365
1366
|
|
|
1367
|
+
// Wait for GitLab project to be fully created and accessible
|
|
1368
|
+
console.error('Waiting for GitLab project creation to complete (20 seconds)...');
|
|
1369
|
+
await new Promise(resolve => setTimeout(resolve, 20000));
|
|
1370
|
+
|
|
1366
1371
|
// Initialize local git repository if needed
|
|
1367
1372
|
const absolutePath = path.resolve(local_path);
|
|
1368
1373
|
try {
|
|
@@ -1401,9 +1406,9 @@ async function initProject(args) {
|
|
|
1401
1406
|
console.error('Note: ssh-agent not available, relying on SSH config');
|
|
1402
1407
|
}
|
|
1403
1408
|
|
|
1404
|
-
// Wait for GitLab SSH key propagation (
|
|
1405
|
-
console.error('Waiting for GitLab SSH key propagation (
|
|
1406
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
1409
|
+
// Wait for GitLab SSH key propagation (20 seconds)
|
|
1410
|
+
console.error('Waiting for GitLab SSH key propagation (30 seconds)...');
|
|
1411
|
+
await new Promise(resolve => setTimeout(resolve, 30000));
|
|
1407
1412
|
|
|
1408
1413
|
// Test SSH connectivity
|
|
1409
1414
|
console.error('Testing SSH connection to GitLab...');
|
|
@@ -1472,7 +1477,11 @@ async function initProject(args) {
|
|
|
1472
1477
|
log.debug('MCP >>> [initProject] Git remote URL:', gitUrl);
|
|
1473
1478
|
gitSteps.push('Pushing to GitLab to trigger deployment');
|
|
1474
1479
|
console.error('Executing: git push -u mlgym main');
|
|
1475
|
-
|
|
1480
|
+
const authInfo = await loadAuth();
|
|
1481
|
+
const sanitizedEmail = authInfo.email.replace('@', '_at_').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
1482
|
+
const sshKeyPath = path.join(os.homedir(), '.ssh', `mlgym_${sanitizedEmail}`);
|
|
1483
|
+
const gitSshCmd = `ssh -i "${sshKeyPath}"`;
|
|
1484
|
+
await execAsync('git push -u mlgym main', { cwd: absolutePath, env: { ...process.env, GIT_SSH_COMMAND: gitSshCmd } });
|
|
1476
1485
|
gitSteps.push('✅ Successfully pushed to GitLab');
|
|
1477
1486
|
pushSucceeded = true;
|
|
1478
1487
|
log.success('MCP >>> [initProject] ✅ Git push completed successfully!');
|
|
@@ -1496,7 +1505,7 @@ async function initProject(args) {
|
|
|
1496
1505
|
for (let i = 0; i < 12; i++) {
|
|
1497
1506
|
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5 seconds
|
|
1498
1507
|
|
|
1499
|
-
const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/
|
|
1508
|
+
const statusResult = await apiRequest('GET', `/api/v1/projects/${project.id}/deployments`, null, true);
|
|
1500
1509
|
if (statusResult.success && statusResult.data) {
|
|
1501
1510
|
const status = statusResult.data.status;
|
|
1502
1511
|
console.error(`Deployment status: ${status}`);
|
|
@@ -2014,10 +2023,10 @@ async function deployProject(args) {
|
|
|
2014
2023
|
if (strategy.type === 'docker-compose') {
|
|
2015
2024
|
const composePath = path.join(local_path, 'docker-compose.yml');
|
|
2016
2025
|
const composePathYAML = path.join(local_path, 'docker-compose.yaml');
|
|
2017
|
-
const actualPath =
|
|
2026
|
+
const actualPath = fsSync.existsSync(composePath) ? composePath : composePathYAML;
|
|
2018
2027
|
|
|
2019
|
-
if (
|
|
2020
|
-
const content =
|
|
2028
|
+
if (fsSync.existsSync(actualPath)) {
|
|
2029
|
+
const content = fsSync.readFileSync(actualPath, 'utf8');
|
|
2021
2030
|
const validation = validateAndFixDockerCompose(content);
|
|
2022
2031
|
|
|
2023
2032
|
if (!validation.isValid) {
|
|
@@ -2028,11 +2037,11 @@ async function deployProject(args) {
|
|
|
2028
2037
|
|
|
2029
2038
|
// Auto-fix the issues
|
|
2030
2039
|
log.info('MCP >>> Auto-fixing docker-compose.yml port mappings...');
|
|
2031
|
-
|
|
2040
|
+
fsSync.writeFileSync(actualPath, validation.fixedContent);
|
|
2032
2041
|
|
|
2033
2042
|
// Create backup
|
|
2034
2043
|
const backupPath = actualPath + '.backup';
|
|
2035
|
-
|
|
2044
|
+
fsSync.writeFileSync(backupPath, content);
|
|
2036
2045
|
log.info(`MCP >>> Created backup at ${backupPath}`);
|
|
2037
2046
|
|
|
2038
2047
|
log.success('MCP >>> Fixed docker-compose.yml:');
|
|
@@ -2044,8 +2053,8 @@ async function deployProject(args) {
|
|
|
2044
2053
|
} else if (strategy.type === 'dockerfile') {
|
|
2045
2054
|
const dockerfilePath = path.join(local_path, 'Dockerfile');
|
|
2046
2055
|
|
|
2047
|
-
if (
|
|
2048
|
-
const content =
|
|
2056
|
+
if (fsSync.existsSync(dockerfilePath)) {
|
|
2057
|
+
const content = fsSync.readFileSync(dockerfilePath, 'utf8');
|
|
2049
2058
|
const validation = validateDockerfile(content);
|
|
2050
2059
|
|
|
2051
2060
|
if (!validation.isValid) {
|
|
@@ -2073,8 +2082,8 @@ async function deployProject(args) {
|
|
|
2073
2082
|
const fixedContent = lines.join('\n');
|
|
2074
2083
|
|
|
2075
2084
|
// Create backup
|
|
2076
|
-
|
|
2077
|
-
|
|
2085
|
+
fsSync.writeFileSync(dockerfilePath + '.backup', content);
|
|
2086
|
+
fsSync.writeFileSync(dockerfilePath, fixedContent);
|
|
2078
2087
|
|
|
2079
2088
|
log.success('MCP >>> Fixed Dockerfile: added EXPOSE 80');
|
|
2080
2089
|
}
|
|
@@ -2230,7 +2239,7 @@ async function getDeploymentLogs(args) {
|
|
|
2230
2239
|
throw new Error(`Failed to get projects: ${projectsResult.error}`);
|
|
2231
2240
|
}
|
|
2232
2241
|
|
|
2233
|
-
const project = projectsResult.data
|
|
2242
|
+
const project = projectsResult.data?.find(p => p.name === projectName);
|
|
2234
2243
|
if (!project) {
|
|
2235
2244
|
throw new Error(`Project "${projectName}" not found in your account`);
|
|
2236
2245
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mlgym-deploy",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.5",
|
|
4
4
|
"description": "MCP server for MLGym - Complete deployment management: deploy, configure, monitor, and rollback applications",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -83,6 +83,3 @@
|
|
|
83
83
|
]
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|