greenbump 0.4.0 → 0.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.
- package/CHANGELOG.md +44 -1
- package/action-dist/index.cjs +813 -18
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/engine/git.js +1 -0
- package/dist/engine/git.js.map +1 -1
- package/dist/engine/perf/metrics.js +171 -0
- package/dist/engine/perf/metrics.js.map +1 -0
- package/dist/engine/perf/regression.js +115 -0
- package/dist/engine/perf/regression.js.map +1 -0
- package/dist/engine/run.js +49 -1
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/sandbox/docker-manager.js +215 -0
- package/dist/engine/sandbox/docker-manager.js.map +1 -0
- package/dist/engine/sandbox/orchestrator.js +107 -0
- package/dist/engine/sandbox/orchestrator.js.map +1 -0
- package/dist/engine/sandbox/service-detector.js +147 -0
- package/dist/engine/sandbox/service-detector.js.map +1 -0
- package/dist/engine/sandbox/template-generator.js +241 -0
- package/dist/engine/sandbox/template-generator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { exec } from "../git.js";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { rm } from "fs/promises";
|
|
5
|
+
/**
|
|
6
|
+
* Check if Docker is available and running
|
|
7
|
+
*/
|
|
8
|
+
export async function checkDockerAvailable() {
|
|
9
|
+
const info = {
|
|
10
|
+
available: false,
|
|
11
|
+
composeAvailable: false,
|
|
12
|
+
};
|
|
13
|
+
try {
|
|
14
|
+
const versionResult = await exec("docker", ["--version"], { cwd: process.cwd() });
|
|
15
|
+
info.version = versionResult.stdout.trim();
|
|
16
|
+
// Check if Docker daemon is running
|
|
17
|
+
await exec("docker", ["ps"], { cwd: process.cwd() });
|
|
18
|
+
info.available = true;
|
|
19
|
+
// Check docker-compose availability
|
|
20
|
+
try {
|
|
21
|
+
await exec("docker-compose", ["--version"], { cwd: process.cwd() });
|
|
22
|
+
info.composeAvailable = true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// docker-compose not available, but that's okay
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Docker not available
|
|
30
|
+
}
|
|
31
|
+
return info;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build Docker image from Dockerfile
|
|
35
|
+
*/
|
|
36
|
+
export async function buildDockerImage(cwd, imageName, dockerfilePath) {
|
|
37
|
+
console.log(`🐳 Building Docker image: ${imageName}...`);
|
|
38
|
+
await exec("docker", [
|
|
39
|
+
"build",
|
|
40
|
+
"-t", imageName,
|
|
41
|
+
"-f", dockerfilePath,
|
|
42
|
+
".",
|
|
43
|
+
], { cwd });
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Run verification in Docker container
|
|
47
|
+
*/
|
|
48
|
+
export async function runVerificationInContainer(cwd, options) {
|
|
49
|
+
const args = [
|
|
50
|
+
"run",
|
|
51
|
+
"--rm",
|
|
52
|
+
"-v", `${cwd}:/workspace`,
|
|
53
|
+
"-w", "/workspace",
|
|
54
|
+
];
|
|
55
|
+
// Add network mode
|
|
56
|
+
if (options.networkMode) {
|
|
57
|
+
args.push("--network", options.networkMode);
|
|
58
|
+
}
|
|
59
|
+
// Add environment variables
|
|
60
|
+
if (options.env) {
|
|
61
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
62
|
+
args.push("-e", `${key}=${value}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
args.push(options.image);
|
|
66
|
+
// Run the container with test command
|
|
67
|
+
args.push("sh", "-c", "npm install && npm run build 2>&1 && npm test 2>&1");
|
|
68
|
+
let exitCode = 0;
|
|
69
|
+
let stdout = "";
|
|
70
|
+
let stderr = "";
|
|
71
|
+
let buildSuccess = false;
|
|
72
|
+
let testsPassed = false;
|
|
73
|
+
try {
|
|
74
|
+
const result = await exec("docker", args, {
|
|
75
|
+
cwd,
|
|
76
|
+
timeout: options.timeout || 600000, // 10 minutes default
|
|
77
|
+
});
|
|
78
|
+
stdout = result.stdout;
|
|
79
|
+
stderr = result.stderr;
|
|
80
|
+
exitCode = 0;
|
|
81
|
+
buildSuccess = true;
|
|
82
|
+
testsPassed = true;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
exitCode = error.exitCode || 1;
|
|
86
|
+
stdout = error.stdout || "";
|
|
87
|
+
stderr = error.stderr || "";
|
|
88
|
+
// Determine what failed
|
|
89
|
+
buildSuccess = !stderr.includes("npm run build") && !stdout.includes("build failed");
|
|
90
|
+
testsPassed = false;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
exitCode,
|
|
94
|
+
stdout,
|
|
95
|
+
stderr,
|
|
96
|
+
buildSuccess,
|
|
97
|
+
testsPassed,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Start docker-compose services
|
|
102
|
+
*/
|
|
103
|
+
export async function startComposeServices(cwd, composeFile) {
|
|
104
|
+
console.log("🚀 Starting docker-compose services...");
|
|
105
|
+
const projectName = `greenbump-${Date.now()}`;
|
|
106
|
+
await exec("docker-compose", [
|
|
107
|
+
"-f", composeFile,
|
|
108
|
+
"-p", projectName,
|
|
109
|
+
"up", "-d",
|
|
110
|
+
], { cwd });
|
|
111
|
+
return projectName;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if a service is healthy
|
|
115
|
+
*/
|
|
116
|
+
export async function checkServiceHealth(projectName, serviceName) {
|
|
117
|
+
try {
|
|
118
|
+
const result = await exec("docker-compose", ["-p", projectName, "ps", "--filter", `name=${serviceName}`, "--format", "json"], { cwd: process.cwd() });
|
|
119
|
+
const output = result.stdout.trim();
|
|
120
|
+
if (!output)
|
|
121
|
+
return false;
|
|
122
|
+
// Check if service is running
|
|
123
|
+
return output.includes('"State":"running"') || output.includes('healthy');
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Wait for services to become healthy
|
|
131
|
+
*/
|
|
132
|
+
export async function waitForServices(projectName, services, timeout = 60) {
|
|
133
|
+
console.log("⏳ Waiting for services to become healthy...");
|
|
134
|
+
const deadline = Date.now() + timeout * 1000;
|
|
135
|
+
const healthyServices = new Set();
|
|
136
|
+
while (Date.now() < deadline && healthyServices.size < services.length) {
|
|
137
|
+
for (const service of services) {
|
|
138
|
+
if (healthyServices.has(service))
|
|
139
|
+
continue;
|
|
140
|
+
const healthy = await checkServiceHealth(projectName, service);
|
|
141
|
+
if (healthy) {
|
|
142
|
+
console.log(`✓ ${service} is healthy`);
|
|
143
|
+
healthyServices.add(service);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (healthyServices.size < services.length) {
|
|
147
|
+
await sleep(2000); // Wait 2 seconds before checking again
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (healthyServices.size < services.length) {
|
|
151
|
+
const unhealthy = services.filter(s => !healthyServices.has(s));
|
|
152
|
+
throw new Error(`Timeout waiting for services to become healthy: ${unhealthy.join(", ")}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Stop and remove docker-compose services
|
|
157
|
+
*/
|
|
158
|
+
export async function stopComposeServices(cwd, composeFile, projectName) {
|
|
159
|
+
console.log("🛑 Stopping docker-compose services...");
|
|
160
|
+
try {
|
|
161
|
+
await exec("docker-compose", [
|
|
162
|
+
"-f", composeFile,
|
|
163
|
+
"-p", projectName,
|
|
164
|
+
"down", "-v",
|
|
165
|
+
], { cwd });
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
console.warn("Warning: Failed to stop compose services:", error);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Remove Docker image
|
|
173
|
+
*/
|
|
174
|
+
export async function removeDockerImage(imageName) {
|
|
175
|
+
try {
|
|
176
|
+
await exec("docker", ["rmi", imageName], { cwd: process.cwd() });
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Ignore errors when removing image
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Cleanup Docker resources
|
|
184
|
+
*/
|
|
185
|
+
export async function cleanupDocker(cwd, imageName, composeFile, projectName) {
|
|
186
|
+
console.log("🧹 Cleaning up Docker resources...");
|
|
187
|
+
// Stop compose services
|
|
188
|
+
if (composeFile && projectName) {
|
|
189
|
+
await stopComposeServices(cwd, composeFile, projectName);
|
|
190
|
+
}
|
|
191
|
+
// Remove image
|
|
192
|
+
await removeDockerImage(imageName);
|
|
193
|
+
// Remove temporary files
|
|
194
|
+
const tempFiles = [
|
|
195
|
+
join(cwd, ".greenbump-Dockerfile"),
|
|
196
|
+
join(cwd, ".greenbump-compose.yml"),
|
|
197
|
+
];
|
|
198
|
+
for (const file of tempFiles) {
|
|
199
|
+
try {
|
|
200
|
+
if (existsSync(file)) {
|
|
201
|
+
await rm(file);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// Ignore cleanup errors
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Utility: sleep
|
|
211
|
+
*/
|
|
212
|
+
function sleep(ms) {
|
|
213
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=docker-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker-manager.js","sourceRoot":"","sources":["../../../src/engine/sandbox/docker-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { writeFile } from "fs/promises";
|
|
3
|
+
import { checkDockerAvailable, buildDockerImage, runVerificationInContainer, startComposeServices, waitForServices, cleanupDocker, } from "./docker-manager.js";
|
|
4
|
+
import { detectProjectType, generateDockerfile, generateComposeFile, } from "./template-generator.js";
|
|
5
|
+
import { detectRequiredServices, getServiceDisplayName, } from "./service-detector.js";
|
|
6
|
+
/**
|
|
7
|
+
* Run verification in isolated Docker sandbox
|
|
8
|
+
*/
|
|
9
|
+
export async function runInSandbox(cwd, options) {
|
|
10
|
+
// Check if Docker is available
|
|
11
|
+
const dockerInfo = await checkDockerAvailable();
|
|
12
|
+
if (!dockerInfo.available) {
|
|
13
|
+
console.warn("⚠️ Docker not available, skipping sandbox verification");
|
|
14
|
+
console.warn(" Install Docker for isolated testing: https://docs.docker.com/get-docker/");
|
|
15
|
+
return {
|
|
16
|
+
passed: false,
|
|
17
|
+
buildSuccess: false,
|
|
18
|
+
testsPassed: false,
|
|
19
|
+
services: [],
|
|
20
|
+
logs: "",
|
|
21
|
+
exitCode: -1,
|
|
22
|
+
skipped: true,
|
|
23
|
+
skipReason: "docker_unavailable",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
console.log("🐳 Starting sandbox environment...");
|
|
27
|
+
console.log(` Docker version: ${dockerInfo.version}`);
|
|
28
|
+
// Detect project type
|
|
29
|
+
const projectType = await detectProjectType(cwd);
|
|
30
|
+
console.log(` Project type: ${projectType}`);
|
|
31
|
+
// Detect or use specified services
|
|
32
|
+
const services = options.services || await detectRequiredServices(cwd);
|
|
33
|
+
if (services.length > 0) {
|
|
34
|
+
console.log(` Required services: ${services.map(getServiceDisplayName).join(", ")}`);
|
|
35
|
+
}
|
|
36
|
+
const imageName = options.dockerImage || "greenbump-verify";
|
|
37
|
+
const dockerfilePath = join(cwd, ".greenbump-Dockerfile");
|
|
38
|
+
const composeFilePath = join(cwd, ".greenbump-compose.yml");
|
|
39
|
+
let projectName;
|
|
40
|
+
const serviceHealths = [];
|
|
41
|
+
try {
|
|
42
|
+
// Generate Dockerfile
|
|
43
|
+
const dockerfile = await generateDockerfile(projectType, cwd);
|
|
44
|
+
await writeFile(dockerfilePath, dockerfile);
|
|
45
|
+
// Generate docker-compose.yml if services needed
|
|
46
|
+
let composeFile = "";
|
|
47
|
+
if (services.length > 0) {
|
|
48
|
+
composeFile = await generateComposeFile(services);
|
|
49
|
+
if (composeFile) {
|
|
50
|
+
await writeFile(composeFilePath, composeFile);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Build Docker image
|
|
54
|
+
await buildDockerImage(cwd, imageName, dockerfilePath);
|
|
55
|
+
// Start services if needed
|
|
56
|
+
if (services.length > 0 && composeFile && dockerInfo.composeAvailable) {
|
|
57
|
+
projectName = await startComposeServices(cwd, composeFilePath);
|
|
58
|
+
// Wait for services to be healthy
|
|
59
|
+
await waitForServices(projectName, services, options.timeout ? options.timeout / 1000 : 60);
|
|
60
|
+
// Record service health
|
|
61
|
+
for (const service of services) {
|
|
62
|
+
serviceHealths.push({
|
|
63
|
+
name: service,
|
|
64
|
+
healthy: true,
|
|
65
|
+
displayName: getServiceDisplayName(service),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (services.length > 0 && !dockerInfo.composeAvailable) {
|
|
70
|
+
console.warn("⚠️ docker-compose not available, skipping service startup");
|
|
71
|
+
}
|
|
72
|
+
// Run verification in container
|
|
73
|
+
console.log("🔍 Running verification in container...");
|
|
74
|
+
const networkMode = projectName ? `${projectName}_default` : "bridge";
|
|
75
|
+
const result = await runVerificationInContainer(cwd, {
|
|
76
|
+
image: imageName,
|
|
77
|
+
networkMode,
|
|
78
|
+
timeout: options.timeout,
|
|
79
|
+
});
|
|
80
|
+
// Display results
|
|
81
|
+
if (result.exitCode === 0) {
|
|
82
|
+
console.log("✅ Verification passed in sandbox");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error("❌ Verification failed in sandbox");
|
|
86
|
+
console.error(result.stderr);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
passed: result.exitCode === 0,
|
|
90
|
+
buildSuccess: result.buildSuccess,
|
|
91
|
+
testsPassed: result.testsPassed,
|
|
92
|
+
services: serviceHealths,
|
|
93
|
+
logs: result.stdout + "\n" + result.stderr,
|
|
94
|
+
exitCode: result.exitCode,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
// Cleanup unless keepContainer is true
|
|
99
|
+
if (!options.keepContainer) {
|
|
100
|
+
await cleanupDocker(cwd, imageName, composeFilePath, projectName);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.log("🔧 Container kept for debugging (use --keep-container=false to cleanup)");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../../../src/engine/sandbox/orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
/**
|
|
5
|
+
* Detect required database/cache services for the project
|
|
6
|
+
*/
|
|
7
|
+
export async function detectRequiredServices(cwd) {
|
|
8
|
+
const services = new Set();
|
|
9
|
+
// Check package.json dependencies
|
|
10
|
+
await detectFromPackageJson(cwd, services);
|
|
11
|
+
// Check docker-compose files
|
|
12
|
+
await detectFromDockerCompose(cwd, services);
|
|
13
|
+
// Check environment files
|
|
14
|
+
await detectFromEnvFiles(cwd, services);
|
|
15
|
+
return Array.from(services);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Detect services from package.json dependencies
|
|
19
|
+
*/
|
|
20
|
+
async function detectFromPackageJson(cwd, services) {
|
|
21
|
+
const pkgPath = join(cwd, "package.json");
|
|
22
|
+
if (!existsSync(pkgPath))
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
26
|
+
const pkg = JSON.parse(content);
|
|
27
|
+
const allDeps = {
|
|
28
|
+
...pkg.dependencies,
|
|
29
|
+
...pkg.devDependencies,
|
|
30
|
+
};
|
|
31
|
+
// PostgreSQL
|
|
32
|
+
if (allDeps.pg || allDeps["pg-promise"] || allDeps.postgres || allDeps.sequelize) {
|
|
33
|
+
services.add("postgres");
|
|
34
|
+
}
|
|
35
|
+
// MySQL
|
|
36
|
+
if (allDeps.mysql || allDeps.mysql2) {
|
|
37
|
+
services.add("mysql");
|
|
38
|
+
}
|
|
39
|
+
// Redis
|
|
40
|
+
if (allDeps.redis || allDeps.ioredis) {
|
|
41
|
+
services.add("redis");
|
|
42
|
+
}
|
|
43
|
+
// MongoDB
|
|
44
|
+
if (allDeps.mongodb || allDeps.mongoose) {
|
|
45
|
+
services.add("mongodb");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore errors reading package.json
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Detect services from docker-compose files
|
|
54
|
+
*/
|
|
55
|
+
async function detectFromDockerCompose(cwd, services) {
|
|
56
|
+
const composeFiles = [
|
|
57
|
+
"docker-compose.yml",
|
|
58
|
+
"docker-compose.yaml",
|
|
59
|
+
"docker-compose.test.yml",
|
|
60
|
+
"docker-compose.test.yaml",
|
|
61
|
+
];
|
|
62
|
+
for (const file of composeFiles) {
|
|
63
|
+
const composePath = join(cwd, file);
|
|
64
|
+
if (!existsSync(composePath))
|
|
65
|
+
continue;
|
|
66
|
+
try {
|
|
67
|
+
const content = await readFile(composePath, "utf-8");
|
|
68
|
+
// Simple text search for service images
|
|
69
|
+
if (/image:\s*postgres/i.test(content)) {
|
|
70
|
+
services.add("postgres");
|
|
71
|
+
}
|
|
72
|
+
if (/image:\s*mysql/i.test(content)) {
|
|
73
|
+
services.add("mysql");
|
|
74
|
+
}
|
|
75
|
+
if (/image:\s*redis/i.test(content)) {
|
|
76
|
+
services.add("redis");
|
|
77
|
+
}
|
|
78
|
+
if (/image:\s*mongo/i.test(content)) {
|
|
79
|
+
services.add("mongodb");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// Ignore errors reading compose files
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Detect services from .env files
|
|
89
|
+
*/
|
|
90
|
+
async function detectFromEnvFiles(cwd, services) {
|
|
91
|
+
const envFiles = [
|
|
92
|
+
".env",
|
|
93
|
+
".env.test",
|
|
94
|
+
".env.local",
|
|
95
|
+
".env.development",
|
|
96
|
+
];
|
|
97
|
+
for (const file of envFiles) {
|
|
98
|
+
const envPath = join(cwd, file);
|
|
99
|
+
if (!existsSync(envPath))
|
|
100
|
+
continue;
|
|
101
|
+
try {
|
|
102
|
+
const content = await readFile(envPath, "utf-8");
|
|
103
|
+
// Check for database connection strings
|
|
104
|
+
if (/postgres:\/\//i.test(content) || /postgresql:\/\//i.test(content)) {
|
|
105
|
+
services.add("postgres");
|
|
106
|
+
}
|
|
107
|
+
if (/mysql:\/\//i.test(content)) {
|
|
108
|
+
services.add("mysql");
|
|
109
|
+
}
|
|
110
|
+
if (/redis:\/\//i.test(content)) {
|
|
111
|
+
services.add("redis");
|
|
112
|
+
}
|
|
113
|
+
if (/mongodb:\/\//i.test(content)) {
|
|
114
|
+
services.add("mongodb");
|
|
115
|
+
}
|
|
116
|
+
// Check for database host environment variables
|
|
117
|
+
if (/POSTGRES_HOST|PGHOST|DATABASE_URL.*postgres/i.test(content)) {
|
|
118
|
+
services.add("postgres");
|
|
119
|
+
}
|
|
120
|
+
if (/MYSQL_HOST|DATABASE_URL.*mysql/i.test(content)) {
|
|
121
|
+
services.add("mysql");
|
|
122
|
+
}
|
|
123
|
+
if (/REDIS_HOST|REDIS_URL/i.test(content)) {
|
|
124
|
+
services.add("redis");
|
|
125
|
+
}
|
|
126
|
+
if (/MONGO_HOST|MONGODB_URL|MONGO_URL/i.test(content)) {
|
|
127
|
+
services.add("mongodb");
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// Ignore errors reading env files
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Get display name for service
|
|
137
|
+
*/
|
|
138
|
+
export function getServiceDisplayName(service) {
|
|
139
|
+
const names = {
|
|
140
|
+
postgres: "PostgreSQL",
|
|
141
|
+
mysql: "MySQL",
|
|
142
|
+
redis: "Redis",
|
|
143
|
+
mongodb: "MongoDB",
|
|
144
|
+
};
|
|
145
|
+
return names[service] || service;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=service-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-detector.js","sourceRoot":"","sources":["../../../src/engine/sandbox/service-detector.ts"],"names":[],"mappings":"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"}
|