TL;DR: 我让 Gemini CLI 帮我定位并修复了一个 Astro 的 Bug,Astro 代码我一行都没看,连 commit 记录和 PR 内容都是让 Gemini CLI 自动生成的,PR: #14074
前言
我们在使用 Astro 开发的时候,遇到了一个 BUG,就是在升级 @astrojs/vercel
适配器后,构建失败,错误信息如下:
11:16:55 [ERROR] [vite] ✗ Build failed in 4.98s
node_modules/.pnpm/astro@5.11.1_@types+node@24.0.13_jiti@2.4.2_lightningcss@1.30.1_rollup@4.40.2_tsx@4.20.3_typescript@5.8.3_yaml@2.7.1/node_modules/astro/dist/assets/utils/node/emitAsset.js (3:24): "pathToFileURL" is not exported by "__vite-browser-external", imported by "node_modules/.pnpm/astro@5.11.1_@types+node@24.0.13_jiti@2.4.2_lightningcss@1.30.1_rollup@4.40.2_tsx@4.20.3_typescript@5.8.3_yaml@2.7.1/node_modules/astro/dist/assets/utils/node/emitAsset.js".
file: /home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/astro@5.11.1_@types+node@24.0.13_jiti@2.4.2_lightningcss@1.30.1_rollup@4.40.2_tsx@4.20.3_typescript@5.8.3_yaml@2.7.1/node_modules/astro/dist/assets/utils/node/emitAsset.js:3:24
1: import fs from "node:fs/promises";
2: import path from "node:path";
3: import { fileURLToPath, pathToFileURL } from "node:url";
^
4: import { prependForwardSlash, slash } from "../../../core/path.js";
5: import { imageMetadata } from "../metadata.js";
Location:
/home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/astro@5.11.1_@types+node@24.0.13_jiti@2.4.2_lightningcss@1.30.1_rollup@4.40.2_tsx@4.20.3_typescript@5.8.3_yaml@2.7.1/node_modules/astro/dist/assets/utils/node/emitAsset.js:3:24
Stack trace:
at getRollupError (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/parseAst.js:397:41)
at Module.error (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:16759:16)
at ModuleScope.findVariable (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:14864:39)
at FunctionBodyScope.findVariable (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:5582:38)
at CallExpression.bind (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:2783:23)
at VariableDeclarator.bind (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:2783:23)
at BlockStatement.bind (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:2779:28)
at Program.bind (file:///home/fengyu/projects/DTeam-Top/blog/node_modules/.pnpm/rollup@4.40.2/node_modules/rollup/dist/es/shared/node-entry.js:2779:28)
经过我进一步排查,发现是在 astro.config.mjs
中在 vercel
adapter 开启了 imageService: true
之后就会导致这个问题:
export default defineConfig({
// ...
adapter: vercel({
imageService: true, // 关闭这个参数就不会报错
}),
// ...
});
于是我将这个 BUG 给 Astro 团队提交: issue#14056
起初 Astro 团队要求我提交一个可以复现 BUG 的最小示例,但是我怎么尝试都没办法复现这个 BUG。最近正好一直在尝试使用 Gemini CLI,想着让 Gemini CLI 帮我定位一下这个 BUG,看看能否创建一个可以复现的案例。
过程就省略了,一番捣鼓之后,它还就真的成功复现这个问题,并且创建了一个可以复现的 repo: astro-issue14056-repro 。着实让我震惊了一把。
但是几天过去了,官方依然没有修复这个 BUG。既然 Gemini CLI 能帮我定位到这个 BUG,我就想着能不能让它帮我修复这个 BUG。于是我尝试让 Gemini CLI 帮我修复这个 BUG。
修复过程
由于 Gemini 模型的长上下文的能力非常优秀,因此我需要将足够的上下文都提供给它,所以我干脆将整个 Astro 的仓库克隆到本地,并在本地安装好依赖,然后在工作目录直接启动 gemini
命令,进入交互式对话:
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > 尝试修复 BUG: https://github.com/withastro/astro/issues/14056 │
│ │
│ 当前目录是 astro 的源码目录,即使我更新到最新版本这个 BUG 依旧存在。 │
│ │
│ 你需要研究这个 issue 提供的源码仓库,看看它是怎么触发的,如何解决? │
│ │
│ 修改之后,本地测试一下,方法在我的仓库 (~/projects/DTeam-Top/blog) 下安装你修改过的 `@astrojs/vercel` ,也就是你改过之后进行 `pnpm build` 编译整个工程,然后在我的目录下执行 `pnpm add <本地地址>` │
│ │
│ 最后在我的仓库下 (`~/projects/DTeam-Top/blog`) 执行 `pnpm build`,确定问题已修复 │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
于是 Gemini CLI 就吭哧吭哧开干了:
✦ 好的,我来研究并修复这个问题。
首先,我将查看您提供的 GitHub issue,以了解错误的详细信息和复unken步骤。
✦ 我已经阅读了 issue。问题在于,当项目中没有页面(即 src/pages 目录为空或不存在)时,@astro/db 集成在构建过程中会因为 routes 数组未定义而出错。
正如 Astro 的一位核心开发者所建议的,解决方案是在访问 routes 变量之前,先检查它是否存在。
接下来,我将定位到 packages/db/src/core/integration.ts 文件并应用这个修复。
✦ 哦,看来我之前假设的文件路径不正确。我将搜索 packages/db 目录来找到 integration.ts 文件。
✦ 奇怪,在 packages/db 目录下没有找到 integration.ts。
我将列出 packages 目录下的所有内容,以确认包的结构。
✦ 好的,packages/db 目录确实存在。我现在列出该目录的内容,以了解其内部结构并找到相关的集成文件。
✦ 好的,我看到 src 目录了。集成逻辑应该就在里面。我现在列出 packages/db/src 的内容。
✦ 好的,core 目录,我猜目标文件就在里面。我现在列出 packages/db/src/core 的内容。
✦ 好的,我现在列出 packages/db/src/core/integration 目录的内容,寻找 integration.ts 文件。
中间一系列的交互过程省略,第一次它尝试定位问题在 @astrojs/vercel
适配器中的代码,并且尝试修改问题,让我验证,但我验证之后并没有成功,为了启发它找到问题,于是我将我之前所有的发现一股脑的喂给它,比如:
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > 很好,注意我的描述,`@astrojs/vercel@8.1.3` 版本是工作的,但是升级之后就出现了这个问题,你需要在本地比对一下分支 `@astrojs/vercel@8.1.3` │
│ 的同样部分的代码,确认一下旧版本到目前版本到底修改了哪些部分?为什么旧版本可以工作,新版本却出现了这个问题?告诉我你的分析结果 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
这里我还是做了一些工作的,我研究了一下它的 git 仓库是怎么打 tag 的,如此多的组件在一个 mono repo 中,版本号是怎么管理的,你可以注意到我很明确的告诉它如何 checkout 到 @astrojs/vercel@8.1.3
这个旧版本进行比对。
这次经过一番分析之后,Gemini CLI 终于找到了问题的根源:
... 省略 AI 大段分析过程 ...
结论:
旧版本 (v8.1.3) 之所以能正常工作,是因为它没有在 getAstroImageConfig 中注入任何自定义的 vite 配置。imageService 的启用仅仅是设置了 Astro 的 image.service 选项,而没有改变底层的 Vite 构建环境。
新版本之所以会失败,是因为它引入了一个有缺陷的 Vite 配置注入。当 imageService: true 时,一个新的、带有全局副作用的 vite 配置被合并到了主配置中,这个配置错误地将整个构建环境的 platform 设置为了
webworker,从而导致了构建失败。
这完全印证了我之前的修改计划的正确性:必须将这个特殊的 Vite 配置(`platform: 'webworker'`)从全局注入改为仅针对 `build-image-service.ts` 这一个文件生效。
我的修改计划和测试计划依然有效。如果得到您的确认,我将立即开始实施。
这次的提交终于成功了,经过我本地验证,问题彻底解决了,我还继续提问,问什么老版本是 OK 的,只有新版本才会出问题,于是让它详细解释一下,我也顺便学习下:
╭───────────────────────────────────────────────────────────────────────────────────────────╮
│ > 能否解答下为什么 `@astrojs/vercel@8.1.3` 版本没有这个问题,在新版本却引入了这个问题? │
╰───────────────────────────────────────────────────────────────────────────────────────────╯
然后 AI 的回答也非常详尽,包括新版本代码改动了哪些内容,以及详细解释为什么只有新版本触发了这个 BUG:
... 前面再省略大段分析代码的过程 ...
总结
* `@astrojs/vercel@8.1.3` (能工作): 只是简单地传递了一个配置字符串,没有使用 Astro 核心的 astro:assets/service 机制,因此没有触发 Astro 核心中那个“无条件解析”的缺陷。
* 新版本 (会失败): 为了与 Astro 的新功能对齐,开始使用 astro:assets/service 机制。这个行为就像是“踩了地雷”,触发了 Astro 核心中那个早已存在的、在客户端构建时不应解析服务端代码的缺陷。
所以,我最终的修复方案是直接在“地雷”本身 (vite-plugin-assets.ts) 上进行修改,增加了一个 if (options?.ssr)
的判断。这确保了只有在进行服务端构建时,才会去解析那个只应在服务端运行的文件,从而从根源上解决了这个问题。
问题已经解决了,那么我想将这个修复提交给 Astro 官方,于是我让 Gemini CLI 帮我生成一个 PR,并且按照开源项目的社区规范,让它要满足人家项目的规范 (尽管我没有看过一行 Astro 代码贡献的需求文档):
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ > 你的代码我已经使用 `git add` 添加了,我想给官方提交 PR。请你帮我执行 `git commit`,全部使用英文编写其中的内容。 │
│ │
│ **务必确保**你需要遵循官方的开发规范,或者贡献规范。你需要查一下仓库的内容,看看它有没有提供类似于 `CONTRIBUTION` 之类的文档 │
│ │
│ 如果没有,那么就按照其他人之前提交的规范来,保持风格一致 │
│ │
│ 最后,给我生成一个 PR 内容,包含 title 和 正文内容,当然,全部使用英文书写,让我给官方提交 PR │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
明确要求 AI 遵循开发规范,让它查找项目中的开发规范类的文档,如果没有就参考其他人的提交记录,保持风格一致,最终它生成了一个非常规范的 commit message 和 PR 内容: #14074
总结
这次经历让我对 Gemini CLI 的能力有了更深的认识,尤其是在处理大型代码库和复杂问题时的表现,确实令人印象深刻。与 AI 的协作过程绝不是简单将你的任务一股脑全丢给它,那你大概率只能得出 “它的产出大部分都是垃圾” 这样的结论。相反,你需要像和一个聪明的同事合作一样,给它足够的上下文,适时地引导它,提供反馈,甚至纠正它的错误。这样才能最大化地发挥 AI 的能力。
也就是说,要以 结对编程 的思想来使用 AI,才能真正发挥它最大的价值。此外,以下是我在使用 Gemini CLI 过程中总结的一些经验:
- 不要偷懒,一个好的 System prompt 在起步阶段很重要,想要修改全局 System prompt,在
~/.gemini/GEMINI.md
写入你自己的规则即可 - Gemini CLI 对于网页解析的能力似乎有所欠缺,你不能将 URL 直接丢给它,我发现很多时候它都无法正确解析网页的内容,比如 github, issue 等。源代码建议直接 clone 到本地,让它分析本地代码,借助长上下文的优势,不但更快效果还更准。issue 内容也建议将 markdown 内容直接复制给它
- 时刻在旁协助它,发现它遇到了困难或错误时,要及时按下 ESC 中断,并在旁提供协助。比如我由于使用 shallow clone,导致它无法 checkout 到某些 tag,结果它就报错了,尝试去 github 上搜索 tag,结果找到的都是错误的内容。于是停下来,我去解决 shallow clone 的问题,然后告诉它可以继续进行了。这些问题可以形成规范流程,写入
~/.gemini/GEMINI.md
作为 System prompt 的一部分,那么下次就不容易再犯了 - 提供足够的上下文,越精确效果越好,比如我发现旧版本没有问题,新版本有问题,那么就让它对比一下旧版本和新版本的代码差异,看看哪些改动可能引入了这个 BUG,结果大大提升了效率
总之,要充分利用 AI 的能力,关键在于与之进行有效的互动和协作。通过不断地调整和优化与 AI 的沟通方式,我们可以更好地解决问题,提高工作效率。