gsd-core 测试模式安装守卫:`finishInstall` 如何在 `GSD_TEST_MODE=1` 下跳过 opencode 权限配置(Bug 130 修复深度解析)

发布时间:2026/9/24 14:11:03
gsd-core 测试模式安装守卫:`finishInstall` 如何在 `GSD_TEST_MODE=1` 下跳过 opencode 权限配置(Bug 130 修复深度解析) gsd-core 测试模式安装守卫finishInstall如何在GSD_TEST_MODE1下跳过 opencode 权限配置Bug #130 修复深度解析【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core导读本文围绕 gsd-core 的安装器bin/install.js在 PR #130 中修复的一个具体缺陷展开此前finishInstall会无条件调用configureOpencodePermissions即便在测试模式GSD_TEST_MODE1下也会向磁盘写入opencode.json权限配置破坏测试模式零副作用契约并污染开发者的真实家目录。修复后测试模式安装不再写入 opencode 配置文件。读完本文你将掌握该修复的触发条件、底层实现含权限写入器的完整行为、配套回归测试的设计思路以及GSD_TEST_MODE在整个仓库测试体系中的角色从而理解测试模式侧效应守卫这一模式的落地方式。一、变更速览一份 changeset 记录了什么本仓库用 changeset 片段文件记录每个 PR 的变更意图解析器位于 scripts/changeset/parse.cjs它把以下 YAML front-matter 文本解析为类型化记录--- type: Fixed pr: 130 --- Test-mode installs no longer write the opencode config file — finishInstall now skips configureOpencodePermissions when GSD_TEST_MODE1.type: Fixed变更类型枚举中的缺陷修复全部合法类型见ALLOWED_TYPESAdded / Changed / Deprecated / Removed / Fixed / Securitypr: 130关联的 Pull Request 编号会被序列化进 CHANGELOG 与 GitHub release notes正文一句话概括修复行为即本文要展开的技术内核。二、Bug 的根因无条件执行的权限写入器在 bin/install.js 中安装收尾阶段由finishInstall函数承担。修复前的问题在于opencode 的权限配置调用不带任何测试模式守卫// Configure OpenCode permissions if (plan.finishPermissionWriter opencode !process.env.GSD_TEST_MODE) { configureOpencodePermissions(isGlobal, configDir); }源码位置bin/install.jsconfigureOpencodePermissions内部会执行两次真实的文件系统写入bin/install.jsfs.mkdirSync(opencodeConfigDir, { recursive: true })—— 确保~/.config/opencode/全局安装或./.opencode/本地安装存在fs.writeFileSync(configPath, JSON.stringify(config, null, 2) \n)—— 将包含权限规则的opencode.json或已存在的opencode.jsonc写回磁盘。问题随之而来测试套件在 CI 与本地反复运行安装流程时一旦走到该分支就会真实触碰磁盘若测试环境未做 HOME 隔离写入会落到真实开发者的~/.config/opencode/污染用户配置这与仓库测试模式必须无副作用的工程约定直接冲突。从仓库结构看这正是 tests/install.test.cjs 注释所归纳的契约Bug #130: finishInstall calls configureOpencodePermissions unconditionally, violating the GSD_TEST_MODE side-effect-free contract.三、修复实现条件守卫的精确落点修复的核心只有一行条件 !process.env.GSD_TEST_MODE。但它的语义比表面更细致需要结合finishInstall的整体调用链理解3.1 守卫只针对 opencode不波及同类写入器同一段收尾代码中还存在另外两个权限写入器bin/install.js// Configure Kilo permissions if (plan.finishPermissionWriter kilo) { configureKiloPermissions(isGlobal, configDir); } // Configure Antigravity permissions MCP companion server if (plan.finishPermissionWriter antigravity) { configureAntigravityPermissions(isGlobal, configDir); configureAntigravityMcpConfig(isGlobal, configDir); }Kilo 与 Antigravity 的写入器没有加GSD_TEST_MODE守卫。代码注释给出了理由这两个写入器只写该 runtime 自身 configDir 范围内的文件~/.config/kilo/、~/.gemini/antigravity*作用域天然受限而 opencode 写入器当时的行为模式被判定为违反零副作用契约因此单独加上守卫。这提示一个通用经验测试模式守卫应该精确施加在会以意外方式触碰外部状态的写入点上而不是一刀切地禁掉所有收尾逻辑。3.2plan.finishPermissionWriter描述符驱动的调度finishPermissionWriter来自resolveInstallPlan(runtime)是运行时描述符驱动的调度字段见 bin/install.js 等处的注释。也就是说finishInstall不再依赖硬编码的isOpencode布尔标志判断该调用谁而是按 plan 描述符分派。守卫条件因此与调度条件并列先确认当前 runtime 的权限写入器确实是 opencode再检查测试模式标志。3.3configureOpencodePermissions的完整行为了解守卫跳过了什么才能评估修复的边界。该函数完整流程bin/install.js定位配置目录本地安装用./.opencode/全局安装用getGlobalConfigDir(opencode, ...)解析出的~/.config/opencode/调用方也可显式传入configDir测试正是利用这一点把写入引向临时目录。选择配置文件resolveOpencodeConfigPathbin/install.js优先返回已存在的opencode.jsonc否则使用opencode.json。解析既有配置若文件已存在则用 JSONC 解析器读取解析失败时绝不覆盖用户配置打印警告后直接返回。幂等合并权限若顶层permission已是字符串如allow则跳过——路径级条目无必要否则确保permission.read与permission.external_directory两个对象中存在gsd-core/*路径的allow条目。默认目录用~/.config/opencode/gsd-core/*缩写非默认目录用完整 POSIX 路径。注册 companion MCP 服务ADR-1239 Phase D / #1682仅当mcp.gsd不存在时才写入{ type: local, command: [npx, -y, -p, PACKAGE_NAME, gsd-mcp-server], enabled: true }尊重用户自定义的mcp.gsd幂等且不覆盖。写回仅在确有修改时执行fs.writeFileSync。在GSD_TEST_MODE1下上述第 16 步被整体短路包括fs.mkdirSync与fs.writeFileSync——这正是测试断言的两类副作用。四、回归测试如何证明文件真的没被写对应回归测试被收纳进 tests/install.test.cjs由原独立测试文件经 consolidation epic #1969 折叠合并。它的设计值得逐层拆解process.env.GSD_TEST_MODE 1; // Point HOME at a temp dir so configureOpencodePermissions cant write to // the real ~/.config/opencode/ even if the guard is missing. const FAKE_HOME fs.mkdtempSync(path.join(os.tmpdir(), gsd-130-test-));第一道防线是环境隔离把HOME与USERPROFILE临时指向mkdtempSync创建的临时目录before/after钩子中保存与恢复原值避免泄漏到同文件内其他折叠套件。即便守卫未来回归消失写入也只能落在临时目录不会污染真实家目录。这是测试必须可失败、且失败也必须无害的经典做法。第二道防线是显式传入 configDirinstallModule.finishInstall( SETTINGS_PATH, {}, null, false, opencode, true, OPENCODE_CONFIG_DIR, // pass explicit configDir pointing at our temp dir );测试直接以opencode作为 runtime、以临时目录作为configDir调用finishInstall保证无论getGlobalDir如何解析写入目标都确定在FAKE_HOME/.config/opencode/内从而可以精确断言文件路径。核心断言test(configureOpencodePermissions does NOT write opencode.json under GSD_TEST_MODE, () { assert.equal(fs.existsSync(OPENCODE_CONFIG_FILE), false, opencode.json should not exist before finishInstall call); callFinishInstall(); assert.equal( fs.existsSync(OPENCODE_CONFIG_FILE), false, opencode.json must NOT be created under GSD_TEST_MODE; found at ${OPENCODE_CONFIG_FILE}, ); });两次existsSync断言构成前-后对照调用前不存在调用后仍不存在。同时该文件还被静默掉console.log保证测试输出干净。同类测试还覆盖了 opencode 与 antigravity runtime 在GSD_TEST_MODE下不写~/.gsd/defaults.jsonBug #410见 tests/install.test.cjs并验证了守卫移除后正常路径确实会写文件defaults.json IS written for opencode runtime when GSD_TEST_MODE is unset——正反两个方向都有证据。五、GSD_TEST_MODE的仓库级角色GSD_TEST_MODE1不是只为 #130 存在的孤立标志它是 gsd-core 测试体系中的通用测试模式开关全仓库大量代码路径都读取它禁止模块级副作用bin/install.js末尾if (require.main module !process.env.GSD_TEST_MODE)控制只有真实 CLI 入口才执行主流程bin/install.js跳过模型别名探测if (_hostBehaviors(runtime).nativeModelAliases || process.env.GSD_TEST_MODE) return;bin/install.js时间钉扎time-pinADT-456 测试严谨性架构规定对 CLI 子进程的确定性时间测试必须用GSD_TEST_MODE1GSD_NOW_MSepoch-ms经由src/clock.cts的realClock进入被测试代码见 docs/adr/456-test-rigor-architecture.md 与 TESTING-STANDARDS.md依赖加载豁免src/runtime-artifact-layout.cts通过保存/设置/恢复GSD_TEST_MODE来安全地require(bin/install.js)见 docs/adr/1508-runtime-artifact-conversion-module.md安装流程开关hooks/gsd-workflow-guard.js、scripts/release-tarball-smoke.cjs、src/real-home-guard.cts等大量测试相关模块均以该变量控制测试分支。可以把这一整套机制抽象为一条工程原则测试模式标志负责在测试进程中关闭一切不该发生的真实副作用而测试代码本身负责把可能残留的副作用导向临时目录两者配合才构成完整的隔离保证。#130 的修复属于前者FAKE_HOME隔离属于后者。六、如何复现与验证本修复以下方式可实测验证该守卫均在仓库只读前提下进行不会改动仓库文件直接运行回归测试node --test tests/install.test.cjs或按项目测试入口运行完整套件观察folded:bug-130-finishinstall-opencode-testmode套件通过。该套件在GSD_TEST_MODE1下调用finishInstall(opencode)并断言opencode.json未被创建。单独验证configureOpencodePermissions仓库还提供了独立测试文件 tests/opencode-permissions.test.cjs直接require(../bin/install.js)获取configureOpencodePermissions覆盖正常写入、幂等合并、解析失败不覆盖用户配置等场景tests/opencode-permissions.test.cjs。手动环境模拟在临时目录中设置export GSD_TEST_MODE1并调用finishInstall传入显式configDir指向另一个空目录检查该目录下是否出现opencode.json——按修复语义应当不存在取消GSD_TEST_MODE后重复调用则应当出现包含permission.read、permission.external_directory与mcp.gsd的配置文件。七、从 #130 可以复用的工程经验把测试模式无副作用写成可断言的契约仅靠口头约定无法防止回归必须用existsSync前-后对照这类断言把契约固化进测试守卫加在副作用点而非流程入口finishInstall仍执行 settings 写入、状态行配置等收尾逻辑只有会以意外方式触碰外部状态的 opencode 写入被短路最小化行为改变面测试环境隔离与守卫互为备份即使守卫失效FAKE_HOME也能把破坏限制在临时目录让失败可见但无害正反例成对出现#130 测试验证测试模式下不写#410 测试同时验证非测试模式下确实写避免守卫过度导致正常路径也被误伤。综上PR #130 是一个教科书级的测试模式侧效应守卫修复一行条件判断修复了真实用户目录被污染的风险配套的折叠回归测试则用双重隔离把该行为固化为长期契约为仓库内其他 runtime 的收尾写入器Kilo、Antigravity提供了可参照的模式。参考路径速查changeset 片段.changeset/archived/130-finishinstall-testmode-guard.md修复落点bin/install.js权限写入器实现bin/install.js配置路径解析bin/install.js回归测试tests/install.test.cjs独立单元测试tests/opencode-permissions.test.cjs测试模式时间钉扎约定TESTING-STANDARDS.md测试严谨性架构 ADRdocs/adr/456-test-rigor-architecture.md【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址: https://gitcode.com/gh_mirrors/ge/gsd-core创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询