greenbump 0.4.0 → 0.6.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/README.md +673 -66
- package/README.zh-CN.md +525 -100
- package/dist/agent/fixer.js +161 -12
- package/dist/agent/fixer.js.map +1 -1
- package/dist/agent/fixer.test.js +410 -0
- package/dist/agent/fixer.test.js.map +1 -0
- package/dist/cli/interactive.js +81 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/cli.js.map +1 -1
- package/dist/engine/analyzers/changelog-parser.js +121 -0
- package/dist/engine/analyzers/changelog-parser.js.map +1 -0
- package/dist/engine/batch.test.js +59 -0
- package/dist/engine/batch.test.js.map +1 -0
- package/dist/engine/cache/manager.js +165 -0
- package/dist/engine/cache/manager.js.map +1 -0
- package/dist/engine/cache/manager.test.js +75 -0
- package/dist/engine/cache/manager.test.js.map +1 -0
- package/dist/engine/checks.test.js +92 -0
- package/dist/engine/checks.test.js.map +1 -0
- package/dist/engine/context/optimizer.js +189 -0
- package/dist/engine/context/optimizer.js.map +1 -0
- package/dist/engine/context/optimizer.test.js +81 -0
- package/dist/engine/context/optimizer.test.js.map +1 -0
- package/dist/engine/ecosystems/parsers.test.js +255 -0
- package/dist/engine/ecosystems/parsers.test.js.map +1 -0
- package/dist/engine/fixer/patterns.js +356 -0
- package/dist/engine/fixer/patterns.js.map +1 -0
- package/dist/engine/fixer/patterns.test.js +124 -0
- package/dist/engine/fixer/patterns.test.js.map +1 -0
- package/dist/engine/git.js +1 -0
- package/dist/engine/git.js.map +1 -1
- package/dist/engine/git.test.js +111 -0
- package/dist/engine/git.test.js.map +1 -0
- 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 +93 -2
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/run.test.js +47 -0
- package/dist/engine/run.test.js.map +1 -0
- 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/verifiers/ast-analyzer.js +212 -0
- package/dist/engine/verifiers/ast-analyzer.js.map +1 -0
- package/dist/engine/verifiers/ast-analyzer.test.js +96 -0
- package/dist/engine/verifiers/ast-analyzer.test.js.map +1 -0
- package/dist/engine/workspace.test.js +95 -0
- package/dist/engine/workspace.test.js.map +1 -0
- package/dist/exitcode.test.js +62 -0
- package/dist/exitcode.test.js.map +1 -0
- package/dist/format.js +15 -0
- package/dist/format.js.map +1 -1
- package/dist/report.test.js +56 -0
- package/dist/report.test.js.map +1 -0
- package/package.json +7 -4
- package/CHANGELOG.md +0 -93
- package/CONTRIBUTING.md +0 -52
- package/SECURITY.md +0 -38
- package/action-dist/index.cjs +0 -19935
- package/action.yml +0 -76
- package/competitive-analysis.md +0 -304
- package/examples/greenbump.yml +0 -37
- package/examples/legacy-repo/package.json +0 -12
- package/examples/legacy-repo/test.js +0 -17
- package/examples/multi-file-repo/models/todo.js +0 -8
- package/examples/multi-file-repo/models/user.js +0 -11
- package/examples/multi-file-repo/package.json +0 -12
- package/examples/multi-file-repo/services/session.js +0 -9
- package/examples/multi-file-repo/test.js +0 -19
- package/optimization-roadmap.md +0 -772
package/optimization-roadmap.md
DELETED
|
@@ -1,772 +0,0 @@
|
|
|
1
|
-
# greenbump 优化迭代方案 v0.3.x
|
|
2
|
-
|
|
3
|
-
## 问题诊断
|
|
4
|
-
|
|
5
|
-
基于竞品分析,greenbump 的三大痛点:
|
|
6
|
-
|
|
7
|
-
1. **验证可靠性不足**(依赖用户测试覆盖率)
|
|
8
|
-
- 现状:只能运行用户提供的 `npm test`,无法保证真实环境正确性
|
|
9
|
-
- 后果:10-20% 误修复率(测试通过但生产环境炸)
|
|
10
|
-
|
|
11
|
-
2. **复杂升级成功率低**(20-40%)
|
|
12
|
-
- 现状:LLM 一次性处理所有改动,上下文不足,大型重构会遗漏文件
|
|
13
|
-
- 后果:浪费 token,用户还得手动修
|
|
14
|
-
|
|
15
|
-
3. **引入新 bug 的风险**(10-20% 误修复率)
|
|
16
|
-
- 现状:LLM 可能偷偷注释测试、删代码来让 CI 通过
|
|
17
|
-
- 后果:用户不 review 就 merge,生产环境炸
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 迭代方案总览
|
|
22
|
-
|
|
23
|
-
### Phase 1: 提升验证可靠性 (v0.3.0) — 2 周
|
|
24
|
-
**目标**:降低误修复率从 10-20% → 5-10%
|
|
25
|
-
**核心功能**:
|
|
26
|
-
- P0.1: 静态分析验证(TypeScript type check, ESLint)
|
|
27
|
-
- P0.2: 变更检测警告(测试文件修改、大量删除代码)
|
|
28
|
-
- P1.1: 快照对比(API 调用前后对比)
|
|
29
|
-
|
|
30
|
-
### Phase 2: 多阶段升级策略 (v0.4.0) — 3 周
|
|
31
|
-
**目标**:复杂升级成功率从 20-40% → 50-70%
|
|
32
|
-
**核心功能**:
|
|
33
|
-
- P0.1: 依赖图分析(识别影响范围)
|
|
34
|
-
- P0.2: 分阶段修复(配置 → 类型定义 → 业务代码)
|
|
35
|
-
- P1.1: 增量提交(每阶段单独 commit,失败可回滚)
|
|
36
|
-
|
|
37
|
-
### Phase 3: 沙盒隔离验证 (v0.5.0) — 4 周
|
|
38
|
-
**目标**:高风险场景可用性提升
|
|
39
|
-
**核心功能**:
|
|
40
|
-
- P0.1: Docker 沙盒模式(`--sandbox`)
|
|
41
|
-
- P1.1: 数据库集成测试支持
|
|
42
|
-
- P1.2: 性能回归检测(响应时间对比)
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Phase 1 详细设计:提升验证可靠性 (v0.3.0)
|
|
47
|
-
|
|
48
|
-
### P0.1: 静态分析验证层
|
|
49
|
-
|
|
50
|
-
#### 问题
|
|
51
|
-
现在只运行 `npm test`,但很多问题测试覆盖不到:
|
|
52
|
-
- TypeScript 类型错误(测试可能是 JS,不检查类型)
|
|
53
|
-
- ESLint 规则违反(代码风格问题)
|
|
54
|
-
- 导入路径错误(IDE 会报错,但测试可能跳过)
|
|
55
|
-
|
|
56
|
-
#### 解决方案
|
|
57
|
-
在 `run_check` 阶段增加静态分析:
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
// src/engine/verify.ts (新文件)
|
|
61
|
-
|
|
62
|
-
export interface VerificationResult {
|
|
63
|
-
passed: boolean;
|
|
64
|
-
stage: 'build' | 'types' | 'lint' | 'test';
|
|
65
|
-
output: string;
|
|
66
|
-
warnings?: string[];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export async function runStaticAnalysis(
|
|
70
|
-
cwd: string,
|
|
71
|
-
ecosystem: string
|
|
72
|
-
): Promise<VerificationResult[]> {
|
|
73
|
-
const results: VerificationResult[] = [];
|
|
74
|
-
|
|
75
|
-
// 1. TypeScript type check (如果是 TS 项目)
|
|
76
|
-
if (await pathExists(join(cwd, 'tsconfig.json'))) {
|
|
77
|
-
const tscResult = await exec('npx tsc --noEmit', { cwd });
|
|
78
|
-
results.push({
|
|
79
|
-
passed: tscResult.code === 0,
|
|
80
|
-
stage: 'types',
|
|
81
|
-
output: tscResult.stderr,
|
|
82
|
-
warnings: parseTypeErrors(tscResult.stderr),
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 2. ESLint (如果配置了)
|
|
87
|
-
if (await pathExists(join(cwd, '.eslintrc.js')) ||
|
|
88
|
-
await pathExists(join(cwd, '.eslintrc.json'))) {
|
|
89
|
-
const lintResult = await exec('npx eslint . --ext .ts,.tsx,.js,.jsx', { cwd });
|
|
90
|
-
results.push({
|
|
91
|
-
passed: lintResult.code === 0,
|
|
92
|
-
stage: 'lint',
|
|
93
|
-
output: lintResult.stdout,
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return results;
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
#### 集成到 fix loop
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
// src/engine/run.ts
|
|
105
|
-
|
|
106
|
-
async function verifyFix(cwd: string, ecosystem: string): Promise<boolean> {
|
|
107
|
-
// 现有逻辑:build + test
|
|
108
|
-
const buildPassed = await runBuild(cwd);
|
|
109
|
-
if (!buildPassed) return false;
|
|
110
|
-
|
|
111
|
-
const testPassed = await runTest(cwd);
|
|
112
|
-
if (!testPassed) return false;
|
|
113
|
-
|
|
114
|
-
// 新增:静态分析(可选,但推荐)
|
|
115
|
-
const staticResults = await runStaticAnalysis(cwd, ecosystem);
|
|
116
|
-
const criticalFailed = staticResults.some(r =>
|
|
117
|
-
!r.passed && r.stage === 'types' // 类型错误是硬阻断
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
if (criticalFailed) {
|
|
121
|
-
log('⚠️ Static analysis failed (type errors). Asking LLM to fix...');
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Lint 错误只警告,不阻断
|
|
126
|
-
const lintWarnings = staticResults.filter(r => !r.passed && r.stage === 'lint');
|
|
127
|
-
if (lintWarnings.length > 0) {
|
|
128
|
-
summary.needsReview = true;
|
|
129
|
-
summary.reviewReason = 'Lint warnings introduced';
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
#### 预期效果
|
|
137
|
-
- **类型错误捕获率**:从 0% → 95%(TypeScript 项目)
|
|
138
|
-
- **误修复率降低**:10-20% → 5-10%(类型系统兜底)
|
|
139
|
-
- **额外时间**:+10-30 秒(tsc 编译时间)
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
### P0.2: 变更检测警告
|
|
144
|
-
|
|
145
|
-
#### 问题
|
|
146
|
-
LLM 可能做这些"坏事"来让测试通过:
|
|
147
|
-
- 注释掉失败的测试
|
|
148
|
-
- 删除大段代码("反正测试过了")
|
|
149
|
-
- 修改测试文件来适配错误的实现
|
|
150
|
-
|
|
151
|
-
#### 解决方案
|
|
152
|
-
Git diff 分析 + 启发式规则:
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
// src/engine/change-detector.ts (新文件)
|
|
156
|
-
|
|
157
|
-
export interface SuspiciousChange {
|
|
158
|
-
type: 'test-modified' | 'large-deletion' | 'test-commented';
|
|
159
|
-
file: string;
|
|
160
|
-
description: string;
|
|
161
|
-
severity: 'warning' | 'critical';
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export async function detectSuspiciousChanges(
|
|
165
|
-
cwd: string
|
|
166
|
-
): Promise<SuspiciousChange[]> {
|
|
167
|
-
const diff = await exec('git diff HEAD', { cwd });
|
|
168
|
-
const changes: SuspiciousChange[] = [];
|
|
169
|
-
|
|
170
|
-
// 1. 测试文件被修改
|
|
171
|
-
const testFilePattern = /\.(test|spec)\.(ts|js|tsx|jsx)$/;
|
|
172
|
-
const modifiedFiles = parseDiffFiles(diff.stdout);
|
|
173
|
-
|
|
174
|
-
for (const file of modifiedFiles) {
|
|
175
|
-
if (testFilePattern.test(file)) {
|
|
176
|
-
changes.push({
|
|
177
|
-
type: 'test-modified',
|
|
178
|
-
file,
|
|
179
|
-
description: 'Test file was modified during fix loop',
|
|
180
|
-
severity: 'critical',
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// 2. 大量删除代码(>50 行)
|
|
186
|
-
const deletionStats = parseDeletionStats(diff.stdout);
|
|
187
|
-
for (const [file, deletions] of Object.entries(deletionStats)) {
|
|
188
|
-
if (deletions > 50) {
|
|
189
|
-
changes.push({
|
|
190
|
-
type: 'large-deletion',
|
|
191
|
-
file,
|
|
192
|
-
description: `${deletions} lines deleted (possible over-aggressive fix)`,
|
|
193
|
-
severity: 'warning',
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// 3. 测试代码被注释(启发式检测)
|
|
199
|
-
const commentedTests = detectCommentedTests(diff.stdout);
|
|
200
|
-
changes.push(...commentedTests);
|
|
201
|
-
|
|
202
|
-
return changes;
|
|
203
|
-
}
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
#### 集成到 summary
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
// src/engine/run.ts
|
|
210
|
-
|
|
211
|
-
async function finalize(summary: RunSummary): Promise<void> {
|
|
212
|
-
const suspiciousChanges = await detectSuspiciousChanges(cwd);
|
|
213
|
-
|
|
214
|
-
if (suspiciousChanges.some(c => c.severity === 'critical')) {
|
|
215
|
-
summary.needsReview = true;
|
|
216
|
-
summary.reviewReason = 'Critical suspicious changes detected';
|
|
217
|
-
summary.warnings = suspiciousChanges.map(c =>
|
|
218
|
-
`${c.type}: ${c.file} - ${c.description}`
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// 打印警告
|
|
223
|
-
if (suspiciousChanges.length > 0) {
|
|
224
|
-
console.error(pc.yellow('\n⚠️ Suspicious changes detected:'));
|
|
225
|
-
for (const change of suspiciousChanges) {
|
|
226
|
-
console.error(` - ${change.file}: ${change.description}`);
|
|
227
|
-
}
|
|
228
|
-
console.error(pc.yellow(' → Please review the PR carefully before merging.\n'));
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
#### 预期效果
|
|
234
|
-
- **测试文件修改捕获率**:100%(现在是 50%,容易漏)
|
|
235
|
-
- **恶意删除代码检测**:>80%(启发式规则)
|
|
236
|
-
- **用户信任度提升**:明确警告 → 强制 review
|
|
237
|
-
|
|
238
|
-
---
|
|
239
|
-
|
|
240
|
-
### P1.1: 快照对比(API 调用前后对比)
|
|
241
|
-
|
|
242
|
-
#### 问题
|
|
243
|
-
即使测试通过,实际 API 行为可能变了:
|
|
244
|
-
- 函数返回值格式变化(测试只检查了部分字段)
|
|
245
|
-
- 副作用变化(数据库写入逻辑改了)
|
|
246
|
-
|
|
247
|
-
#### 解决方案
|
|
248
|
-
在升级前后运行一次真实请求,对比响应:
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
// src/engine/snapshot.ts (新文件)
|
|
252
|
-
|
|
253
|
-
export interface ApiSnapshot {
|
|
254
|
-
endpoint: string;
|
|
255
|
-
method: string;
|
|
256
|
-
response: any;
|
|
257
|
-
statusCode: number;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
export async function captureApiSnapshot(
|
|
261
|
-
cwd: string,
|
|
262
|
-
ecosystem: string
|
|
263
|
-
): Promise<ApiSnapshot[]> {
|
|
264
|
-
// 从测试文件推断 API endpoints(启发式)
|
|
265
|
-
const testFiles = await glob('**/*.test.{ts,js}', { cwd });
|
|
266
|
-
const endpoints = extractApiCalls(testFiles);
|
|
267
|
-
|
|
268
|
-
const snapshots: ApiSnapshot[] = [];
|
|
269
|
-
|
|
270
|
-
// 启动 dev server
|
|
271
|
-
const server = await startDevServer(cwd);
|
|
272
|
-
|
|
273
|
-
for (const endpoint of endpoints) {
|
|
274
|
-
const response = await fetch(`http://localhost:${server.port}${endpoint}`);
|
|
275
|
-
snapshots.push({
|
|
276
|
-
endpoint,
|
|
277
|
-
method: 'GET',
|
|
278
|
-
response: await response.json(),
|
|
279
|
-
statusCode: response.status,
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
await server.stop();
|
|
284
|
-
return snapshots;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
export function compareSnapshots(
|
|
288
|
-
before: ApiSnapshot[],
|
|
289
|
-
after: ApiSnapshot[]
|
|
290
|
-
): { changed: boolean; diff: string } {
|
|
291
|
-
// 深度对比 JSON 结构
|
|
292
|
-
const diffs = deepDiff(before, after);
|
|
293
|
-
return {
|
|
294
|
-
changed: diffs.length > 0,
|
|
295
|
-
diff: formatDiff(diffs),
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
#### 使用场景(可选功能,默认关闭)
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
# 用户显式启用
|
|
304
|
-
greenbump react --snapshot
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
#### 限制
|
|
308
|
-
- 只适用于有 dev server 的项目(API 项目、Web 应用)
|
|
309
|
-
- 需要能快速启动(<30 秒)
|
|
310
|
-
- 增加时间:+1-2 分钟
|
|
311
|
-
|
|
312
|
-
#### 预期效果
|
|
313
|
-
- **Runtime 行为回归检测**:0% → 60%(启发式,不是 100%)
|
|
314
|
-
- **适用场景**:API 升级(Express, Fastify, Django)
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
## Phase 2 详细设计:多阶段升级策略 (v0.4.0)
|
|
319
|
-
|
|
320
|
-
### P0.1: 依赖图分析
|
|
321
|
-
|
|
322
|
-
#### 问题
|
|
323
|
-
现在 LLM 一次性看到所有错误,不知道从哪下手:
|
|
324
|
-
- 升级 `react` 可能影响 50 个组件
|
|
325
|
-
- LLM 上下文不够,会遗漏文件
|
|
326
|
-
|
|
327
|
-
#### 解决方案
|
|
328
|
-
构建依赖图,按影响范围排序修复:
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
// src/engine/dep-graph.ts (新文件)
|
|
332
|
-
|
|
333
|
-
export interface DependencyNode {
|
|
334
|
-
file: string;
|
|
335
|
-
imports: string[]; // 导入的其他文件
|
|
336
|
-
usesUpgradedPkg: boolean; // 是否直接使用升级的包
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
export async function buildDependencyGraph(
|
|
340
|
-
cwd: string,
|
|
341
|
-
upgradedPkg: string
|
|
342
|
-
): Promise<DependencyNode[]> {
|
|
343
|
-
// 使用 madge 或 dependency-cruiser
|
|
344
|
-
const graph = await analyzeDependencies(cwd);
|
|
345
|
-
|
|
346
|
-
// 找出直接使用升级包的文件
|
|
347
|
-
const directUsers = graph.filter(node =>
|
|
348
|
-
node.imports.includes(upgradedPkg)
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// 找出间接依赖的文件(通过导入链)
|
|
352
|
-
const indirectUsers = findTransitiveDeps(graph, directUsers);
|
|
353
|
-
|
|
354
|
-
return [...directUsers, ...indirectUsers];
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export function prioritizeFiles(nodes: DependencyNode[]): string[][] {
|
|
358
|
-
// 按层级分组(L0: 直接依赖,L1: 间接依赖,...)
|
|
359
|
-
const layers: string[][] = [];
|
|
360
|
-
let currentLayer = nodes.filter(n => n.usesUpgradedPkg).map(n => n.file);
|
|
361
|
-
|
|
362
|
-
while (currentLayer.length > 0) {
|
|
363
|
-
layers.push(currentLayer);
|
|
364
|
-
currentLayer = findNextLayer(nodes, currentLayer);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return layers;
|
|
368
|
-
}
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
#### 集成到 fix loop
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
// src/engine/run.ts
|
|
375
|
-
|
|
376
|
-
async function fixWithStaging(
|
|
377
|
-
cwd: string,
|
|
378
|
-
dep: string,
|
|
379
|
-
testCmd: string
|
|
380
|
-
): Promise<RunSummary> {
|
|
381
|
-
const depGraph = await buildDependencyGraph(cwd, dep);
|
|
382
|
-
const layers = prioritizeFiles(depGraph);
|
|
383
|
-
|
|
384
|
-
console.log(`📊 Dependency graph: ${layers.length} layers, ${depGraph.length} files`);
|
|
385
|
-
|
|
386
|
-
for (let i = 0; i < layers.length; i++) {
|
|
387
|
-
console.log(`\n🔧 Stage ${i + 1}/${layers.length}: Fixing ${layers[i].length} files`);
|
|
388
|
-
|
|
389
|
-
// 只让 LLM 看这一层的文件
|
|
390
|
-
const stageResult = await fixLayer(cwd, layers[i], testCmd);
|
|
391
|
-
|
|
392
|
-
if (!stageResult.success) {
|
|
393
|
-
console.error(`❌ Stage ${i + 1} failed. Stopping.`);
|
|
394
|
-
return { status: 'unfixed', stage: i + 1 };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// 每阶段单独 commit
|
|
398
|
-
await exec(`git add -A && git commit -m "Stage ${i + 1}: fix ${layers[i].length} files"`, { cwd });
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
return { status: 'fixed', stages: layers.length };
|
|
402
|
-
}
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
#### 预期效果
|
|
406
|
-
- **复杂升级成功率**:20-40% → 50-70%
|
|
407
|
-
- **Token 使用效率**:+30%(LLM 每次只看相关文件)
|
|
408
|
-
- **可回滚性**:每阶段失败可独立回滚
|
|
409
|
-
|
|
410
|
-
---
|
|
411
|
-
|
|
412
|
-
### P0.2: 分阶段修复策略
|
|
413
|
-
|
|
414
|
-
#### 问题
|
|
415
|
-
某些升级需要先改配置,再改代码:
|
|
416
|
-
- Webpack 5: 先改 `webpack.config.js`,再改代码
|
|
417
|
-
- TypeScript 5: 先改 `tsconfig.json`,再修类型错误
|
|
418
|
-
|
|
419
|
-
#### 解决方案
|
|
420
|
-
预定义修复顺序:
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
423
|
-
// src/engine/stages.ts (新文件)
|
|
424
|
-
|
|
425
|
-
export interface FixStage {
|
|
426
|
-
name: string;
|
|
427
|
-
filePatterns: string[]; // 这个阶段修复哪些文件
|
|
428
|
-
validator: (cwd: string) => Promise<boolean>; // 这个阶段是否成功
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
export const COMMON_STAGES: FixStage[] = [
|
|
432
|
-
{
|
|
433
|
-
name: 'Configuration',
|
|
434
|
-
filePatterns: [
|
|
435
|
-
'**/package.json',
|
|
436
|
-
'**/tsconfig.json',
|
|
437
|
-
'**/webpack.config.js',
|
|
438
|
-
'**/.eslintrc.*',
|
|
439
|
-
],
|
|
440
|
-
validator: async (cwd) => {
|
|
441
|
-
// 配置文件语法是否正确
|
|
442
|
-
return await validateJsonFiles(cwd);
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
{
|
|
446
|
-
name: 'Type Definitions',
|
|
447
|
-
filePatterns: ['**/*.d.ts', '**/types/**'],
|
|
448
|
-
validator: async (cwd) => {
|
|
449
|
-
// 类型是否通过
|
|
450
|
-
const result = await exec('npx tsc --noEmit', { cwd });
|
|
451
|
-
return result.code === 0;
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
{
|
|
455
|
-
name: 'Source Code',
|
|
456
|
-
filePatterns: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
|
|
457
|
-
validator: async (cwd) => {
|
|
458
|
-
// 业务代码是否通过测试
|
|
459
|
-
return await runTest(cwd);
|
|
460
|
-
},
|
|
461
|
-
},
|
|
462
|
-
];
|
|
463
|
-
|
|
464
|
-
export async function runStagedFix(
|
|
465
|
-
cwd: string,
|
|
466
|
-
dep: string
|
|
467
|
-
): Promise<RunSummary> {
|
|
468
|
-
for (const stage of COMMON_STAGES) {
|
|
469
|
-
console.log(`\n🔧 Stage: ${stage.name}`);
|
|
470
|
-
|
|
471
|
-
const filesToFix = await matchFiles(cwd, stage.filePatterns);
|
|
472
|
-
if (filesToFix.length === 0) {
|
|
473
|
-
console.log(` (no files matched, skipping)`);
|
|
474
|
-
continue;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const result = await fixFiles(cwd, filesToFix);
|
|
478
|
-
const passed = await stage.validator(cwd);
|
|
479
|
-
|
|
480
|
-
if (!passed) {
|
|
481
|
-
console.error(`❌ ${stage.name} stage failed`);
|
|
482
|
-
return { status: 'unfixed', failedStage: stage.name };
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
await exec(`git add -A && git commit -m "Fix ${stage.name}"`, { cwd });
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return { status: 'fixed' };
|
|
489
|
-
}
|
|
490
|
-
```
|
|
491
|
-
|
|
492
|
-
#### 预期效果
|
|
493
|
-
- **Webpack/Babel 升级成功率**:30% → 70%(配置先行)
|
|
494
|
-
- **TypeScript 升级成功率**:40% → 80%(类型定义先修)
|
|
495
|
-
|
|
496
|
-
---
|
|
497
|
-
|
|
498
|
-
### P1.1: 增量提交(每阶段单独 commit)
|
|
499
|
-
|
|
500
|
-
#### 现有问题
|
|
501
|
-
Fix loop 结束后只有一个大 commit,失败了全部回滚
|
|
502
|
-
|
|
503
|
-
#### 改进
|
|
504
|
-
每个阶段独立 commit:
|
|
505
|
-
|
|
506
|
-
```bash
|
|
507
|
-
greenbump/react-19.0.0
|
|
508
|
-
├── Stage 1: Fix configuration (webpack.config.js)
|
|
509
|
-
├── Stage 2: Fix type definitions (types/*.d.ts)
|
|
510
|
-
└── Stage 3: Fix source code (src/**/*.tsx)
|
|
511
|
-
```
|
|
512
|
-
|
|
513
|
-
#### 用户体验
|
|
514
|
-
```bash
|
|
515
|
-
$ greenbump react --staged
|
|
516
|
-
|
|
517
|
-
🌱 greenbump
|
|
518
|
-
· target: react 18.3.1 → 19.0.0
|
|
519
|
-
· dependency graph: 3 layers, 47 files
|
|
520
|
-
|
|
521
|
-
🔧 Stage 1/3: Configuration (2 files)
|
|
522
|
-
✓ Fixed webpack.config.js
|
|
523
|
-
✓ Committed: Stage 1/3
|
|
524
|
-
|
|
525
|
-
🔧 Stage 2/3: Type definitions (5 files)
|
|
526
|
-
✓ Fixed types/react.d.ts
|
|
527
|
-
✓ Committed: Stage 2/3
|
|
528
|
-
|
|
529
|
-
🔧 Stage 3/3: Source code (40 files)
|
|
530
|
-
❌ Failed after 10 rounds
|
|
531
|
-
|
|
532
|
-
💡 Tip: Stage 1-2 succeeded. You can:
|
|
533
|
-
- Review the commits and continue manually from Stage 3
|
|
534
|
-
- Or run: git reset HEAD~2 to rollback everything
|
|
535
|
-
```
|
|
536
|
-
|
|
537
|
-
---
|
|
538
|
-
|
|
539
|
-
## Phase 3 详细设计:沙盒隔离验证 (v0.5.0)
|
|
540
|
-
|
|
541
|
-
### P0.1: Docker 沙盒模式
|
|
542
|
-
|
|
543
|
-
#### 问题
|
|
544
|
-
Fix loop 在本地运行,可能污染环境:
|
|
545
|
-
- 全局安装的包冲突
|
|
546
|
-
- 数据库状态被修改
|
|
547
|
-
- 无法并行运行多个升级
|
|
548
|
-
|
|
549
|
-
#### 解决方案
|
|
550
|
-
Docker 隔离:
|
|
551
|
-
|
|
552
|
-
```typescript
|
|
553
|
-
// src/engine/sandbox.ts (新文件)
|
|
554
|
-
|
|
555
|
-
export interface SandboxConfig {
|
|
556
|
-
image: string; // 基础镜像(node:20, python:3.11)
|
|
557
|
-
volumes: string[]; // 挂载的目录
|
|
558
|
-
env: Record<string, string>; // 环境变量
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
export async function runInSandbox(
|
|
562
|
-
cwd: string,
|
|
563
|
-
cmd: string,
|
|
564
|
-
config: SandboxConfig
|
|
565
|
-
): Promise<{ code: number; stdout: string; stderr: string }> {
|
|
566
|
-
const containerId = generateId();
|
|
567
|
-
|
|
568
|
-
// 1. 启动容器
|
|
569
|
-
await exec(`docker run -d --name ${containerId} \
|
|
570
|
-
-v ${cwd}:/workspace \
|
|
571
|
-
-w /workspace \
|
|
572
|
-
${config.image} \
|
|
573
|
-
sleep infinity
|
|
574
|
-
`);
|
|
575
|
-
|
|
576
|
-
// 2. 执行命令
|
|
577
|
-
const result = await exec(`docker exec ${containerId} ${cmd}`);
|
|
578
|
-
|
|
579
|
-
// 3. 清理
|
|
580
|
-
await exec(`docker rm -f ${containerId}`);
|
|
581
|
-
|
|
582
|
-
return result;
|
|
583
|
-
}
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
#### 使用方式
|
|
587
|
-
|
|
588
|
-
```bash
|
|
589
|
-
greenbump react --sandbox
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
#### 限制
|
|
593
|
-
- 需要本地安装 Docker
|
|
594
|
-
- 启动时间:+10-30 秒
|
|
595
|
-
- 不支持 macOS 特有的工具(如 Xcode)
|
|
596
|
-
|
|
597
|
-
#### 预期效果
|
|
598
|
-
- **环境隔离**:100%(不污染本地)
|
|
599
|
-
- **并行升级**:可以同时跑 5 个 greenbump(不同容器)
|
|
600
|
-
- **可重现性**:+95%(Docker 镜像固定)
|
|
601
|
-
|
|
602
|
-
---
|
|
603
|
-
|
|
604
|
-
### P1.1: 数据库集成测试支持
|
|
605
|
-
|
|
606
|
-
#### 问题
|
|
607
|
-
很多项目的测试需要真实数据库(PostgreSQL, MySQL)
|
|
608
|
-
|
|
609
|
-
#### 解决方案
|
|
610
|
-
Docker Compose 支持:
|
|
611
|
-
|
|
612
|
-
```yaml
|
|
613
|
-
# greenbump.sandbox.yml (用户自定义)
|
|
614
|
-
version: '3'
|
|
615
|
-
services:
|
|
616
|
-
app:
|
|
617
|
-
image: node:20
|
|
618
|
-
volumes:
|
|
619
|
-
- .:/workspace
|
|
620
|
-
depends_on:
|
|
621
|
-
- db
|
|
622
|
-
db:
|
|
623
|
-
image: postgres:15
|
|
624
|
-
environment:
|
|
625
|
-
POSTGRES_PASSWORD: test
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
```bash
|
|
629
|
-
greenbump react --sandbox --compose greenbump.sandbox.yml
|
|
630
|
-
```
|
|
631
|
-
|
|
632
|
-
#### 预期效果
|
|
633
|
-
- **集成测试成功率**:30% → 90%(数据库可用)
|
|
634
|
-
|
|
635
|
-
---
|
|
636
|
-
|
|
637
|
-
## 优先级排序 & 时间规划
|
|
638
|
-
|
|
639
|
-
### v0.3.0 (2 周) - 必做
|
|
640
|
-
- ✅ P0.1: 静态分析验证(TypeScript + ESLint)— 3 天
|
|
641
|
-
- ✅ P0.2: 变更检测警告(测试修改、大量删除)— 2 天
|
|
642
|
-
- ⚠️ P1.1: 快照对比(API 前后对比)— 5 天(可选)
|
|
643
|
-
|
|
644
|
-
**发布标准**:误修复率降到 5-10%
|
|
645
|
-
|
|
646
|
-
### v0.4.0 (3 周) - 推荐
|
|
647
|
-
- ✅ P0.1: 依赖图分析(madge 集成)— 5 天
|
|
648
|
-
- ✅ P0.2: 分阶段修复(配置→类型→代码)— 5 天
|
|
649
|
-
- ✅ P1.1: 增量提交(每阶段独立 commit)— 3 天
|
|
650
|
-
|
|
651
|
-
**发布标准**:复杂升级成功率达到 50-70%
|
|
652
|
-
|
|
653
|
-
### v0.5.0 (4 周) - 长期
|
|
654
|
-
- ✅ P0.1: Docker 沙盒模式 — 7 天
|
|
655
|
-
- ⚠️ P1.1: 数据库集成测试 — 5 天
|
|
656
|
-
- ⚠️ P1.2: 性能回归检测 — 5 天
|
|
657
|
-
|
|
658
|
-
**发布标准**:适用于高风险生产环境
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## 技术选型
|
|
663
|
-
|
|
664
|
-
### 依赖图分析
|
|
665
|
-
- **madge**(推荐):轻量,支持 ES6/CommonJS
|
|
666
|
-
- **dependency-cruiser**:功能更强,但配置复杂
|
|
667
|
-
|
|
668
|
-
### 静态分析
|
|
669
|
-
- **TypeScript**:tsc --noEmit(必须)
|
|
670
|
-
- **ESLint**:eslint .(推荐)
|
|
671
|
-
- **Prettier**:prettier --check .(可选)
|
|
672
|
-
|
|
673
|
-
### Docker
|
|
674
|
-
- **官方 Node 镜像**:node:20-alpine(体积小)
|
|
675
|
-
- **Docker Compose**:v2.x(数据库支持)
|
|
676
|
-
|
|
677
|
-
---
|
|
678
|
-
|
|
679
|
-
## 成本-收益评估
|
|
680
|
-
|
|
681
|
-
### v0.3.0 投入 vs 产出
|
|
682
|
-
**投入**:
|
|
683
|
-
- 开发时间:2 周 × 1 人 = 10 天
|
|
684
|
-
- 测试时间:3 天
|
|
685
|
-
|
|
686
|
-
**产出**:
|
|
687
|
-
- 误修复率:10-20% → 5-10%(降低一半)
|
|
688
|
-
- 用户信任度:+30%(明确警告)
|
|
689
|
-
- PR review 时间:-40%(自动标记可疑改动)
|
|
690
|
-
|
|
691
|
-
**ROI**:⭐⭐⭐⭐⭐(必做,影响用户信任)
|
|
692
|
-
|
|
693
|
-
### v0.4.0 投入 vs 产出
|
|
694
|
-
**投入**:
|
|
695
|
-
- 开发时间:3 周 × 1 人 = 15 天
|
|
696
|
-
- 测试时间:5 天
|
|
697
|
-
|
|
698
|
-
**产出**:
|
|
699
|
-
- 复杂升级成功率:20-40% → 50-70%(+30%)
|
|
700
|
-
- Token 使用效率:+30%(减少无效尝试)
|
|
701
|
-
- 用户满意度:+50%(Webpack/TS 升级可用)
|
|
702
|
-
|
|
703
|
-
**ROI**:⭐⭐⭐⭐(推荐,显著提升成功率)
|
|
704
|
-
|
|
705
|
-
### v0.5.0 投入 vs 产出
|
|
706
|
-
**投入**:
|
|
707
|
-
- 开发时间:4 周 × 1 人 = 20 天
|
|
708
|
-
- 测试时间:7 天
|
|
709
|
-
- 基础设施成本:Docker 镜像存储
|
|
710
|
-
|
|
711
|
-
**产出**:
|
|
712
|
-
- 高风险场景可用性:0% → 60%
|
|
713
|
-
- 并行升级能力:1x → 5x
|
|
714
|
-
- 可重现性:+95%
|
|
715
|
-
|
|
716
|
-
**ROI**:⭐⭐⭐(长期投资,适合企业用户)
|
|
717
|
-
|
|
718
|
-
---
|
|
719
|
-
|
|
720
|
-
## 风险评估
|
|
721
|
-
|
|
722
|
-
### v0.3.0 风险
|
|
723
|
-
- **兼容性**:某些项目没配置 TSConfig/ESLint(降级处理:跳过检查)
|
|
724
|
-
- **误报**:启发式规则可能误判(解决:可配置关闭)
|
|
725
|
-
|
|
726
|
-
### v0.4.0 风险
|
|
727
|
-
- **依赖图准确性**:动态导入可能漏检(解决:保守策略,多包含文件)
|
|
728
|
-
- **阶段划分**:不是所有项目都适合 3 阶段(解决:用户可自定义)
|
|
729
|
-
|
|
730
|
-
### v0.5.0 风险
|
|
731
|
-
- **Docker 依赖**:用户可能没装 Docker(解决:检测并降级到本地模式)
|
|
732
|
-
- **启动时间**:+10-30 秒(解决:缓存镜像)
|
|
733
|
-
|
|
734
|
-
---
|
|
735
|
-
|
|
736
|
-
## 下一步行动
|
|
737
|
-
|
|
738
|
-
### 立即可做(v0.3.0)
|
|
739
|
-
1. **Week 1**:
|
|
740
|
-
- Day 1-2: 实现 `verify.ts`(TypeScript + ESLint 集成)
|
|
741
|
-
- Day 3-4: 实现 `change-detector.ts`(测试修改检测)
|
|
742
|
-
- Day 5: 集成到 `run.ts`,写单元测试
|
|
743
|
-
|
|
744
|
-
2. **Week 2**:
|
|
745
|
-
- Day 1-3: 实现 `snapshot.ts`(API 快照对比)
|
|
746
|
-
- Day 4-5: 端到端测试(真实 repo)
|
|
747
|
-
- Day 6-7: 文档 + 发布 v0.3.0
|
|
748
|
-
|
|
749
|
-
### 用户验证(Beta 测试)
|
|
750
|
-
在 v0.3.0 发布后,招募 10-20 个 beta 用户:
|
|
751
|
-
- 收集误修复案例
|
|
752
|
-
- 调优启发式规则
|
|
753
|
-
- 决定是否继续 v0.4.0
|
|
754
|
-
|
|
755
|
-
---
|
|
756
|
-
|
|
757
|
-
## 总结
|
|
758
|
-
|
|
759
|
-
**核心策略**:
|
|
760
|
-
1. v0.3.0 先解决「信任问题」(降低误修复率)
|
|
761
|
-
2. v0.4.0 再解决「能力问题」(提升复杂升级成功率)
|
|
762
|
-
3. v0.5.0 最后解决「场景问题」(高风险环境可用)
|
|
763
|
-
|
|
764
|
-
**预期成果**:
|
|
765
|
-
- 6 个月后,greenbump 成为「可信赖的」AI 升级工具
|
|
766
|
-
- 适用场景从「小项目」扩展到「中大型项目」
|
|
767
|
-
- 与 Migratowl 在「Python + 高风险」之外的所有场景竞争
|
|
768
|
-
|
|
769
|
-
**关键成功指标**:
|
|
770
|
-
- 误修复率 < 5%
|
|
771
|
-
- 复杂升级成功率 > 60%
|
|
772
|
-
- GitHub Star 增长 > 50%(用户信任度)
|