公司动态

首个开源 PR 从 0 到合并:Reasonix 修复全流程复盘

📅 2026/8/9 16:13:49
首个开源 PR 从 0 到合并:Reasonix 修复全流程复盘
一直用别人的开源项目从来没给开源项目提过 PR。这次碰巧撞上一个 bug从报告 issue 到写补丁到合并进上游完整走了一遍流程。记录下来既是复盘也给想贡献开源但不知道怎么开始的人一个参考。背景Reasonix 是一个 AI agent 工具我平时在 Windows 11 上用。从 v1.19.6 应用内更新到 v1.20.0 后开始菜单里的 Reasonix 快捷方式失效了——点击报找不到应用然后图标自动消失。删掉快捷方式重新创建后恢复正常但每次更新都出这问题显然不是个例。我是 Java 后端Go 没有写过。但这个 bug 看着不复杂决定试试自己修。一、诊断先文档再取证后代码排障的方法论比结果重要。这次走的路线是查文档 → 本机取证 → 定位代码。第 1 步查文档和 changelog先搞清楚 v1.20.0 改了什么。看发布说明从这版开始 Windows 改用了版本化安装布局不再把核心文件直接放在安装根目录而是放到versions/版本号/子目录下用current.json指向当前激活的版本根目录放一个薄启动器reasonix-launcher.exe以及它的别名Reasonix.exe开始菜单快捷方式应指向稳定的Reasonix.exe这样升级版本时快捷方式不需要变这个设计本身是对的——快捷方式指向稳定路径版本更迭不影响。但问题是升级后快捷方式还是坏了说明有什么地方没按设计走。第 2 步本机取证光看文档不够得看本机实际状态。去安装目录翻了几个关键文件current.json指向 v1.20.0激活正常重建后的快捷方式 TargetPath指向安装目录\Reasonix.exe文件存在正常versions/目录只有v1.19.5和v1.20.0看起来都正常但反过来想重建后正常说明问题出在升级前那个快捷方式上。升级前的快捷方式 TargetPath 指向哪里结合版本化布局设计旧版快捷方式很可能指向的是versions/旧版本号/reasonix-desktop.exe。再看versions/目录——只剩 v1.19.5 和 v1.20.0没有 v1.19.6。说明更新器在升级时清理了旧版本目录。到这里证据链清楚了v1.19.6 的快捷方式 TargetPath 指向versions/v1.19.6/reasonix-desktop.exe升级到 v1.20.0 时更新器清理了 v1.19.6 的目录TargetPath 悬空快捷方式失效第 3 步定位代码带着升级后快捷方式应该自动修复但没修的线索去找代码。搜到了desktop/icon_repair_windows.go里面有一套启动自修复逻辑repairDesktopIconIntegration → repairWindowsShortcut。读完代码根因确认v1.20.0 的快捷方式自修复只重写 IconLocation图标路径从不重写 TargetPath目标路径。代码里已经把版本化路径判定为Reasonix 自有的快捷方式但判定完只修图标不修目标。TargetPath 继续悬空快捷方式继续失效。二、修复最小改动 可测试性设计改动思路问题明确了自修复逻辑需要识别TargetPath 指向已不存在的版本化路径这种情况并重写为稳定的启动器路径。改动原则最小 diff只改icon_repair_windows.go这个文件不顺手重构无关代码。review 过程中有人提到附近另一段代码的潜在问题选择不动——扩大改动面会让 review 难审也增加引入新 bug 的风险。纯逻辑抽函数把决定要不要修、修什么这个判断逻辑抽成无副作用的纯函数和有 COM 依赖的快捷方式操作分离。这样判断逻辑可以脱离 Windows COM 环境做单元测试。不误伤用户自定义只修指向 Reasonix 版化路径的快捷方式用户如果把快捷方式指向别处不碰。具体改动新增/修改了三个函数reasonixWindowsVersionedTarget新增识别一个 TargetPath 是否指向versions/v/reasonix-desktop.exe这种版本化路径。用路径匹配判断不依赖文件是否存在——因为旧版本目录被清理后文件已经不存在了但路径结构还在快捷方式里。repairWindowsShortcutPlan新增纯函数输入快捷方式当前属性输出一个修复计划——需要重写 target 吗需要重写 icon 吗还是都不用动无副作用可直接单测。repairWindowsShortcut修改主函数。检测到版本化 TargetPath 时重写为reasonix-launcher.exe的稳定路径同时修正 WorkingDirectory再按需修图标最后统一 Save。调用repairWindowsShortcutPlan拿到修复计划按计划执行。测试写了icon_repair_windows_test.go2 个测试函数共 14 个用例正例各种合法的版本化路径不同版本号格式反例指向其他安装目录的快捷方式、指向非版本化路径的快捷方式、空值边界大小写差异、深层路径、不同盘符反例和边界用例比正例多。因为最怕的不是该修的没修而是不该修的误修了——用户的自定义快捷方式被改坏比 bug 本身严重得多。验证go test ./desktop/ ✅ ok reasonix/desktop 26.4s gofmt -l . ✅ 无输出格式合规 go vet ./desktop/ ✅ 无问题本地全过再提交。CI 也会跑这些检查但本地先过一遍是对自己也对 review 的人负责。提交前 review 提了一个大小写敏感的疑虑——某些 Windows 路径匹配可能因大小写不一致而漏判。我本机跑了 8 组路径实测确认是误报但在回复里附了测试数据说明而不是只说我试了没问题。三、PR 流程GitHub 开源协作的完整闭环这部分对我来说是最新的东西。以前只用 Git 管自己的项目没走完过 fork → branch → PR → merge 的链路。写 Issue先开了 issue#7750用了项目的 bug_report 模板。几个要点现象描述要具体点击报找不到应用 → 图标自动消失比快捷方式坏了有用 100 倍附上取证证据current.json内容、TargetPath、目录列表——维护者一眼就能确认问题给根因假设哪怕不确定也写上说明认真查过也引导修复方向声明我来修开源社区非常欢迎报告 自修的贡献者比只报 bug 等别人修强得多fork → 分支 → 提交git remote -v # origin 自己的 fork (Nontee22) # upstream esengine (上游) git checkout main-v2 git pull upstream main-v2 # 先同步最新 git checkout -b fix/windows-shortcut-target # 建功能分支不在 main 上直接改。每个修复一个分支分支名用fix/前缀见名知意。提交信息用 Conventional Commits 规范fix(desktop): rewrite versioned shortcut target to stable launcher path格式是type(scope): subject。type 用 fixbug 修复scope 是 desktop。维护者扫一眼就知道这个 PR 干什么。push 和认证push 到自己的 fork不是 upstreamgit push -u origin fix/windows-shortcut-targetHTTPS 推送需要 Personal Access TokenPAT不是账号密码。在 GitHub Settings → Developer settings → Tokens 生成勾上repo权限。开 PRpush 完 GitHub 会给你一个链接点过去直接开 PR。按项目模板填base 选esengine:main-v2上游目标分支compare 选Nontee22:fix/windows-shortcut-target自己的分支填模板Documentation-impact: none / Cache-impact: none最后一行单独写Fixes #7750——合并时 GitHub 会自动关闭关联的 issue等 CI 和 reviewfork 的 PR 首次跑 CI 需要维护者批准安全机制防止恶意 workflow。维护者批准后23 项检查自动跑lint、race detector、三平台测试Windows/macOS/Linux、漏洞扫描。CI 全绿后等 review。review 意见改完git push就行PR 会自动更新不用关了重开。最终被 SivanCola 合并进esengine:main-v2issue #7750 自动关闭。修复将随 v1.21.0 发布。合并后git checkout main-v2 git pull upstream main-v2 # 同步上游 git branch -d fix/windows-shortcut-target # 删掉本地旧分支四、踩过的环境坑Go 工具链用便携版zip 解压即用放在非 C 盘不污染系统。go env -w GOPATH...迁移缓存目录原 C 盘缓存清掉释放了约 158MB。SHA256 校验下载的 Go 工具链用Get-FileHash对比官方值防止下载损坏或被篡改。证书吊销检查失败go mod下载依赖时报CRYPT_E_REVOCATION_OFFLINE。用curl --ssl-no-revoke或国内镜像绕过。PATH 持久化用[Environment]::SetEnvironmentVariable写 User 级 PATH持久生效。但每个新开的 shell 要重新加载才能用。五、这次流程教会我什么1. 排障方法论证据链思维这次最有价值的不是修好了这个 bug而是验证了一套排障路线查文档机制应该什么样→ 本机取证实际是什么样找差异→ 差异即线索 → 定位代码。这套路适用于任何升级后出问题的场景先搞清楚升级改了什么再对比本机实际状态和应有状态的差异差异点就是问题根源所在。2. 工程化改代码的纪律最小 diff只改必要的文件和函数不顺手重构无关代码纯逻辑抽函数把判断逻辑和副作用操作分离让没有环境依赖的部分可单测测试用例设计反例和边界比正例重要——误修比漏修严重工具链纪律gofmt / go vet / go test 本地先全过不把问题留给 CI3. GitHub 开源协作的完整闭环概念要点fork把别人的仓库复制到自己账号下获得可写副本remoteorigin 指自己的 forkpush 目标upstream 指上游拉更新源功能分支不在 main 上直接改每个修复一个fix/xxx分支Conventional Commits提交信息有规范fix(scope): 描述push 认证HTTPS 推送要 PAT不是密码PR 模板按项目模板填base 选上游分支compare 选自己分支Fixes #issue单独一行写合并时自动关闭关联 issuefork CI首次跑 workflow 需维护者批准安全机制合并后pull upstream 同步删旧分支4. 新手完全可以贡献不需要多强认真查证 规范流程 愿意改就够。等 review 和 CI 是常态不是被卡住。CI 23 项全绿是对改动质量的强背书比我觉得没问题有说服力得多。第一次合并的感觉你的代码进入了别人每天都在用的软件。这比写一百个练手项目都有成就感。