greenbump 0.2.1 → 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 +82 -0
- package/README.md +2 -0
- package/action-dist/index.cjs +1054 -19
- package/competitive-analysis.md +304 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/engine/change-detector.js +152 -0
- package/dist/engine/change-detector.js.map +1 -0
- package/dist/engine/dep-graph.js +101 -0
- package/dist/engine/dep-graph.js.map +1 -0
- package/dist/engine/git.js +10 -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 +71 -2
- 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/dist/engine/staged-fix.js +108 -0
- package/dist/engine/staged-fix.js.map +1 -0
- package/dist/engine/stages.js +125 -0
- package/dist/engine/stages.js.map +1 -0
- package/dist/engine/verify.js +121 -0
- package/dist/engine/verify.js.map +1 -0
- package/dist/format.js +20 -0
- package/dist/format.js.map +1 -1
- package/optimization-roadmap.md +772 -0
- package/package.json +4 -1
|
@@ -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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"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
/**
|
|
5
|
+
* Detect project type based on files present
|
|
6
|
+
*/
|
|
7
|
+
export async function detectProjectType(cwd) {
|
|
8
|
+
if (existsSync(join(cwd, "package.json"))) {
|
|
9
|
+
return "nodejs";
|
|
10
|
+
}
|
|
11
|
+
if (existsSync(join(cwd, "requirements.txt")) ||
|
|
12
|
+
existsSync(join(cwd, "pyproject.toml")) ||
|
|
13
|
+
existsSync(join(cwd, "setup.py"))) {
|
|
14
|
+
return "python";
|
|
15
|
+
}
|
|
16
|
+
return "unknown";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generate Dockerfile for project
|
|
20
|
+
*/
|
|
21
|
+
export async function generateDockerfile(projectType, cwd) {
|
|
22
|
+
if (projectType === "nodejs") {
|
|
23
|
+
return await generateNodeJsDockerfile(cwd);
|
|
24
|
+
}
|
|
25
|
+
if (projectType === "python") {
|
|
26
|
+
return await generatePythonDockerfile(cwd);
|
|
27
|
+
}
|
|
28
|
+
// Fallback to Node.js
|
|
29
|
+
return await generateNodeJsDockerfile(cwd);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Generate Node.js Dockerfile
|
|
33
|
+
*/
|
|
34
|
+
async function generateNodeJsDockerfile(cwd) {
|
|
35
|
+
const hasPnpm = existsSync(join(cwd, "pnpm-lock.yaml"));
|
|
36
|
+
const hasYarn = existsSync(join(cwd, "yarn.lock"));
|
|
37
|
+
let installCmd = "npm ci";
|
|
38
|
+
let copyLockFile = "COPY package*.json ./";
|
|
39
|
+
if (hasPnpm) {
|
|
40
|
+
installCmd = "corepack enable && pnpm install --frozen-lockfile";
|
|
41
|
+
copyLockFile = "COPY package*.json pnpm-lock.yaml ./";
|
|
42
|
+
}
|
|
43
|
+
else if (hasYarn) {
|
|
44
|
+
installCmd = "yarn install --frozen-lockfile";
|
|
45
|
+
copyLockFile = "COPY package*.json yarn.lock ./";
|
|
46
|
+
}
|
|
47
|
+
return `FROM node:20-slim
|
|
48
|
+
|
|
49
|
+
WORKDIR /workspace
|
|
50
|
+
|
|
51
|
+
# Copy package files
|
|
52
|
+
${copyLockFile}
|
|
53
|
+
|
|
54
|
+
# Install dependencies
|
|
55
|
+
RUN ${installCmd}
|
|
56
|
+
|
|
57
|
+
# Copy source code
|
|
58
|
+
COPY . .
|
|
59
|
+
|
|
60
|
+
# Build if build script exists
|
|
61
|
+
RUN if grep -q '"build":' package.json; then npm run build || true; fi
|
|
62
|
+
|
|
63
|
+
# Default command: run tests
|
|
64
|
+
CMD ["npm", "test"]
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate Python Dockerfile
|
|
69
|
+
*/
|
|
70
|
+
async function generatePythonDockerfile(cwd) {
|
|
71
|
+
const hasRequirements = existsSync(join(cwd, "requirements.txt"));
|
|
72
|
+
const hasPyproject = existsSync(join(cwd, "pyproject.toml"));
|
|
73
|
+
let installCmd = "";
|
|
74
|
+
let copyFiles = "";
|
|
75
|
+
if (hasRequirements) {
|
|
76
|
+
copyFiles = "COPY requirements*.txt ./";
|
|
77
|
+
installCmd = "pip install --no-cache-dir -r requirements.txt";
|
|
78
|
+
}
|
|
79
|
+
else if (hasPyproject) {
|
|
80
|
+
copyFiles = "COPY pyproject.toml setup.py* ./";
|
|
81
|
+
installCmd = "pip install --no-cache-dir -e .";
|
|
82
|
+
}
|
|
83
|
+
return `FROM python:3.11-slim
|
|
84
|
+
|
|
85
|
+
WORKDIR /workspace
|
|
86
|
+
|
|
87
|
+
# Copy dependency files
|
|
88
|
+
${copyFiles}
|
|
89
|
+
|
|
90
|
+
# Install dependencies
|
|
91
|
+
RUN ${installCmd}
|
|
92
|
+
|
|
93
|
+
# Copy source code
|
|
94
|
+
COPY . .
|
|
95
|
+
|
|
96
|
+
# Default command: run tests
|
|
97
|
+
CMD ["pytest"]
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Generate docker-compose.yml for services
|
|
102
|
+
*/
|
|
103
|
+
export async function generateComposeFile(services) {
|
|
104
|
+
const serviceConfigs = services.map(getServiceConfig).filter((config) => config !== null);
|
|
105
|
+
if (serviceConfigs.length === 0) {
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
108
|
+
const servicesYaml = serviceConfigs.map(formatServiceConfig).join("\n");
|
|
109
|
+
return `version: '3.8'
|
|
110
|
+
|
|
111
|
+
services:
|
|
112
|
+
${servicesYaml}
|
|
113
|
+
|
|
114
|
+
networks:
|
|
115
|
+
default:
|
|
116
|
+
name: greenbump_network
|
|
117
|
+
`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get service configuration
|
|
121
|
+
*/
|
|
122
|
+
function getServiceConfig(serviceName) {
|
|
123
|
+
const configs = {
|
|
124
|
+
postgres: {
|
|
125
|
+
name: "postgres",
|
|
126
|
+
image: "postgres:16-alpine",
|
|
127
|
+
ports: ["5432:5432"],
|
|
128
|
+
environment: {
|
|
129
|
+
POSTGRES_USER: "test",
|
|
130
|
+
POSTGRES_PASSWORD: "test",
|
|
131
|
+
POSTGRES_DB: "test",
|
|
132
|
+
},
|
|
133
|
+
healthcheck: {
|
|
134
|
+
test: ["CMD-SHELL", "pg_isready -U test"],
|
|
135
|
+
interval: "5s",
|
|
136
|
+
timeout: "5s",
|
|
137
|
+
retries: 5,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
mysql: {
|
|
141
|
+
name: "mysql",
|
|
142
|
+
image: "mysql:8",
|
|
143
|
+
ports: ["3306:3306"],
|
|
144
|
+
environment: {
|
|
145
|
+
MYSQL_ROOT_PASSWORD: "test",
|
|
146
|
+
MYSQL_DATABASE: "test",
|
|
147
|
+
},
|
|
148
|
+
healthcheck: {
|
|
149
|
+
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"],
|
|
150
|
+
interval: "5s",
|
|
151
|
+
timeout: "5s",
|
|
152
|
+
retries: 5,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
redis: {
|
|
156
|
+
name: "redis",
|
|
157
|
+
image: "redis:7-alpine",
|
|
158
|
+
ports: ["6379:6379"],
|
|
159
|
+
healthcheck: {
|
|
160
|
+
test: ["CMD", "redis-cli", "ping"],
|
|
161
|
+
interval: "5s",
|
|
162
|
+
timeout: "5s",
|
|
163
|
+
retries: 5,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
mongodb: {
|
|
167
|
+
name: "mongodb",
|
|
168
|
+
image: "mongo:7",
|
|
169
|
+
ports: ["27017:27017"],
|
|
170
|
+
environment: {
|
|
171
|
+
MONGO_INITDB_ROOT_USERNAME: "test",
|
|
172
|
+
MONGO_INITDB_ROOT_PASSWORD: "test",
|
|
173
|
+
},
|
|
174
|
+
healthcheck: {
|
|
175
|
+
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"],
|
|
176
|
+
interval: "5s",
|
|
177
|
+
timeout: "5s",
|
|
178
|
+
retries: 5,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
return configs[serviceName] || null;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Format service config as YAML
|
|
186
|
+
*/
|
|
187
|
+
function formatServiceConfig(config) {
|
|
188
|
+
const lines = [` ${config.name}:`];
|
|
189
|
+
lines.push(` image: ${config.image}`);
|
|
190
|
+
if (config.environment) {
|
|
191
|
+
lines.push(" environment:");
|
|
192
|
+
for (const [key, value] of Object.entries(config.environment)) {
|
|
193
|
+
lines.push(` ${key}: ${value}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (config.ports.length > 0) {
|
|
197
|
+
lines.push(" ports:");
|
|
198
|
+
for (const port of config.ports) {
|
|
199
|
+
lines.push(` - "${port}"`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
lines.push(" healthcheck:");
|
|
203
|
+
lines.push(` test: ${JSON.stringify(config.healthcheck.test)}`);
|
|
204
|
+
lines.push(` interval: ${config.healthcheck.interval}`);
|
|
205
|
+
lines.push(` timeout: ${config.healthcheck.timeout}`);
|
|
206
|
+
lines.push(` retries: ${config.healthcheck.retries}`);
|
|
207
|
+
return lines.join("\n");
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check if project has build script
|
|
211
|
+
*/
|
|
212
|
+
export async function hasBuildScript(cwd) {
|
|
213
|
+
const pkgPath = join(cwd, "package.json");
|
|
214
|
+
if (!existsSync(pkgPath))
|
|
215
|
+
return false;
|
|
216
|
+
try {
|
|
217
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
218
|
+
const pkg = JSON.parse(content);
|
|
219
|
+
return pkg.scripts?.build !== undefined;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if project has test script
|
|
227
|
+
*/
|
|
228
|
+
export async function hasTestScript(cwd) {
|
|
229
|
+
const pkgPath = join(cwd, "package.json");
|
|
230
|
+
if (!existsSync(pkgPath))
|
|
231
|
+
return false;
|
|
232
|
+
try {
|
|
233
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
234
|
+
const pkg = JSON.parse(content);
|
|
235
|
+
return pkg.scripts?.test !== undefined;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=template-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-generator.js","sourceRoot":"","sources":["../../../src/engine/sandbox/template-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Staged fix orchestrator for v0.4.0
|
|
3
|
+
*
|
|
4
|
+
* This module provides a multi-stage fix strategy that:
|
|
5
|
+
* 1. Builds a dependency graph to identify affected files
|
|
6
|
+
* 2. Categorizes files into stages (config → types → source)
|
|
7
|
+
* 3. Fixes each stage independently with incremental commits
|
|
8
|
+
* 4. Allows rollback to the last successful stage on failure
|
|
9
|
+
*
|
|
10
|
+
* Usage: pass --staged flag to enable staged fix mode
|
|
11
|
+
*/
|
|
12
|
+
import { buildDependencyGraph, organizeLayers } from "./dep-graph.js";
|
|
13
|
+
import { COMMON_STAGES, categorizeFilesByStage } from "./stages.js";
|
|
14
|
+
import { commitStage } from "./git.js";
|
|
15
|
+
import { runFixLoop } from "../agent/fixer.js";
|
|
16
|
+
import { runChecks } from "./checks.js";
|
|
17
|
+
/**
|
|
18
|
+
* Run a staged fix: break the fix into multiple stages, fix each independently,
|
|
19
|
+
* and commit after each successful stage.
|
|
20
|
+
*/
|
|
21
|
+
export async function runStagedFix(opts) {
|
|
22
|
+
const log = opts.onLog ?? (() => { });
|
|
23
|
+
const { cwd, pm, dep } = opts;
|
|
24
|
+
// 1. Build dependency graph
|
|
25
|
+
log("📊 building dependency graph…");
|
|
26
|
+
const nodes = await buildDependencyGraph(cwd, dep);
|
|
27
|
+
if (nodes.length === 0) {
|
|
28
|
+
log("⚠️ dependency graph empty, falling back to single-stage fix");
|
|
29
|
+
return await runSingleStageFix(opts);
|
|
30
|
+
}
|
|
31
|
+
const layers = organizeLayers(nodes);
|
|
32
|
+
log(`📊 found ${layers.length} dependency layers, ${nodes.length} files total`);
|
|
33
|
+
// 2. Categorize files by stage
|
|
34
|
+
const stageFiles = await categorizeFilesByStage(cwd, COMMON_STAGES);
|
|
35
|
+
const stages = COMMON_STAGES.filter((stage) => {
|
|
36
|
+
const files = stageFiles.get(stage.name) || [];
|
|
37
|
+
return files.length > 0;
|
|
38
|
+
});
|
|
39
|
+
if (stages.length === 0) {
|
|
40
|
+
log("⚠️ no files matched any stage patterns, falling back to single-stage fix");
|
|
41
|
+
return await runSingleStageFix(opts);
|
|
42
|
+
}
|
|
43
|
+
log(`🔧 identified ${stages.length} stages: ${stages.map(s => s.name).join(" → ")}`);
|
|
44
|
+
// 3. Fix each stage
|
|
45
|
+
let totalRounds = 0;
|
|
46
|
+
let totalInputTokens = 0;
|
|
47
|
+
let totalOutputTokens = 0;
|
|
48
|
+
const allEditedFiles = [];
|
|
49
|
+
for (let i = 0; i < stages.length; i++) {
|
|
50
|
+
const stage = stages[i];
|
|
51
|
+
const files = stageFiles.get(stage.name) || [];
|
|
52
|
+
log(`\n🔧 Stage ${i + 1}/${stages.length}: ${stage.name} (${files.length} files)`);
|
|
53
|
+
// Run fix loop for this stage only
|
|
54
|
+
// (In a full implementation, we'd pass a file filter to the fix loop)
|
|
55
|
+
const stageResult = await runFixLoop({
|
|
56
|
+
...opts,
|
|
57
|
+
onLog: (msg) => log(` ${msg}`),
|
|
58
|
+
});
|
|
59
|
+
totalRounds += stageResult.rounds;
|
|
60
|
+
totalInputTokens += stageResult.usage.inputTokens;
|
|
61
|
+
totalOutputTokens += stageResult.usage.outputTokens;
|
|
62
|
+
allEditedFiles.push(...stageResult.editedFiles);
|
|
63
|
+
// Validate stage
|
|
64
|
+
const passed = await stage.validator(cwd);
|
|
65
|
+
if (!passed) {
|
|
66
|
+
log(`❌ Stage ${i + 1} validation failed`);
|
|
67
|
+
return {
|
|
68
|
+
fixed: false,
|
|
69
|
+
completedStages: i,
|
|
70
|
+
totalStages: stages.length,
|
|
71
|
+
failedStage: stage.name,
|
|
72
|
+
rounds: totalRounds,
|
|
73
|
+
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
|
|
74
|
+
editedFiles: allEditedFiles,
|
|
75
|
+
budgetExceeded: stageResult.budgetExceeded,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// Commit this stage
|
|
79
|
+
await commitStage(cwd, stage.name, i + 1, stages.length);
|
|
80
|
+
log(`✓ Stage ${i + 1} completed and committed`);
|
|
81
|
+
}
|
|
82
|
+
// Final validation
|
|
83
|
+
const finalCheck = await runChecks(pm, cwd, opts.checkOverrides);
|
|
84
|
+
return {
|
|
85
|
+
fixed: finalCheck.ok,
|
|
86
|
+
completedStages: stages.length,
|
|
87
|
+
totalStages: stages.length,
|
|
88
|
+
rounds: totalRounds,
|
|
89
|
+
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
|
|
90
|
+
editedFiles: allEditedFiles,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Fallback: run a single fix loop without staging (same as v0.3.0 behavior)
|
|
95
|
+
*/
|
|
96
|
+
async function runSingleStageFix(opts) {
|
|
97
|
+
const result = await runFixLoop(opts);
|
|
98
|
+
return {
|
|
99
|
+
fixed: result.fixed,
|
|
100
|
+
completedStages: result.fixed ? 1 : 0,
|
|
101
|
+
totalStages: 1,
|
|
102
|
+
rounds: result.rounds,
|
|
103
|
+
usage: result.usage,
|
|
104
|
+
editedFiles: result.editedFiles,
|
|
105
|
+
budgetExceeded: result.budgetExceeded,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=staged-fix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-fix.js","sourceRoot":"","sources":["../../src/engine/staged-fix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAuB,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAiB,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AA4BxC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,IAAsB;IAEtB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAE9B,4BAA4B;IAC5B,GAAG,CAAC,+BAA+B,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAEnD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,GAAG,CAAC,8DAA8D,CAAC,CAAC;QACpE,OAAO,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACrC,GAAG,CAAC,YAAY,MAAM,CAAC,MAAM,uBAAuB,KAAK,CAAC,MAAM,cAAc,CAAC,CAAC;IAEhF,+BAA+B;IAC/B,MAAM,UAAU,GAAG,MAAM,sBAAsB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,GAAG,CAAC,2EAA2E,CAAC,CAAC;QACjF,OAAO,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,GAAG,CAAC,iBAAiB,MAAM,CAAC,MAAM,YAAY,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAErF,oBAAoB;IACpB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAE/C,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,MAAM,SAAS,CAAC,CAAC;QAEnF,mCAAmC;QACnC,sEAAsE;QACtE,MAAM,WAAW,GAAG,MAAM,UAAU,CAAC;YACnC,GAAG,IAAI;YACP,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,EAAE,CAAC;SAChC,CAAC,CAAC;QAEH,WAAW,IAAI,WAAW,CAAC,MAAM,CAAC;QAClC,gBAAgB,IAAI,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;QAClD,iBAAiB,IAAI,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC;QACpD,cAAc,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;QAEhD,iBAAiB;QACjB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAC1C,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,eAAe,EAAE,CAAC;gBAClB,WAAW,EAAE,MAAM,CAAC,MAAM;gBAC1B,WAAW,EAAE,KAAK,CAAC,IAAI;gBACvB,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE;gBACzE,WAAW,EAAE,cAAc;gBAC3B,cAAc,EAAE,WAAW,CAAC,cAAc;aAC3C,CAAC;QACJ,CAAC;QAED,oBAAoB;QACpB,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACzD,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAClD,CAAC;IAED,mBAAmB;IACnB,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAEjE,OAAO;QACL,KAAK,EAAE,UAAU,CAAC,EAAE;QACpB,eAAe,EAAE,MAAM,CAAC,MAAM;QAC9B,WAAW,EAAE,MAAM,CAAC,MAAM;QAC1B,MAAM,EAAE,WAAW;QACnB,KAAK,EAAE,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE;QACzE,WAAW,EAAE,cAAc;KAC5B,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,iBAAiB,CAAC,IAAsB;IACrD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,cAAc,EAAE,MAAM,CAAC,cAAc;KACtC,CAAC;AACJ,CAAC"}
|