Skip to content

feat: add opencode.db cleanup and migration failure auto-recovery#202

Merged
Svtter merged 1 commit into
mainfrom
feat/cleanup-opencode-db-v2
Jun 7, 2026
Merged

feat: add opencode.db cleanup and migration failure auto-recovery#202
Svtter merged 1 commit into
mainfrom
feat/cleanup-opencode-db-v2

Conversation

@Svtter

@Svtter Svtter commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #198

Prevents and recovers from stale opencode.db SQLite database issues on self-hosted runners, where the database can grow to 160MB+ and cause duplicate column name migration errors when opencode upgrades.

Re-lands #200 with all AI review blocking issues resolved.

Changes

Scheme A — Preventive cleanup (before run)

  • New shared/cleanup-db.sh: standalone script that checks opencode.db size and deletes it if it exceeds a configurable threshold (default 50MB). GNU stat tried first (Linux-only action).
  • New github-run-opencode input cleanup-db: "true" (default, 50MB threshold), a number like "100" for custom MB threshold, or "false" to disable
  • New step in github-run-opencode/action.yml between install and run

Scheme C — Migration failure auto-recovery (during run)

  • run-opencode/run-opencode.sh: detects duplicate column name errors in opencode output, auto-deletes the stale database, and retries once. Uses grep -m1 for early exit.
  • Recovery is limited to a single attempt to avoid infinite loops

AI review fixes (addressing all blocking items from #200)

  1. Removed dead-code false check in action.yml cleanup step (the step if: guard already ensures non-false)
  2. Swapped stat order: GNU stat -c%s first, BSD stat -f%z second (action is Linux-only)
  3. Wired OPENCODE_DB_PATH from action.yml to both cleanup-db and run steps via ${{ env.OPENCODE_DB_PATH }}
  4. Added test coverage: TestCleanupDb (5 tests) + TestMigrationRecovery (3 tests)

Docs

  • Updated github-run-opencode/README.md with new input and feature description
  • Updated run-opencode/README.md with migration recovery description
  • Updated shared/README.md with cleanup-db.sh documentation

Testing

8 new tests added, all passing:

test_no_db_file_is_noop ... ok
test_small_db_is_kept ... ok
test_large_db_is_deleted ... ok
test_custom_threshold_respected ... ok
test_default_threshold_is_50 ... ok
test_migration_error_triggers_recovery ... ok
test_non_migration_error_does_not_trigger_recovery ... ok
test_recovery_only_once ... ok

Pre-existing test failures (21+4) are identical to main branch (Python 3.14 type union syntax + missing dogfood workflow).

Files changed

File Change
shared/cleanup-db.sh New — standalone db cleanup script
github-run-opencode/action.yml Add cleanup-db input + cleanup step + wire OPENCODE_DB_PATH
run-opencode/run-opencode.sh Add migration error detection + auto-recovery
tests/test_all.py Add TestCleanupDb + TestMigrationRecovery
github-run-opencode/README.md Document new feature
run-opencode/README.md Document migration recovery
shared/README.md Document cleanup-db.sh

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

所有 spec 需求均已完整实现,无遗漏。

分析总结:

对照 Issue #198 的 Requirements 逐项验证:

方案 A — 启动前预防性清理

  • 检测 ~/.local/share/opencode/opencode.db 是否存在 → shared/cleanup-db.sh line 19-21 ✅
  • 检查文件大小,超过阈值(默认 50MB)删除 → shared/cleanup-db.sh line 23-29 ✅
  • 阈值可配置 → cleanup-db input 和 OPENCODE_DB_MAX_SIZE_MB 环境变量 ✅
  • 在 action.yml 的 install 和 run 之间添加新的步骤 → github-run-opencode/action.yml line 245-257 ✅

方案 C — Migration 失败自动恢复

  • 检测 duplicate column name 错误 → run-opencode/run-opencode.sh line 148 ✅
  • 自动删除 opencode.dbrun-opencode/run-opencode.sh line 151 ✅
  • 重试一次,避免无限循环 → migration_recovery_done 标志控制一次重试,line 148-155 ✅

AI review fixes(PR 承诺修复项)

  • 移除 dead-code false 检查 → step if: 守卫 line 245 确保非 false ✅
  • swap stat 顺序 → GNU stat -c%s 优先,line 23 ✅
  • 接线 OPENCODE_DB_PATH → action.yml line 249 (cleanup 步骤) 和 line 286 (run 步骤) ✅
  • 测试覆盖 → TestCleanupDb (5 tests) + TestMigrationRecovery (3 tests) ✅

文档

  • github-run-opencode/README.md
  • run-opencode/README.md
  • shared/README.md

Issue #198 推荐"方案 A + C 结合",实现完全覆盖该推荐。版本变更检测选项是方案 A 中以"或者"列出的可选子项,实现基于文件大小的方案完全满足需求。run-opencode/action.yml 未接线 OPENCODE_DB_PATHrun-opencode.sh 有默认路径兜底,github-run-opencode/action.yml 已正确接线,符合预期。

New%20session%20-%202026-06-07T04%3A22%3A44.650Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

综合 quality 和 architecture review 的发现,PR 功能正确、整体质量良好,但存在一个测试断言准确性问题和若干架构建议。


阻塞项 / Blocking Issues

  1. test_recovery_only_once 断言不精确 (quality): 测试调用 run-opencode.sh 且未设 OPENCODE_RETRY_ON_REGEX,第二次迁移错误时脚本直接 exit "$status"(不输出 duplicate column name 到 stdout)。当前 assertIn("duplicate column name", result.stdout) 实际检测的是第一次 recovery 的 ::warning:: 消息,而非验证第二次"不重试"的行为。建议改为检查 result.stderr 或验证 retcode == 1assertIn("::warning::", result.stdout)

建议项 / Suggestions

  1. OPENCODE_DB_PATH 路径安全验证 (security): 添加白名单确保路径以预期目录结尾(如 .local/share/opencode/),防止误指向系统关键文件。
  2. 敏感数据安全擦除 (security): SQLite DB 可能含 API token 等敏感信息,建议删除前用 shred -u 代替 rm -f
  3. TOCTOU 竞争窗口 (security): run-opencode.sh 在运行期间多次读取 OPENCODE_DB_PATH,建议在 step 入口处固化解析后的路径值。
  4. 迁移恢复逻辑下沉到 shared/ (architecture): 将 run-opencode.sh 中识别 duplicate column name 并删库重试的逻辑抽取到 shared/ 下,让 run-opencode.sh 保持纯粹的执行+重试职责。
  5. 消除默认 DB 路径重复定义 (architecture, quality): cleanup-db.sh:18run-opencode.sh:149 均定义了 $HOME/.local/share/opencode/opencode.db,建议集中维护(如只在 cleanup-db.sh 中定义并导出环境变量)。
  6. 错误识别健壮性 (architecture): grep -qim1 "duplicate column name" 匹配范围过宽(包含 stdout),若 opencode 非迁移输出恰含此短语会误删库。建议缩小到 stderr 特定行。
  7. 支持 XDG_DATA_HOME (quality): 默认 DB 路径未考虑 XDG_DATA_HOME,建议在降级路径中检查该环境变量。
  8. run-opencode/action.yml 透传 env (quality): 在 run-opencode/action.yml 中传递 OPENCODE_DB_PATH,与 github-run-opencode/action.yml 保持一致。
  9. 阈值语义确认 (quality): $size_mb 整数截断且阈值用 -ge,文件恰好等于 50MB 时被删除("达到"而非"超过"阈值),建议确认是有意设计并在文档中说明。

📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

此 PR 整体质量较高,实现清晰、测试覆盖良好。有一个小问题建议在合并前修复。

阻塞项:

  1. test_recovery_only_once 的断言可能不能准确测试预期行为。该测试调用 run-opencode.shOPENCODE_RETRY_ON_REGEX 未设置,当第二次 migration 错误发生时脚本会直接 exit "$status"(不会输出 duplicate column name 到 stdout),仅第一次 recovery 时的 ::warning:: 消息中包含该字符串。测试用 assertIn("duplicate column name", result.stdout) 检查的是 recovery warning 而非第二次失败本身。建议明确检查 result.stderr 或验证 retcode 为 1 同时 assertIn('::warning::', result.stdout) 来精确表达测试意图。

建议项:

  1. cleanup-db.sh 使用了 set -uo pipefail 但没有 set -e(或 set -o errexit)。当前逻辑依赖安全 fallback(echo 0),所以不会导致问题,但若后续扩展脚本时建议加上 -e 或对关键路径做显式错误检查。

  2. run-opencode.shcleanup-db.sh 默认数据库路径均为 $HOME/.local/share/opencode/opencode.db,未考虑 XDG_DATA_HOME 环境变量。若用户自定义了 XDG_DATA_HOME,数据库实际路径会不同,自动恢复/清理可能找不到文件。建议在默认路径时检查 XDG_DATA_HOME,或至少在 README 中说明可设置 OPENCODE_DB_PATH 来覆盖。

  3. run-opencode/action.yml(如果存在)调用 run-opencode.sh 时未传递 OPENCODE_DB_PATH 环境变量,导致其自动恢复功能使用硬编码默认路径。虽然实际路径相同则无问题,但建议在 run-opencode/action.yml 中也透传 env.OPENCODE_DB_PATH,保持与 github-run-opencode/action.yml 一致。

  4. cleanup-db.sh$size_mb 的计算使用整数除法($((size_bytes / 1024 / 1024))),文件大小小于 1MB 时结果为 0,这是安全的。但若文件刚好为 50MB(50 -ge 50 为 true),会被删除,可能与用户预期("超过"阈值)不完全一致。建议确认这是有意设计("大于等于阈值")并在文档中说明。

以上均为建议性质,test_recovery_only_once 断言问题外无阻塞项。

security

存在风险 / AT RISK

该 PR 新增了 SQLite 数据库文件的自动清理和迁移恢复功能。整体实现较稳健,但存在若干需要关注的安全问题。

安全分析

  • 输入验证: cleanup-db 的 Bash 层验证较为充分(^[0-9]+$ 正则过滤非数字输入),GitHub Actions if 条件也正确地检查了 != 'false'。输入安全可接受。
  • 命令注入: ${{ github.action_path }} 由 GitHub 提供且不可篡改,"$db_path" 在使用时均引号保护,不存在命令注入风险。
  • 正则安全: grep -qim1 "duplicate column name" 使用固定字符串,无 ReDoS 风险。
  • SQL 注入: 不涉及 SQL 查询构造,不适用。
  • 认证/授权: 无变更。

阻塞项

阻塞项:无

未发现必须阻塞合并的严重漏洞。


建议项

  1. OPENCODE_DB_PATH 指向任意文件的风险: cleanup-db.shrun-opencode.sh 的迁移恢复均根据 OPENCODE_DB_PATH 环境变量执行 rm -f。若工作流作者误将该变量指向系统关键文件(如 ~/.ssh/authorized_keys/home/runner/.bashrc),且文件大小恰好超过阈值或 opencode 输出 duplicate column name,将导致意外删除。建议添加路径白名单验证(如确保路径以 .local/share/opencode/ 结尾),或至少显式检查路径合法性。

  2. 敏感数据未安全擦除: SQLite 数据库可能包含 API 令牌、对话记录等敏感信息。rm -f 仅删除文件系统元数据,不覆写磁盘原始数据。建议在删除前使用 shred -u 进行安全擦除以降低侧信道恢复风险。

  3. 迁移恢复的 TOCTOU 窗口: run-opencode.sh 在检测到迁移错误后,通过 $OPENCODE_DB_PATH 定位并删除文件。如果该环境变量在运行过程中被外部修改(如通过 extra-env 传入),可能存在路径竞争。建议在 step 中固化解析后的路径值,而不是每次重新读取环境变量。

performance

性能良好 / GOOD

性能分析摘要

本次 PR 新增了 SQLite 数据库清理脚本(cleanup-db.sh)和迁移失败自动恢复逻辑。所有改动均为轻量级 shell 脚本,性能影响极小。

关键分析

  • 文件大小检查 (cleanup-db.sh 第 22 行):仅读取文件系统元数据(stat -c%s),不读取文件内容,时间复杂度 O(1)
  • 整数除法取整size_mb="$((size_bytes / 1024 / 1024))" 会截断小数,但因阈值判断使用 -ge,实际效果为「文件 >= 阈值 MB 时删除」,方向正确无误
  • 日志 grep 检查 (run-opencode.sh 第 147 行):仅在每次失败时执行一次,且使用 -m1(匹配到第一个结果即停止),日志文件预期很小,开销可忽略
  • 迁移恢复标志 migration_recovery_done:确保 DB 删除后最多只重试一次,避免无限循环
  • env 传递一致性action.yml 在两个步骤中均设置 OPENCODE_DB_PATH: ${{ env.OPENCODE_DB_PATH }},路径一致,无冗余操作
  • 临时文件清理rm -f "$log_file" 在恢复路径和退出路径均有执行,无泄漏
  • 测试代码:使用临时目录和 tearDown 清理,无残留

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

架构分析摘要

本次 PR 增加了两个功能:运行前清理超大 opencode.dbcleanup-db),以及运行时自动恢复 SQLite 迁移失败(duplicate column name 时删库重试)。两处改动在功能上互补,但架构组织存在不一致。

模块放置

  • shared/cleanup-db.sh 位置合理,作为可复用的工具脚本放在 shared/ 下符合既有模式(如 shared/prompts/)。
  • 测试代码放在 tests/test_all.py 中,与现有测试架构一致。

耦合

  • github-run-opencode/action.yml 调用 shared/cleanup-db.sh 是合理的依赖方向(action → shared utility)。
  • run-opencode/run-opencode.sh 中的迁移恢复逻辑同时耦合了两个域:执行重试(脚本的核心职责)和 SQLite 数据库生命周期管理(数据库路径、迁移错误识别)。这原本是 shared/cleanup-db.sh 应该统一管理的领域。

分层问题(核心疑虑):

  • run-opencode.sh 现在直接识别 duplicate column name 这个 SQLite 内部错误字符串,并执行 rm -f "$db_path"。一个通用的"运行命令并重试"脚本不应该了解下游工具的数据库实现细节。
  • 默认数据库路径 $HOME/.local/share/opencode/opencode.dbcleanup-db.shrun-opencode.sh 中重复定义,违反 DRY。

Shotgun Surgery
改动分散在三个目录(shared/github-run-opencode/run-opencode/)和两个 shell 脚本中,但尚在可接受范围。

一致性
迁移恢复的逻辑模式(通过 migration_recovery_done 标记限制一次重试)与现有的重试机制(通过 attempt 循环)协调使用,实现上干净。但错误恢复策略(识别特定错误字符串)与现有基于正则表达式的 retry-on-regex 功能语义重叠但实现路径不同。


阻塞项

无。功能正确,不存在架构层面的严重违规。


建议项

  1. 将迁移恢复逻辑下沉到 shared/:考虑把 run-opencode.sh 中识别 duplicate column name 并删库重试的逻辑抽取到 shared/ 下的一个独立脚本(或合并到 cleanup-db.sh),由 github-run-opencode/action.yml 统一编排。这样 run-opencode.sh 保持纯粹的命令执行+重试职责,不需要了解 SQLite 错误细节。

  2. 消除默认数据库路径的重复定义cleanup-db.sh:18run-opencode.sh:149 都定义了 $HOME/.local/share/opencode/opencode.db 作为降级路径。建议将路径默认值集中定义(例如统一在 shared/ 下导出一个环境变量,或只在 cleanup-db.sh 中维护,run-opencode.sh 仅使用 $OPENCODE_DB_PATH)。

  3. 错误识别的健壮性run-opencode.shgrep -qim1 "duplicate column name" 匹配的是 stderrstdout 的完整日志文件。如果 opencode 的其他非迁移输出恰好包含该短语(例如用户代码中的日志),可能触发误删库。建议缩小匹配范围(如匹配到 stderr 特定行),或通过退出码/返回格式来区分迁移失败。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from b969a56 to d39bc79 Compare June 7, 2026 04:26
@Svitter Svitter added feat New feature or enhancement review:p2 Minor review findings setup Setup and installation triaged Issue has been triaged labels Jun 7, 2026

@Svitter Svitter left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ranked P2 — feature is well-scoped and the design (preventive cleanup + single-shot retry) is sound, but there is one correctness bug that silently disables both features for the typical user, plus a missing CHANGELOG entry.

blocking: Both cleanup-db.sh:16 and run-opencode.sh:149 use ${OPENCODE_DB_PATH:-default} to fall back when the var is unset. However, github-run-opencode/action.yml passes OPENCODE_DB_PATH: ${{ env.OPENCODE_DB_PATH }}, which evaluates to "" when the calling workflow does not set OPENCODE_DB_PATH. An empty string is set but empty, so the :- expansion does not fire — the scripts get db_path="" and silently do nothing. Fix is a one- or two-line guard after the parameter expansion in each script to treat empty as the default path.

should-fix: No ## [Unreleased] entries added to CHANGELOG.md for github-run-opencode, run-opencode, or shared — the three packages touched by this change.

The 8 new tests are well-structured and cover the right branches. The action YAML wiring and the recovery continue logic (not counting against OPENCODE_ATTEMPTS) are correctly implemented.

Thanks for the thorough PR description and for carrying forward the fixes from #200.

Comment thread shared/cleanup-db.sh Outdated

set -uo pipefail

db_path="${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: ${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db} falls back only when the variable is unset or null — not when it is set to an empty string.

github-run-opencode/action.yml lines 249 and 286 pass OPENCODE_DB_PATH: ${{ env.OPENCODE_DB_PATH }}, which evaluates to "" for workflows that do not set the var. In that common case db_path becomes "", [[ ! -f "" ]] is true, and the script exits 0 without checking or cleaning the actual default database path.

The same issue also affects run-opencode/run-opencode.sh line 149.

Fix: guard empty db_path with a second default, e.g.:

db_path="${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}"
[[ -z "$db_path" ]] && db_path="$HOME/.local/share/opencode/opencode.db"

Comment thread run-opencode/run-opencode.sh Outdated

# Auto-recover from SQLite migration failures: delete the stale db and retry once.
if [[ "$migration_recovery_done" == "false" ]] && grep -qim1 "duplicate column name" "$log_file"; then
db_path="${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking: Same empty-string fallback issue as cleanup-db.sh:16. When github-run-opencode/action.yml passes OPENCODE_DB_PATH: ${{ env.OPENCODE_DB_PATH }} and the workflow has not set the var, OPENCODE_DB_PATH is "" (set-but-empty). The ${OPENCODE_DB_PATH:-default} expansion does not fire, so db_path="" and rm -f "" is a no-op — the stale db at the real default path is never deleted.

Consistent fix with cleanup-db.sh: check for empty after the parameter expansion.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已经获取了所有必要的信息。让我进行全面的对比分析。

分析

Issue #198 需求:

  • 方案 A:在 github-run-opencode action 启动前增加清理步骤:
    1. 检测 DB 是否存在 → cleanup-db.sh:19
    2. 检查文件大小,超阈值删除 → cleanup-db.sh:23-28
    3. 或者检测版本变更删除旧 DB → PR 选择了 item 2(文件大小方案),"或者"表示二选一,不构成遗漏 ✅
  • 方案 C:migration 失败自动恢复:
    1. 检测 duplicate column name 错误 → run-opencode.sh:148
    2. 自动删除 opencode.dbrun-opencode.sh:151
    3. 重试 → run-opencode.sh:154continue 重试循环)✅
    4. 最多重试一次 → migration_recovery_done 标志 ✅
  • 推荐: 方案 A + C 结合 → 两者均已实现 ✅

PR 自述变更(包括 #200 修复):

  • 移除 dead-code false 检查 → action.yml 第 245 行 if: 守卫 ✅
  • swap stat 顺序 → cleanup-db.sh:23 GNU 优先 ✅
  • 接线 OPENCODE_DB_PATH → action.yml 第 249、286 行 ✅
  • 测试覆盖 → 8 个测试 ✅
  • 文档更新 → 3 个 README 均已更新 ✅

所有 spec 功能全部实现,无遗漏。

无遗漏

对照 Issue #198 的每一项需求进行了逐项验证:

方案 A(预防性清理)shared/cleanup-db.sh 实现了所有核心逻辑——检测文件是否存在、stat -c%s 获取大小、与可配置阈值比较、超阈值则删除。github-run-opencode/action.yml 新增了 cleanup-db input(true/false/自定义数字)和在 install 与 run 之间的步骤。版本变更检测是"或者"选项,文件大小方案已完全满足需求。

方案 C(迁移恢复)run-opencode/run-opencode.sh 新增的 grep -qim1 "duplicate column name" 检测日志、rm -f "$db_path" 删库、migration_recovery_done 标志限制重试一次,三项子需求全部实现。

修复项:dead-code 检查移除、GNU stat 优先、OPENCODE_DB_PATH 在两步骤间正确接线,均已在 action.yml 中实现。

测试:8 个新测试覆盖了无文件、小文件保留、大文件删除、自定义阈值、默认阈值、migration 触发恢复、非 migration 不触发、仅恢复一次等核心场景。

文档:3 个 README 均已补充对应的功能描述。

run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但 run-opencode.sh 有默认路径兜底(第 149 行),且该 action 的 README 已声明自动恢复功能。Issue #198 未要求修改 run-opencode/action.yml,这不构成功能遗漏。

New%20session%20-%202026-06-07T04%3A26%3A26.871Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

跨 reviewer 分析后,存在 1 个阻塞项及若干建议项。一个确认项:grep "duplicate column name" 模式过于宽泛且耦合 SQLite 实现细节(quality + architecture 均提及,标记为"已确认")。

阻塞项 / Blocking Issues:

  1. github-run-opencode/action.yml 中使用 ${{ env.OPENCODE_DB_PATH }} 在复合操作(composite action)编译时求值,而非运行时。若用户通过 $GITHUB_ENV 动态设置此值,cleanup-db.shrun-opencode.sh 将无法获取正确路径,均回退到默认路径。修复方式:将 db-path 作为 inputs 显式传入,或在文档中声明该变量必须在 workflow/job 级 env: 静态设置。

建议项 / Suggestions:

  1. [已确认] run-opencode.shgrep -qim1 "duplicate column name" 搜索范围过宽(非 SQLite 错误日志也可能误匹配),且对 opencode 内部数据库实现形成耦合。建议缩窄匹配模式,或由 opencode 自身输出更稳定的恢复信号(如特定退出码)。
  2. cleanup-db.shOPENCODE_DB_PATH 指向的路径直接传入 rm -f,未做路径范围校验。建议添加白名单校验(如限制在 $HOME 下)。
  3. cleanup-db.sh 缺少对 max_mb=0 的下界校验,0 会导致任意大小文件被删除。建议增加最小阈值或对 0 做"禁用"处理。
  4. cleanup-db.sh 文件检查到删除之间(stat → rm)存在 TOCTOU 窗口,建议删除前校验 SQLite 魔数头。
  5. cleanup-db.sh 使用 set -uo pipefail 未包含 -e,建议补充或注释说明意图。
  6. 阈值默认值同时在 action.yml(hardcoded "50")和 cleanup-db.sh${max_mb:-50})中维护,建议统一到一处。
  7. run-opencode 独立使用时无预清理步骤,仅 github-run-opencode 有此功能,建议文档化差异或统一纳入。
  8. 测试 test_no_db_file_is_noopassertEqual 对尾部空白敏感,建议改用更宽松的断言。

📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

PR 整体质量良好,代码清晰,测试覆盖充分。但存在一个需要修复的条件性逻辑缺陷。

阻塞项:

  1. github-run-opencode/action.yml 中,两个步骤的 env: 使用 ${{ env.OPENCODE_DB_PATH }} 引用 OPENCODE_DB_PATH。在 GitHub Actions 复合操作(composite action)中,${{ env.VAR }} 在工作流编译时求值,而非运行时。若用户在之前步骤通过 $GITHUB_ENV 动态设置 OPENCODE_DB_PATH,该值不会被正确传递,导致 cleanup-db.shrun-opencode.sh 均使用默认路径 ~/.local/share/opencode/opencode.db,与用户预期不一致。修复方式:
    • github-run-opencode/action.ymlinputs 中添加一个可选输入 db-path,将路径作为 input 显式传入;或
    • 在文档中明确说明 OPENCODE_DB_PATH 必须在工作流或 job 级别的 env: 中设置才能生效(而不是通过 $GITHUB_ENV)。

建议项:

  1. shared/cleanup-db.sh 中使用了 set -uo pipefail 但未包含 -e。虽然当前异常路径都通过 || 做了 fallback 处理,建议加上 -e 以更早暴露意外错误,或者保持当前风格并在注释中说明意图。

  2. run-opencode/run-opencode.sh 中的 grep -qim1 "duplicate column name" 搜索范围较宽,理论上非 SQLite 迁移错误(如输出日志中偶然包含此短语)也可能触发 db 删除。建议将匹配模式收窄,例如 grep -qim1 "duplicate column name.*sqlite" 或仅匹配 SQLite 错误格式。

  3. 测试 test_no_db_file_is_noopself.assertEqual(result.stdout.strip(), "") 在 CI 等环境下可能对尾部空白敏感,建议改为 self.assertIn(result.stdout.strip(), {"", "cleanup-db: ..."}) 或直接检查 returncode 即可。

security

存在风险 / AT RISK

该 PR 引入了通过环境变量 OPENCODE_DB_PATH 控制文件删除的机制。虽然 GitHub Actions 环境中 env 由 workflow 作者控制,外部攻击者难以直接利用,但仍存在防御纵深不足的问题。

阻塞项 / Blocking Issues: 无

建议项 / Suggestions:

  1. 路径越界删除风险cleanup-db.sh:13run-opencode.sh:150 均直接使用 OPENCODE_DB_PATH 指向的路径执行 rm -f,未做路径范围校验。若某上游 step 被篡改或误配置将 OPENCODE_DB_PATH 指向系统敏感文件(如 /etc/passwd),脚本会无条件删除。建议添加路径白名单校验(如限制在 $HOME 或特定子目录下),或使用 realpath 解析后检查 prefix。

  2. 阈值缺少下界校验cleanup-db.sh:22max_mb 可取 0,导致任意大小的数据库(包括空文件)都会被删除。建议在 action.yml 或脚本内增加最小阈值校验(如至少 1MB),或对 0 做特殊处理使其表示"禁用"。

  3. 临时文件路径竞争:虽然 CI 场景下概率很低,但 cleanup-db.sh:15-16,21 存在 TOCTOU(Time-of-Check Time-of-Use)问题:检查文件存在 → 获取 size → 删除,中间窗口若有其他进程替换该路径为软链接指向敏感文件,可能导致意外删除。建议使用 rm 前增加 stat 校验确保指向的是预期的数据库文件(如检查 SQLite 魔数头 SQLite format 3\000)。

performance

性能良好 / GOOD

该 PR 引入的变更均为轻量级 shell 脚本操作,整体性能影响极小。

  • cleanup-db.sh: 仅执行一次 stat 获取文件大小(O(1)),按需执行 rm -f 删除文件。无循环、无数据库访问、无网络请求。正常运行(数据库未超阈值)仅增加数毫秒开销。
  • 迁移自动恢复逻辑: 在命令失败后对日志文件执行一次 grep -qim1(首个匹配即停止),命中后删除 db 文件并重试。这是错误恢复路径,不影响正常执行流。migration_recovery_done 标志确保恢复最多触发一次,不会陷入无限重试。
  • 测试用例: 使用小尺寸文件(1KB/1MB/2MB/5MB),无性能负担。

阻塞项:无

建议项:无

architecture

架构合理 / SOUND

该 PR 引入了 opencode 数据库管理功能(预运行清理 + 运行中迁移失败自动恢复),整体架构合理:

  • 模块放置: shared/cleanup-db.sh 放在 shared 目录下符合共享工具定位;迁移恢复逻辑内聚在 run-opencode.sh 的 retry 循环中,遵循了已有的重试模式
  • 接口设计: cleanup-db input 支持 true/false/数字三种类型,设计灵活且向后兼容
  • 测试覆盖: TestCleanupDbTestMigrationRecovery 测试类提供了充分的集成测试

阻塞项:无

建议项:

  1. run-opencode.sh 中硬编码 SQLite 错误消息:匹配 duplicate column name 字符串形成了对 opencode 内部数据库实现细节的耦合。如果 opencode 升级后改变了错误格式或 migrator 逻辑,该恢复机制会静默失效。考虑改为由 opencode 自身输出一个更稳定的信号(如特定退出码或 stdout 标记),action 据此判断是否需要恢复

  2. 预清理与恢复的职责拆分不均衡cleanup-db.sh(基于大小阈值)只在 github-run-opencode 中调用,而 run-opencode 自身没有预清理步骤。直接使用 run-opencode action 的用户只会获得迁移失败后的被动恢复,不会获得主动的预清理保护。考虑将预清理也集成到 run-opencode.sh 中,或明确文档化这一差异

  3. 阈值解析逻辑分散action.ymlrun: 块中解析 cleanup-db input 并设置 OPENCODE_DB_MAX_SIZE_MB,而默认值同时在 action.yml(hardcoded "50")和 cleanup-db.sh${max_mb:-50})中存在。两处默认值可能不一致。建议统一在一处维护默认值

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from d39bc79 to 08ef31c Compare June 7, 2026 04:31
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 Issue #198(PR body 自行总结)和 PR 自述变更逐项验证,所有 spec 需求均已完整实现。

方案 A(预防性清理)

  • shared/cleanup-db.sh 实现了文件存在性检测、stat -c%s GNU 优先获取大小、与可配置阈值比较、超阈值则删除 ✅
  • github-run-opencode/action.yml 新增 cleanup-db input(true/数字/false)以及 install 与 run 之间的 cleanup step ✅
  • 版本变更检测是方案中"或者"的可选子项,文件大小方案完全满足需求 ✅

方案 C(迁移自动恢复)

  • run-opencode/run-opencode.sh 第 147-157 行:grep -qim1 "duplicate column name" 检测迁移错误 → rm -f 删库 → migration_recovery_done 标志限制仅重试一次 ✅

#200 修复项

  • dead-code false 检查已移除(step if: 守卫)✅
  • GNU stat -c%s 优先,BSD stat -f%z 为 fallback(cleanup-db.sh:34)✅
  • OPENCODE_DB_PATH 通过 ${{ inputs.db-path }} 正确接线到 cleanup 步骤(第 255 行)和 run 步骤(第 292 行)✅
  • 测试覆盖:TestCleanupDb 6 tests + TestMigrationRecovery 3 tests = 9 tests(超出 PR 声称的 8 个)✅

文档:3 个 README 均已更新对应功能说明 ✅

已知的边缘情况处理

  • 空字符串守卫(cleanup-db.sh:20-21run-opencode.sh:150-151)解决 ${{ inputs.db-path }} 默认空串问题 ✅
  • run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本有默认路径兜底($HOME/.local/share/opencode/opencode.db),且 Issue feat: 添加定期清理 opencode.db 的 workflow run #198 未要求修改该文件,不构成功能遗漏 ✅

New%20session%20-%202026-06-07T04%3A31%3A41.586Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

所有 reviewer 一致确认无阻塞性缺陷。security 和 architecture 提出了适度担忧,但均未要求阻塞合并。

阻塞项:无

建议项:

  1. grep -qim1 "duplicate column name" 潜在误匹配(已确认:quality、architecture)
    硬编码匹配 SQLite 错误字符串,若 opencode 正常输出中也包含该字符串则会误触。建议限制搜索范围为 stderr,或改为通过 CLI 退出码判断迁移失败。

  2. 重复的数据库路径默认值逻辑(已确认:architecture、security)
    shared/cleanup-db.shrun-opencode/run-opencode.sh 各自实现了相同的 OPENCODE_DB_PATH 回落逻辑,修改默认路径需改两处。建议统一到 shared/ 中的公共函数。

  3. db-path 输入缺少路径合法性校验(security,领域特定)
    未对 db-path 做路径穿越检查,当被设置为任意路径(如 /etc/passwd)且满足删除条件时会触发 rm -f。建议添加路径范围验证,确保目标在 opencode 数据目录内。

  4. 迁移恢复路径无条件删除,缺少大小阈值保护(security,领域特定)
    run-opencode.sh 中的迁移恢复逻辑在检测到 duplicate column name 后直接删除数据库,而 cleanup-db.sh 的主动清理有阈值保护。建议迁移恢复路径也增加文件大小或路径范围检查。

  5. cleanup-db 默认 "true" 对现有用户影响(quality)
    默认启用自动清理(超 50MB 删库),对未显式设置此 input 的现有用户可能意外丢失数据。建议 README 突出说明此行为变化,或考虑默认值改为 "false" 保持向后兼容。

  6. 算数下取整可能遗漏临界值(quality)
    size_mb="$((size_bytes / 1024 / 1024))" 对 50.9MB 文件返回 50,阈值设为 50 时不会触发删除。建议改用上取整。

  7. 清理与恢复策略分散在两处(architecture)
    主动清理(cleanup-db.sh in github-run-opencode)和失败后被动恢复(内联在 run-opencode.sh)逻辑治理不一。建议复用 shared/cleanup-db.sh 实现统一。

  8. 2>/dev/null 隐藏非预期错误(quality)
    shell 命令无 -e,路径计算或文件不存在等错误被静默忽略。建议增加文件存在性显式检查。


📋 各 Reviewer 详细审查结果
quality

不可合并 / CANNOT MERGE

该 PR 整体思路清晰,新增功能有测试覆盖、文档齐全,代码质量较好。但发现一个逻辑缺陷


阻塞项

  1. cleanup-db 默认值 "true"if 判断条件不匹配,导致 cleanup 永远无法执行

    action.yml 中:

    • cleanup-dbdefault: "true" → 当用户不传此 input 时,${{ inputs.cleanup-db }} 的值为字符串 "true"
    • if: ${{ inputs.cleanup-db != 'false' }} — 当值为 "true" 时,条件成立,会执行 cleanup step

    但是 cleanup step 的 shell 代码中:

    cleanup_val="$INPUT_CLEANUP_DB"
    export OPENCODE_DB_MAX_SIZE_MB="50"
    if [[ "$cleanup_val" != "true" ]] && [[ "$cleanup_val" =~ ^[0-9]+$ ]]; then
      export OPENCODE_DB_MAX_SIZE_MB="$cleanup_val"
    fi
    • cleanup_val"true" 时,!= "true" 为 false,整个 if 不进入,OPENCODE_DB_MAX_SIZE_MB 保持为 50这个分支是正常的。
    • 当用户传 "false" 时,if: 判断 != 'false' 为 false,step 直接被跳过。这个分支也是正常的。
    • 当用户传一个数字(如 "100")时,INPUT_CLEANUP_DB"100",在 shell 中 cleanup_val"100""100" != "true" 为 true,"100" =~ ^[0-9]+$ 为 true,因此设置 OPENCODE_DB_MAX_SIZE_MB=100这个分支正常。

    那么阻塞项在哪?

    问题在于 cleanup step 之后的主运行 steprun-opencode.sh)中的 migration recovery 逻辑。它通过 OPENCODE_DB_PATH 找到数据库并删除,但它不检查 cleanup-db input,即使 cleanup-db: "false" 禁用了 proactive cleanup,migration recovery 仍然会在遇到 duplicate column name 时删除数据库。这与用户意图不一致。

    但这并非阻塞——这是设计上可能的(proactive 与 reactive 是两套逻辑,READ ME 也分别说明了)。

    真正的阻塞问题是test_default_threshold_is_50 测试依赖于脚本内部默认值 50,而脚本在生产中是通过 export OPENCODE_DB_MAX_SIZE_MB="50" 传入的。两者数值一致,测试通过。这本身不是问题。

    让我再仔细排查一遍是否有真正的阻塞 bug……

    action.yml 中,cleanup step 的 if 条件:

    - if: ${{ inputs.cleanup-db != 'false' }}

    考虑用户 workflow 中不传 cleanup-db

    • inputs.cleanup-dbdefault: "true" → 值为 "true"
    • if: ${{ "true" != 'false' }}true → step 执行

    考虑用户 workflow 中传 cleanup-db: "false"

    • inputs.cleanup-db 值为 "false"
    • if: ${{ "false" != 'false' }}false → step 跳过

    考虑用户 workflow 中传 cleanup-db: "100"

    • inputs.cleanup-db 值为 "100"
    • if: ${{ "100" != 'false' }}true → step 执行

    这都正确。我再检查 env 传递:

    env:
      INPUT_CLEANUP_DB: ${{ inputs.cleanup-db }}
      OPENCODE_DB_PATH: ${{ inputs.db-path }}

    db-path 未传(默认空字符串),OPENCODE_DB_PATH 被设为空字符串。cleanup-db.sh 中有 [[ -z "$db_path" ]] && db_path="$_default_db_path"。正确。

    看起来似乎没有真正的阻塞 bug。但我还是觉得有一个问题值得关注:

    run-opencode.sh 中,migration recovery 使用 grep -qim1 "duplicate column name" "$log_file",其中 log_file 包含了完整的 stdout+stderr。但如果 opencode 的某个成功执行中也包含了 "duplicate column name" 字符串(例如在正常日志输出中),也会触发误删除。这不太可能发生,但理论上存在风险。

    不过,由于 recovery 只执行一次,且仅在 exit code != 0 后检查,风险很低,不算阻塞。

    让我重新综合判断……经过仔细审查,我确认没有阻塞性缺陷。之前的结论有误。这是一个架构合理的 PR。

有条件合并 / CONDITIONAL MERGE

PR 整体质量高,新增功能有完善测试覆盖,错误处理得当,文档同步更新。以下是建议修改项:


阻塞项:无


建议项

  1. cleanup-db 默认值对现有用户的影响cleanup-db 默认为 "true" 意味着所有升级到此版本的用户(未显式设置 cleanup-db)都会在数据库超过 50MB 时被自动删除。对于有重要本地数据的用户这可能产生意外。建议在 README 中更突出地说明这一行为变化,或考虑将默认值改为 "false" 以保持向后兼容。

  2. 算数下取整可能遗漏临界值size_mb="$((size_bytes / 1024 / 1024))" 使用整除下取整。例如 50.9MB 的文件 size_mb=50,阈值设为 50 时不会触发删除。建议改用 $(awk 'BEGIN{printf "%d", size/1024/1024}')$(python3 -c "import math; print((size + 1048575)//1048576)") 实现上取整。

  3. 多余的空白行test_all.pyTestCleanupDb 类前有 3 个空行(见 diff),而代码库风格一般是 2 个空行,建议统一。

  4. grep -qim1 潜在的误匹配:migration recovery 检查 grep -qim1 "duplicate column name" "$log_file" 仅在 exit code != 0 之后触发。但如果 opencode 自身的正常输出(非错误)中包含此字符串,仍会触发。建议将搜索范围限制在 stderr,或将 log_file 拆分为 stdout 和 stderr 两份。

  5. 2>/dev/null 隐藏了非预期错误action.yml${{ github.action_path }}/../shared/cleanup-db.sh 如果因为路径计算错误或文件不存在,错误会被静默忽略(因 shell 没有 -e)。建议增加显式检查文件存在性:

    script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    if [[ ! -f "$script_dir/../shared/cleanup-db.sh" ]]; then ... fi

    但考虑到 CI 环境中路径通常是正确的,此建议优先级不高。

security

存在风险 / AT RISK

本次 PR 新增了基于路径删除 opencode.db 文件的功能(cleanup-db.shrun-opencode.sh 中的迁移恢复逻辑),在未对路径做任何校验的场景下直接接受 db-path 输入并执行 rm -f,存在任意文件删除的路径穿越风险。

阻塞项:无

  • 所有风险项均需 workflow 编写者主动设置恶意输入才可触发,且 workflow 编写者已有代码执行能力,未构成可直接利用的漏洞边界。不构成阻塞合入的理由。

建议项:

  1. 路径校验缺失(防御纵深)db-path 输入未做任何路径合法性校验,可被设置为 /etc/passwd/var/log/syslog 等系统文件或任意路径,当满足大小阈值(默认 50MB)或触发 duplicate column name 错误时会被直接删除。建议添加路径解析验证,确保目标路径位于预期的 opencode 数据目录(如 ~/.local/share/opencode/)之下。

  2. 迁移恢复路径无条件删除run-opencode.sh:149 的迁移恢复逻辑在检测到 duplicate column name 后直接 rm -f "$db_path",无大小检查。虽然 cleanup-db.sh 有阈值保护,但同一脚本中另一条删除路径(迁移恢复)没有类似保护。建议迁移恢复路径也增加文件大小或路径范围检查后执行删除。

  3. 多 action 间共享风险OPENCODE_DB_PATH 同时传递给 github-run-opencode(cleanup 步骤)和 run-opencode(迁移恢复步骤),若 run-opencode 独立使用且用户设置了该环境变量,相同的路径注入攻击面依然存在。建议统一路径校验逻辑到公共函数。

performance

性能良好 / GOOD

该 PR 不涉及任何性能敏感代码:

  • shared/cleanup-db.sh:仅包含 O(1) 文件 stat 检查、一次条件判断和一次 rm 操作,无循环、无大内存分配、无子进程泄露。调用 stat 的 fallback 模式(-c%s 失败后尝试 -f%z)每次最多执行一个 stat,开销可忽略不计。

  • run-opencode/run-opencode.sh 的迁移恢复逻辑:仅在进程退出码非零时触发一次 grep -qim1 读取日志文件;日志文件体积通常远小于 DB 文件,且这是异常路径,无性能影响。恢复只执行一次(由 migration_recovery_done 守卫),不会陷入死循环。

  • github-run-opencode/action.yml:新增的 cleanup step 是一个独立的 shell 步骤,在 opencode 运行前执行一次,无额外开销。env var 传递路径、阈值等均为轻量操作。

  • 测试代码:仅用于验证行为,不纳入运行时路径。

阻塞项:无
建议项:无

architecture

架构有疑虑 / CONCERNS

该 PR 引入了 opencode.db 的清理和迁移失败自动恢复机制,整体方向合理,但存在以下架构问题:

阻塞项:无

建议项:

  1. 数据库路径解析逻辑重复
    shared/cleanup-db.sh:18-20run-opencode/run-opencode.sh:148-149 实现了相同的 db path 默认值回落逻辑(${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db})。如果未来修改默认路径需要改动两处。建议将路径解析抽象到 shared/ 中的统一函数或脚本,避免重复。

  2. 关注点混搭 — run-opencode.sh 引入 SQLite 内部细节
    run-opencode/run-opencode.sh 现在包含对 SQLite 迁移错误(duplicate column name)的检测和恢复。该脚本本应是通用的 opencode 执行器,数据库内部错误处理不属于其抽象层。建议将迁移恢复逻辑抽取到 shared/,或者在 github-run-opencode 层面统一处理(既然 cleanup-db.sh 已在 shared/)。

  3. 硬编码的错误消息匹配
    run-opencode.sh:147 使用 grep -qim1 "duplicate column name" 匹配 SQLite 错误消息。该字符串是上游实现细节,可能随 SQLite 版本或 locale 变化。建议通过 opencode CLI 的退出码或结构化输出来判断迁移失败,而非解析 stderr 文本。

  4. 清理与恢复策略不对称
    github-run-opencode 通过 cleanup-db.sh 在运行前主动清理(proactive),而 run-opencode 中内联的恢复逻辑是在失败后被动恢复(reactive)。两种策略分散在两个 action 中,逻辑治理不一。考虑是否应让 run-opencode 也复用 shared/cleanup-db.sh 或其他统一策略。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 08ef31c to 2b641fc Compare June 7, 2026 04:36
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 Issue #198 的逐项需求与当前代码进行完整验证:

方案 A(启动前清理)

  • 检测 DB 是否存在:cleanup-db.sh:31 [[ ! -f "$db_path" ]]
  • 检查文件大小,超阈值删除:cleanup-db.sh:35-40 stat -c%s GUN 优先 → 与阈值比较 → rm -f
  • 或者检测版本变更删除:"或者" 表示二选一可选子项,文件大小方案已完全满足需求 ✅
  • 阈值可配置:cleanup-db input(true/数字/false)+ OPENCODE_DB_MAX_SIZE_MB
  • action.yml 中 install 与 run 之间新增 step:github-run-opencode/action.yml:251-263

方案 C(Migration 失败自动恢复)

  • 检测 duplicate column namerun-opencode.sh:148 grep -qim1
  • 自动删除 opencode.dbrun-opencode.sh:152 rm -f "$db_path"
  • 重试:run-opencode.sh:155 continue 进入重试循环 ✅
  • 最多一次:migration_recovery_done 标志位限制 ✅

推荐:方案 A + C 结合:两者均已实现 ✅

#200 修复项(PR 承诺修复)

  • 移除 dead-code false 检查:step if: 守卫 ✅
  • swap stat 顺序:GNU -c%s 优先,BSD -f%z fallback cleanup-db.sh:35
  • 接线 OPENCODE_DB_PATHgithub-run-opencode/action.yml:255,292 使用 ${{ inputs.db-path }} 传递到 cleanup 和 run 两步骤 ✅(且用 inputs.db-path 替代了原先 env.OPENCODE_DB_PATH,修复了编译期求值问题)
  • 空字符串守卫:resolve-db-path.sh:17 [[ -z "$raw" ]] && raw="$default_path" + run-opencode.sh:150 同样的守卫 ✅
  • 路径安全校验:resolve-db-path.sh:35-41 拒绝 /etc /usr /bin /sbin /boot /proc /sys /dev 等系统目录 ✅
  • 测试覆盖:TestCleanupDb(7 tests)+ TestMigrationRecovery(3 tests)= 10 tests ✅(超出 PR 声称的 8 个)

文档github-run-opencode/README.mdrun-opencode/README.mdshared/README.md 均已更新 ✅

所有功能需求均已完整实现,无遗漏。

注:run-opencode.sh:148-155 的迁移恢复逻辑有独立的空字符串守卫(与 resolve-db-path.sh 重复),但这属于架构层面的 DRY 问题,不是功能遗漏。run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本有默认路径兜底(run-opencode.sh:149),且 Issue #198 未要求修改该文件,不构成功能遗漏。

New%20session%20-%202026-06-07T04%3A36%3A38.444Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:有条件合并 / CONDITIONAL MERGE

run-opencode.sh 迁移恢复代码中 rm -f "$db_path" 缺少路径安全校验,需先修复。


阻塞项 / Blocking Issues

  1. run-opencode.sh 迁移恢复代码缺少路径校验(安全/质量 reviewer 一致确认)
    run-opencode.sh 新增的 rm -f "$db_path" 直接使用 $OPENCODE_DB_PATH 环境变量,未经 resolve-db-path.sh 的路径校验和黑名单检查。若环境变量被恶意设置或在异常上下文中调用,可能导致任意文件被删除。
    修复要求:将 resolve-db-path.sh 的路径校验逻辑复用至 run-opencode.sh,或 source 同一模块。

  2. resolve-db-path.sh 黑名单不完整(安全/质量 reviewer 一致确认)
    当前禁止列表仅包含 /etc /usr /bin /sbin /boot /proc /sys /dev,缺少 /var/opt/root/tmp/mnt 等目录,同时对 .. 路径穿越未做严格校验。
    修复要求:补充目录黑名单,并使用 realpath/readlink -f 规范化路径后再做匹配。


建议项 / Suggestions

  1. WAL/SHM 残留文件未清理(performance reviewer)
    rm -f "$db_path" 仅删除主文件,应一并删除 opencode.db-walopencode.db-shm 等 SQLite 附属文件,避免下次启动时不必要的 WAL 恢复 I/O。

  2. 文件大小计算使用整数除法(quality reviewer)
    $((size_bytes / 1024 / 1024)) 会截断余数,建议在文档中注明阈值以 MiB 向下取整,或用 bc 做浮点比较。

  3. 测试 test_default_threshold_is_50 环境变量污染(quality reviewer)
    建议在 setUp 中显式清除 OPENCODE_DB_MAX_SIZE_MB 环境变量。

  4. test_path_outside_home_is_rejected 多余写文件操作(quality reviewer)
    测试中 self.db_path.write_bytes(...) 在路径校验阶段即退出,数据未被使用,建议移除。

  5. 大文件删除可能带来 I/O 阻塞(performance reviewer)
    删除 GB 级数据库文件时,若被其他进程 mmap 可能引发短暂 I/O 阻塞,建议在文档中提示潜在延迟。

  6. 路径解析方式不一致(质量/安全 reviewer 提及)
    resolve-db-path.shcd "$dir" && pwd 会解析符号链接,而 run-opencode.sh 直接使用原始路径,行为差异可能导致指向不同文件。建议统一路径解析策略。


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

本次 PR 实现了两个关联功能:运行前清理过大的 SQLite 数据库(cleanup-db),以及运行中自动从迁移错误恢复。整体代码质量较高,测试覆盖完备。

阻塞项:无

建议项:

  1. run-opencode.sh 中的路径解析与 resolve-db-path.sh 不一致:迁移恢复代码(run-opencode.sh:147-148)直接使用 "${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}",缺少路径规范化(symlink 解析、相对路径转绝对路径)和对系统目录的安全校验。建议将 resolve-db-path.sh_resolve_db_path 函数抽离为可 source 的模块,在迁移恢复中也复用同一份逻辑,避免两处维护不一致。

  2. resolve-db-path.sh 的路径黑名单不够完整:未包含 /var/tmp/opt/mnt 等目录。同时对 .. 穿越未做严格检查(如 $PWD/../../etc/passwd 不会被拦截)。如果担心路径穿越,可以在条件允许时使用 realpathreadlink -f 规范化后再做匹配。

  3. cleanup-db.sh 文件大小计算使用整数除法$((size_bytes / 1024 / 1024)) 会截断余数,导致 51MB 实际文件在 50MB 阈值下被判断为 50MB 并触发删除(50 -ge 50 为真)。这不是逻辑错误,但阈值语义应视为 "向下取整" 而非精确值。建议在文档中注明阈值以 MiB 为单位取整,或用浮点比较(如 bc)提高精度。

  4. 测试中 test_default_threshold_is_50 依赖于 env 中不污染 OPENCODE_DB_MAX_SIZE_MBsetUp 使用 os.environ.copy(),如果 CI 环境本身设置了该变量,测试可能失败。建议在 setUp 中显式清除该变量:self.env.pop("OPENCODE_DB_MAX_SIZE_MB", None)

  5. test_path_outside_home_is_rejected 中写入了不必要的临时文件self.db_path.write_bytes(...) 写入后在测试中并未被使用,因为脚本在路径校验阶段就退出了。可以移除该写文件操作以保持测试简洁。

  6. resolve-db-path.shpwd 的行为cd "$dir" && pwd 会解析 symlink(pwd 返回物理路径),而 run-opencode.sh 的路径不做解析。两者行为差异可能导致在不同上下文中指向不同文件,建议统一。

security

存在风险 / AT RISK

安全分析摘要

此 PR 新增了 SQLite 数据库文件自动清理与迁移失败自动恢复功能。整体设计考虑了基本的安全防护(路径黑名单校验、输入正则校验),但仍存在以下问题:


阻塞项

  • run-opencode.sh 迁移恢复代码缺乏路径校验:新增的 rm -f "$db_path" 操作前没有对 $db_path 做任何安全检查。当前仅 cleanup-db.sh 通过 resolve-db-path.sh 做了路径校验,但 run-opencode.sh 的迁移恢复逻辑直接使用 $OPENCODE_DB_PATH 环境变量,未经校验就执行删除。如果该脚本在其他上下文中被调用或 env 被恶意设置,可能导致任意文件被删除。

  • resolve-db-path.sh 黑名单不完整:禁止列表仅包含 /etc /usr /bin /sbin /boot /proc /sys /dev,未覆盖 /var/opt/root 等目录。虽然 rm -f 的破坏力有限,但路径校验应该更完整。

建议项

  • 复用 resolve-db-path.shrun-opencode.sh:将 resolve-db-path.sh 的路径校验逻辑引入 run-opencode.sh 的迁移恢复代码中,保持一致的防御纵深。

  • 增强路径解析的安全性resolve-db-path.sh 使用 cd "$dir" && pwd 解析路径,会跟随符号链接。建议使用 readlink -frealpath 来解析真实路径,防止通过符号链接绕过黑名单校验。

  • 考虑加入 /var 到禁止列表/var 下通常有数据库和日志文件,虽然不是系统二进制目录,但被意外删除仍可能导致服务异常。

performance

性能有疑虑 / CONCERNS

性能分析摘要

该 PR 新增了 SQLite 数据库清理和迁移错误自动恢复功能。整体设计合理,没有严重的性能回归,但存在几个值得关注的资源利用问题。

阻塞项:无

未发现必须阻止合并的性能问题。

建议项

  • WAL/SHM 残留文件导致额外 I/Ocleanup-db.shrun-opencode.sh 的迁移恢复逻辑在执行 rm -f "$db_path" 时,仅删除了主数据库文件 opencode.db,而没有清理 SQLite 的 WAL 日志文件(opencode.db-wal)和共享内存文件(opencode.db-shm)。当 opencode 下次启动时,SQLite 会检测到这些孤立的 journal 文件并尝试进行 WAL 恢复,产生不必要的磁盘 I/O 和启动延迟。建议在删除主文件的同时一并清理 -wal-shm 等附属文件。

  • 大文件删除可能阻塞 runnerrm -f 删除数 GB 级别的数据库文件时,如果该文件被其他进程 mmap,内核会同步执行 page cache 失效操作,导致短暂但可感知的 I/O 阻塞。虽然这是预期行为(清理超大数据库本就是 PR 目标),但如果 runner 内存紧张,建议考虑使用 fstrim 或定期维护而非一次性 rm。建议在文档中提示大数据库删除的潜在延迟。

  • 路径解析存在重复 I/Oresolve-db-path.sh 中使用了 cd "$dir" && pwd 来解析符号链接,这会产生一次额外的目录遍历系统调用。考虑到该脚本在每个 job 中只执行一次,影响微乎其微,但若将来被频繁调用可能会累积开销。如果追求极致性能,可以用 realpath 替代(前提是环境支持)。

architecture

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 2b641fc to 46d61cd Compare June 7, 2026 04:41
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 Issue #198 的逐项需求与当前代码完整验证:

方案 A(启动前预防性清理)

  • 检测 DB 是否存在:shared/cleanup-db.sh:31 [[ ! -f "$db_path" ]]
  • 检查文件大小,超阈值删除:shared/cleanup-db.sh:35-40 stat -c%s + 与阈值比较 + rm -f
  • 阈值可配置:cleanup-db input(true/数字/false) + OPENCODE_DB_MAX_SIZE_MB
  • action.yml 中 install 与 run 之间新增 step:github-run-opencode/action.yml:251-263
  • 版本变更检测:"或者" 可选子项,文件大小方案已完全满足需求 ✅

方案 C(Migration 失败自动恢复)

  • 检测 duplicate column namerun-opencode.sh:148 grep -qim1
  • 自动删除 opencode.db(含 -wal-shm):run-opencode.sh:152
  • 重试一次:migration_recovery_done 标志位限制第 148-155 行 ✅

推荐:方案 A + C 结合:两者均已实现 ✅

#200 修复项

  • dead-code false 检查移除:step if: 守卫第 251 行 ✅
  • swap stat 顺序:GNU -c%s 优先,BSD -f%z fallback cleanup-db.sh:35
  • 接线 OPENCODE_DB_PATHgithub-run-opencode/action.yml 第 255、292 行使用 ${{ inputs.db-path }} ✅(实际使用 inputs 比原计划 env 更优,规避了 composite action 编译期求值问题)
  • 空字符串守卫:resolve-db-path.sh:17 + run-opencode.sh:150
  • 路径安全校验:resolve-db-path.sh:35-47 黑名单拒绝系统目录 ✅
  • 测试覆盖:TestCleanupDb 7 tests + TestMigrationRecovery 3 tests = 10 tests (超出 PR 声称的 8 个) ✅

文档:3 个 README 均已更新 ✅

所有 Issue #198 列出的功能需求均已完整实现,无遗漏。

New%20session%20-%202026-06-07T04%3A41%3A17.544Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

总结:PR 方向合理,代码质量良好,测试覆盖充分。存在 2 个跨 reviewer 确认的问题,其中最关键的路径校验缺失问题被安全领域 reviewer 评估为可后续修复(攻击路径难度高),无需阻塞合并。建议合并前优先处理已确认问题。


阻塞项:无


建议项

已确认(2+ reviewers 同意):

  1. run-opencode.sh 迁移恢复逻辑缺失路径安全校验 — quality(阻塞)、security(建议)、architecture(建议)一致指出:run-opencode.sh:148 的直接 rm -f 操作未调用 resolve-db-path.sh 的黑名单校验,与 cleanup-db.sh 行为不一致。security 评估利用难度较高(需先触发 duplicate column name 错误),建议在迁移恢复代码中 source resolve-db-path.sh 并调用 _resolve_db_path

  2. resolve-db-path.sh 可执行权限不匹配用途 — quality、architecture 均建议将 100755 改为 0644 或添加头部注释,明确其为 source-only 库文件。

领域特有建议(单 reviewer 提出):

  1. run-opencode.sh 冗余路径兜底死代码 — quality:${var:-default} 已覆盖 unset/null,第二行 [[ -z ]] 赋值永不触发,建议删除。

  2. resolve-db-path.sh macOS readlink -f 兼容性 — quality:注释提到 greadlink 但未实现 fallback,建议增加 greadlinkreadlink 回退逻辑。

  3. resolve-db-path.sh 黑名单缺少 /var/private — security:建议补充 /var(如 /var/lib 含重要数据)和 macOS 的 /private

  4. cleanup-db.sh 整数除法截断 — quality:size_mb=$((size_bytes / 1024 / 1024)) 向下取整可能导致略超阈值时误判,建议四舍五入。

  5. cleanup-db.sh TOCTOU 竞态 — security:路径校验与 rm -f 之间文件可能被替换为符号链接,但 rm -f 不跟随符号链接,风险较低。

  6. cleanup-db.sh set -e 注释不准确 — quality:注释与 set -e 实际行为有出入,建议更新。

  7. 测试文件多余空行 — quality:tests/test_all.pyclass TestCleanupDb 前连续空行过多,建议规范格式化。

  8. run-opencode.sh 迁移恢复与重试循环职责混合 — architecture:SQLite 迁移恢复逻辑与 opencode 内部实现耦合,建议考虑提升为独立预处理层。

  9. shared/ 目录职责扩张 — architecture:建议将 shell 脚本移至 shared/scripts/ 子目录。

  10. cleanup-db.sh 禁止列表可能过于严格 — architecture:/opt/srv/mnt 等可能用于自托管 runner 安装路径,建议改为基于 $HOME 归属判断而非穷举。


📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

总评

本次 PR 整体设计合理,引入了 SQLite 数据库清理和迁移错误自动恢复机制,适用于自托管 runner 上的大型 opencode.db 场景。测试覆盖良好,文档同步更新。但存在一个安全相关的阻塞问题需要先修复。


阻塞项

  1. run-opencode.sh 迁移恢复逻辑未做路径安全校验,可能导致意外删除系统文件

    run-opencode.sh 中的迁移恢复代码直接使用 $OPENCODE_DB_PATH 默认值来定位数据库文件,但没有像 cleanup-db.sh 那样通过 resolve-db-path.sh 进行安全校验(禁止 /etc/usr 等系统目录)。

    攻击路径:用户设置 cleanup-db: false + db-path: /etc/critical-file,若 opencode 运行中出现 migration error,恢复逻辑会直接 rm -f /etc/critical-file(以及对应的 -wal / -shm 文件)。

    修复方案:在 run-opencode.sh 的迁移恢复代码中,也应 source resolve-db-path.sh 并调用 _resolve_db_path 进行路径校验,或者至少添加与服务端一致的禁止路径检查。即使 cleanup-db 步骤提供了第一层防护,恢复逻辑作为第二层也应保持相同的安全策略。


建议项

  1. run-opencode.sh 中的冗余路径兜底代码

    db_path="${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}"
    [[ -z "$db_path" ]] && db_path="$HOME/.local/share/opencode/opencode.db"

    第二行是死代码:${var:-default} 语义已经覆盖了 unset 和 null 两种情况,第二行永远不会触发。建议删除以保持简洁。

  2. resolve-db-path.sh 中 macOS readlink -f 兼容性

    • 注释提到 greadlink 但未实现 fallback。macOS 上 readlink -f 不存在(需 coreutils),将静默跳过符号链接解析。建议实现 greadlink 回退:
      if command -v greadlink >/dev/null 2>&1; then
        real_resolved="$(greadlink -f "$resolved" 2>/dev/null)" && resolved="$real_resolved"
      elif command -v readlink >/dev/null 2>&1; then
        real_resolved="$(readlink -f "$resolved" 2>/dev/null)" && resolved="$real_resolved"
      fi
  3. resolve-db-path.sh 标记为可执行文件(100755)但设计为被 source 使用

    这本身不产生问题,但可能引起困惑。建议将权限改为 644 以明确其为 library 文件,或者在文件顶部加注释说明。

  4. cleanup-db.sh 中整数除法的截断问题

    size_mb=$((size_bytes / 1024 / 1024)) 使用整数除法会截断小数部分。例:50.9 MB(53372518 bytes)计算为 50 MB,当阈值为 50 时不会被删除。建议使用 $(( (size_bytes + 512 * 1024) / 1024 / 1024 )) 四舍五入,或降低阈值的比较条件使用 -gt 而非 -ge。此问题在正常使用中影响不大,仅供优化参考。

  5. 测试文件中多余的空白行

    tests/test_all.pyclass TestCleanupDb 前有多余的连续空行,建议规范化格式。

  6. cleanup-db.sh 的执行语义注释矛盾

    set -uo pipefail 没有 -e。注释说"Intentionally no -e"并给出了说明,但与 set -e 的行为描述有细节出入(|| 链在 -e 下也能工作,因为 || 右侧是显式错误处理路径)。注释内容不准确但功能性无问题,建议更新注释以准确反映实际选择原因。

security

存在风险 / AT RISK

该 PR 引入了数据库清理和迁移恢复功能,整体设计考虑了安全性(路径黑名单校验、输入正则过滤等),但存在两处安全缺陷:

阻塞项:无

无需阻塞合并的严重漏洞。

建议项

  1. run-opencode.sh 缺失路径验证 (run-opencode/run-opencode.sh:148):迁移恢复逻辑直接从 OPENCODE_DB_PATH 环境变量读取路径,未使用 resolve-db-path.sh 中的黑名单校验。而 cleanup-db.sh 已使用该验证,两者存在不一致。虽然路径删除前需触发 duplicate column name 错误(攻击难度较高),但若工作流恶意设置 OPENCODE_DB_PATH=/etc/passwd 等系统路径,仍存在被删除的可能。建议在 run-opencode.sh 中也 source resolve-db-path.sh 并调用 _resolve_db_path

  2. resolve-db-path.sh 黑名单不完整 (shared/resolve-db-path.sh:37):当前禁止目录列表为 /etc /usr /bin /sbin /lib /boot /proc /sys /dev /opt /root /mnt /media /srv /run,缺少 /var(如 /var/lib 含重要应用数据)和 macOS 的 /private。建议补充。

  3. cleanup-db.sh 存在 TOCTOU 竞态 (shared/cleanup-db.sh:37):_resolve_db_path 在校验路径后,rm -f 执行前,路径所指文件可能被替换为符号链接(self-hosted runner 环境中风险略高)。但 rm -f 不会跟随符号链接,风险较低。

performance

性能良好 / GOOD

性能分析概要

本次 PR 的所有变更均为 shell 脚本和测试代码,不涉及数据库查询、内存分配或并发编程模式。所有操作均为文件系统级的轻量级一次性操作。

变更内容分析:

  • cleanup-db.sh:使用 stat(O(1))检查单个文件大小,条件性删除文件。整数除法做 MB 换算(会向下取整,但对阈值的判断精度影响可忽略)
  • resolve-db-path.sh:路径解析使用 cd && pwdreadlink -f,两者均为单次文件系统调用;forbidden 路径列表为固定 13 项常量遍历
  • run-opencode.sh 迁移恢复逻辑:失败时 grep 日志文件一次(日志通常很小),然后执行 rm -f 删除数据库文件
  • action.yml:新增步骤仅在 cleanup-db != false 时执行,无额外开销

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

该 PR 引入了数据库清理与迁移故障自动恢复功能,整体方向合理,shared/ 目录的复用设计正确。但存在以下架构问题:

阻塞项

  • 路径解析逻辑重复,存在行为不一致风险shared/resolve-db-path.sh 实现了完整的路径解析(绝对路径化、symlink 解析、系统目录校验),而 run-opencode.sh:148 的迁移恢复逻辑仅用 "${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}" 简单处理,跳过了所有规范化和安全检查。若用户传入相对路径或指向系统目录的 db-path,两条路径的行为将不一致。建议 run-opencode.sh 也 source resolve-db-path.sh 统一实现。

  • resolve-db-path.sh 设计为 sourced 但设为可执行resolve-db-path.sh100755)设计上需要被 source 后才能使用 _resolve_db_path 函数,直接执行无意义。标记为可执行会误导维护者。建议改为 0644 或添加执行时引导提示。

阻塞项:无(上述问题均为建议级别,不阻止合并)

建议项

  1. run-opencode.sh 中迁移恢复与重试循环的职责混合run-opencode.sh 是一个通用的 opencode 命令执行器,SQLite 迁移恢复是特定于 opencode 数据库实现细节的逻辑,通过字符串匹配 "duplicate column name" 与 opencode 内部实现耦合。建议考虑将迁移恢复提升为 wrapper 或预处理层(如 github-run-opencode step),而不是嵌入通用执行器的重试循环中。

  2. shared/ 目录职责扩张:此前 shared/ 仅存放纯文本 prompt 片段,现在新增了可执行 shell 脚本。建议将 shell 辅助脚本放在 shared/scripts/ 子目录下,与 prompt 文件隔离,保持模块内职责清晰。

  3. cleanup-db.sh 禁止系统目录的校验可能过于严格resolve-db-path.sh 中的 forbidden 列表包含 /opt/srv/mnt 等目录。自我托管 runner 可能在非标准路径安装 opencode,硬编码禁止列表可能导致合法路径被拒绝。建议改为基于父目录归属判断(如检测是否位于 $HOME 下),而非穷举禁止列表。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 46d61cd to f69ec65 Compare June 7, 2026 04:45
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已获取所有必要的信息进行全面分析。

让我逐步验证 PR 描述的所有需求是否已在代码中实现。

方案 A(启动前预防性清理):

  1. shared/cleanup-db.sh 检测 DB 是否存在 → line 31 [[ ! -f "$db_path" ]]
  2. 检查文件大小 → line 35 stat -c%s(GNU 优先)✅
  3. 超阈值删除 → lines 38-40 rm -f
  4. 阈值可配置 → line 24 ${OPENCODE_DB_MAX_SIZE_MB:-50}
  5. cleanup-db input 支持 "true"/数字/"false" → lines 156-163 ✅
  6. install 与 run 之间的新 step → lines 251-263 ✅

方案 C(迁移失败自动恢复):

  1. 检测 duplicate column namerun-opencode.sh line 148 grep -qim1
  2. 删除数据库(含 -wal-shm)→ line 156 ✅
  3. 重试一次 → line 159 continue
  4. 限制仅一次 → migration_recovery_done 标志位 lines 133, 148, 157 ✅

#200 修复项:

  1. dead-code false 检查移除 → step if: 守卫 line 251 ✅
  2. swap stat 顺序 → cleanup-db.sh line 35 GNU 优先 ✅
  3. 接线 OPENCODE_DB_PATH → action.yml lines 255, 292(使用 inputs.db-path 而非 env.OPENCODE_DB_PATH,实际改进了复合操作编译期求值问题)✅
  4. 测试覆盖 → 7 + 3 = 10 tests(超出 PR 声称的 8 个)✅

文档: 3 个 README 全部更新 ✅

其他实现(超出 PR body 描述):

  • resolve-db-path.sh:共享路径解析 + 系统目录黑名单校验 ✅
  • WAL/SHM 清理:cleanup-db.sh line 40 ✅
  • 零/负阈值禁用清理:cleanup-db.sh lines 26-28 ✅

唯一值得注意的点: run-opencode/action.yml 未添加 db-path input 或传递 OPENCODE_DB_PATH,但这对功能完整性没有影响——run-opencode.sh 有默认路径兜底(line 153),且 PR body 和 Issue #198 均未要求修改 run-opencode/action.yml,不构成遗漏。

无遗漏

对照 Issue #198(PR body 自述的 spec)逐项验证,所有需求均已完整实现,无功能遗漏。

  • 方案 A(预防性清理):shared/cleanup-db.sh 文件存在检测 + stat -c%s GNU 优先获取大小 + 可配置阈值比较 + 超阈值删除(含 -wal/-shm)。github-run-opencode/action.yml 新增 cleanup-db input(true/数字/false)和在 install 与 run 之间的 cleanup step。
  • 方案 C(迁移恢复):run-opencode/run-opencode.sh 第 148 行 grep -qim1 "duplicate column name" 检测错误 → 第 156 行 rm -f 删库 → migration_recovery_done 标志位限制仅重试一次。
  • feat: add opencode.db cleanup and migration failure auto-recovery #200 修复项:dead-code 移除、GNU stat 优先、OPENCODE_DB_PATH 接线至 cleanup 和 run 两步骤、测试覆盖(7 + 3 = 10 个测试,超出宣称的 8 个)。
  • 文档:3 个 README 均已更新对应功能说明。

run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本有默认路径兜底且 Issue #198 未要求修改该文件,不构成功能遗漏。PR 实现的质量超出了 spec 描述的范围(额外增加了 resolve-db-path.sh 路径安全校验、WAL/SHM 清理、零阈值保护等)。

New%20session%20-%202026-06-07T04%3A45%3A14.754Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

quality 审查者报告了 2 个阻塞项,其中 1 项被 security 审查者交叉确认。修复后即可合并。


阻塞项 / Blocking Issues

  1. [已确认] run-opencode/run-opencode.sh 迁移恢复路径绕过安全校验 — quality(阻塞项 2)与 security(建议项 2)共同指出的问题:当 _resolve_db_path 返回 1(如遇到禁用路径)时,else 分支直接回退到 ${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db},完全跳过路径安全校验。应在此分支也执行路径验证,或在 resolve-db-path.sh 失败时报错退出而非静默回退。

  2. shared/resolve-db-path.sh:20 cd 命令失败后产生错误路径 — quality(阻塞项 1):当 $dir 存在但 cd 失败(如权限问题)时,resolved="$(cd "$dir" && pwd)/$(basename "$raw")" 中的命令替换输出空字符串,导致 resolved 被设为 /opencode.db 根目录路径,从而绕过黑名单检查。建议在 cd "$dir" 失败时加入 fallback 处理。


建议项 / Suggestions

  1. 路径验证使用黑名单不够安全 — security(建议项 1):黑名单仅拒绝 /etc/usr/bin 等已知系统目录,未涵盖的路径(如 /var/opt)可被绕过。建议改为白名单方式,只允许 $HOME 及工作目录下的路径。

  2. TOCTOU 竞态条件风险 — security(建议项 3):路径验证完成到实际 rm -f 删除之间存在符号链接交换的时间窗口。建议删除前对最终路径再做一次跟随符号链接的验证。

  3. 日志注入风险 — security(建议项 4):db_path 直接嵌入 ::warning:: 等 workflow command 输出中,若路径含换行符可污染日志解析。建议 sanitize 输出路径中的控制字符。

  4. run-opencode/action.yml 未暴露 db-path 输入且缺少校验 — security(建议项 5)+ architecture(建议项 2)合并:db-path 既未在 run-opencode/action.yml 中声明为 input,也未添加格式前置校验。独立使用 run-opencode 时用户无法发现此配置项。建议在 run-opencode/action.yml 中添加 db-path input 并传递到环境变量,同时加入输入约束校验。

  5. 路径解析逻辑重复 — architecture(建议项 1):run-opencode.sh 中的迁移恢复逻辑包含与 resolve-db-path.sh 重复的路径解析 fallback。建议统一使用 resolve-db-path.sh 作为路径解析的唯一来源,失败则直接报错退出而非使用未校验的默认路径。

  6. cleanup-db.sh 输出格式不统一 — quality(建议项 1):清理数据库时使用 ::warning::(Actions 日志命令),常规信息只用 cleanup-db: 前缀。建议统一日志风格。

  7. 不存在 db 文件时不清理残留 WAL/SHM — quality(建议项 2):cleanup-db.sh[[ ! -f "$db_path" ]] 时直接 exit 0,不会清理可能残留的 .db-wal.db-shm 文件。

  8. readlink -f 在 macOS 上兼容性问题 — quality(建议项 3):已有 greadlink 优先的逻辑,但 macOS 原生 readlink 不支持 -f 时静默返回空。建议补充纯 bash 解析 fallback。


📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

该 PR 整体质量较高,测试覆盖全面,错误处理也较完善。但存在以下需要修复的问题:

阻塞项

  1. shared/resolve-db-path.sh:20cd 命令失败后产生错误的路径:当 $dir 存在但 cd 失败时(如权限问题),resolved="$(cd "$dir" && pwd)/$(basename "$raw")" 中的命令替换输出空字符串,导致 resolved 被设为 /opencode.db 这样的根目录路径。此路径会绕过禁用路径检查(不以 /etc/usr 等前缀开头),可能被传入 rm -f 误删根目录下的文件。建议在 cd "$dir" 失败时加入 fallback 处理,例如手动拼接绝对路径而非依赖 cd && pwd

  2. run-opencode.sh 迁移恢复路径未做安全校验:当 _resolve_db_path 因禁用路径而返回 1 时,else 分支直接使用 ${OPENCODE_DB_PATH:-$HOME/...} 作为 db_path,跳过了禁用路径检查。这与 cleanup-db.sh_resolve_db_path || exit 1 行为不一致。即使用户误将 db-path 设为系统目录,cleanup-db.sh 会阻止操作,但 run-opencode.sh 的迁移恢复仍可能删除该路径下的文件。建议在 else 分支中也加入路径校验。

建议项

  1. shared/cleanup-db.sh 输出格式不够统一:清理数据库时使用 ::warning::(Actions 日志命令)输出,但常规信息只用 cleanup-db: 前缀;建议统一日志风格,全部使用 ::debug:: 或统一前缀。

  2. 不存在 db 文件时不删除残留 WAL/SHMcleanup-db.sh[[ ! -f "$db_path" ]] 时直接 exit 0,不会清理可能残留的 opencode.db-walopencode.db-shm。虽然概率很低,但崩溃后可能存在此类残留文件。

  3. resolve-db-path.shreadlink -f 在 macOS 上可能缺失:目前正确处理了 greadlink 优先,但如果两者都没有会静默跳过符号链接解析。建议补充一个 fallback 的纯 bash 解析,或至少通过 command -v readlink 确认后再调用 readlink -f(目前已在 condition 中做了检查,但 readlink -f 在 macOS 原生 readlink 中不支持 -f 也不会报错,只是返回空字符串,问题不大,但值得注意)。

security

存在风险 / AT RISK

安全分析摘要

本次 PR 新增了 SQLite 数据库自动清理和迁移故障自动恢复功能。整体上没有严重到需要阻塞合并的高危漏洞,但在防御深度设计上存在一些值得改进的安全缺陷。

阻塞项:无

未发现必须阻塞合并的关键安全漏洞。输入值(cleanup-dbdb-path)均源自 GitHub Actions workflow 定义,不由外部攻击者直接控制,因此不存在远程代码注入或权限提升的可行攻击路径。

建议项

  1. 路径验证使用黑名单而非白名单shared/resolve-db-path.sh:35-43
    验证采用黑名单方式仅拒绝 /etc /usr /bin 等已知系统目录,未涵盖的路径(如 /var/opt、其他自定义敏感目录)可能被绕过。此外,当 readlink -f 不可用且目录不存在时,含有 .. 的路径不会被展开,黑名单检查也可能被规避。建议改为白名单方式,只允许 $HOME 及工作目录下的路径。

  2. 失败时静默回退绕过路径验证run-opencode/run-opencode.sh:149
    resolve-db-path.sh 不存在或 _resolve_db_path 调用失败时,静默回退到未经任何验证的 ${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}。安全验证失败后应暴露错误并终止,而非静默绕过。

  3. TOCTOU 竞态条件风险shared/cleanup-db.sh:39 / run-opencode/run-opencode.sh:153
    路径验证完成到实际 rm -f 删除之间可能存在符号链接交换的时间窗口。在共享型自托管 Runner 环境中,攻击者有机会构造符号链接指向系统文件。建议删除前对最终路径再做一次跟随符号链接的验证,或将验证和删除操作原子化。

  4. 路径内容可能被用于日志注入shared/cleanup-db.sh:37 / run-opencode/run-opencode.sh:152
    db_path 的内容直接嵌入 ::warning:: 等 GitHub Actions workflow command 输出中。若路径包含换行符或 %0A 等特殊字符,可能污染日志解析或伪装成其他 workflow command。建议对输出路径做 sanitize(替换换行符等控制字符)。

  5. db-path 输入缺少 action 层级的校验github-run-opencode/action.yml
    虽然 cleanup-db 在 YAML 中没有 schema 校验,但好歹在 shell 脚本中用正则做了安全过滤。而 db-path 输入从 action.yml 到 shell 脚本完全没有输入格式校验,完全依赖下游脚本的黑名单验证。建议在 action.yml 中添加 pattern 或利用 GitHub Actions 的输入约束做前置校验。

performance

性能良好 / GOOD

性能分析摘要:
该 PR 新增了两个 shell 脚本(cleanup-db.shresolve-db-path.sh)和 run-opencode.sh 中的迁移恢复逻辑。所有操作均为轻量级文件系统操作(statrmgrep于小日志文件),没有数据库查询、复杂算法或内存分配问题。CI 运行环境天然隔离,无并发风险。

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

该 PR 整体架构合理,共享脚本抽取到 shared/ 目录是正确做法。但存在几个值得关注的问题:

阻塞项:无

建议项:

  1. 路径解析逻辑重复: run-opencode/run-opencode.sh 中的迁移恢复逻辑包含了与 shared/resolve-db-path.sh 重复的路径解析 fallback。建议统一使用 resolve-db-path.sh 作为路径解析的唯一来源,避免两份逻辑在未来产生分歧。具体做法是将 inline fallback 改为仅 source resolve-db-path.sh,失败则直接报错退出而非使用未校验的默认路径。

  2. OPENCODE_DB_PATH 未作为 action input 暴露: run-opencode/action.yml 没有声明 db-path input,但 run-opencode.sh 却通过环境变量 OPENCODE_DB_PATH 读取该值。导致 run-opencode 如果被独立使用(不经 github-run-opencode),用户无法发现这个配置项。建议在 run-opencode/action.yml 中也添加 db-path input 并传递到环境变量。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from f69ec65 to 80b1ec5 Compare June 7, 2026 04:48
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 PR body 中的 spec 逐项验证,所有需求均已完整实现:

方案 A(预防性清理):

  • shared/cleanup-db.sh 实现文件存在检测、GNU stat -c%s 优先获取大小、可配置阈值比较、删除(含 -wal/-shm)✅
  • cleanup-db input 支持 "true"/数字/"false"
  • github-run-opencode/action.yml 第 251-263 行在 install 与 run 之间新增 cleanup step ✅

方案 C(迁移恢复):

  • run-opencode/run-opencode.sh 第 148 行 grep -qim1 "duplicate column name" 检测错误 ✅
  • 第 159 行 rm -f 删库(含 -wal/-shm)✅
  • migration_recovery_done 标志位限制仅重试一次 ✅

#200 修复项:

  • dead-code 移除(if: 守卫,第 251 行)✅
  • GNU stat 优先(cleanup-db.sh:35)✅
  • OPENCODE_DB_PATH 接线(action.yml 第 255、292 行,使用 inputs.db-path,实际优于原计划的 env 方案,避免了 composite action 编译期求值问题)✅
  • 空字符串守卫(resolve-db-path.sh:18 + run-opencode.sh:150-157)✅
  • 路径安全校验(resolve-db-path.sh:35-47 黑名单拒绝系统目录)✅
  • 测试覆盖:TestCleanupDb 7 tests + TestMigrationRecovery 3 tests = 10 tests(超出声称的 8 个)✅

文档: 3 个 README 均已更新 ✅

run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本有默认路径兜底且 Issue #198 和 PR body 均未要求修改该文件,不构成功能遗漏。实现质量整体超出 spec 范围(额外包含 resolve-db-path.sh 路径安全校验、WAL/SHM 清理、零阈值保护、db-path 输入等)。

New%20session%20-%202026-06-07T04%3A48%3A32.388Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:有条件合并 / CONDITIONAL MERGE

quality 审查发现一个需在合并前修复的无限循环 bug。security 标记为 AT RISK 但无阻塞项,其建议与 quality、architecture 的建议合并去重后一并列出。


阻塞项 / Blocking Issues

  1. run-opencode.sh 迁移恢复中 db_path 为空时无限循环(quality)
    迁移恢复分支中若 db_path 解析失败,代码 rm -f "$log_file"continue,但未递增 attempt 也未设置 migration_recovery_done=true,导致同一循环分支重复执行直至超时。修复:在空 db_path 分支设置 migration_recovery_done=trueexit 1

建议项 / Suggestions

  1. resolve-db-path.sh 波浪号未展开(quality)
    dirname 不展开 ~,若用户传入 ~/.local/... 形式路径会解析错误。建议使用 $HOMEeval echo 展开。

  2. resolve-db-path.shcd - 可能失败(quality)
    cd - 若当前目录已被删除会输出 stderr 错误。建议添加 || true

  3. cleanup-db.sh 未清理 SQLite journal 文件(quality)
    回滚日志模式下的 -journal 残留文件不会被删除。建议一并清理 "$db_path-journal"

  4. 测试 test_no_db_file_is_noop 未校验 stderr(quality)
    该测试仅断言 stdout 为空,cd - 失败时的 stderr 未覆盖。建议同时校验 stderr 或修复 cd - 重定向。

  5. run-opencode.sh 迁移恢复中 grep 未区分 stdout/stderr 来源(quality)
    日志文件含两者合并输出,误匹配风险虽低但可考虑仅匹配 stderr。

  6. cleanup-db 默认值为 "true" 可能意外丢失对话数据(security)
    升级后超过 50MB 的 opencode.db 会自动删除,历史数据可能丢失。建议改为 "false" 默认或显著提醒。

  7. 路径校验禁止列表不完整(security)
    缺少 /data/backup 等常见数据目录。建议补充或提供可扩展校验机制。

  8. cleanup-db 输入设计问题(security + architecture)

    • 非法字符串如 "yes" 被静默使用默认阈值而非报错(security)
    • 该 input 同时承载 boolean、数字、禁用三种语义,违反单一类型原则(architecture)
      建议拆分为 cleanup-db-enabled(boolean)和 cleanup-db-max-size-mb(number)两个独立 input,并对非法输入显式报错。
  9. db-path 相对路径的 PWD 不一致(security)
    cleanup-db.sh 与迁移恢复模块在 working-directory 生效前后执行,若 db-path 为相对路径,两次 $PWD 可能不同。建议在 action.yml 中统一解析为绝对路径。

  10. run-opencode.shsource 在重试循环内(architecture)
    每次迁移失败重试都重新 source resolve-db-path.sh。建议将 source 提升到脚本顶部,循环内只调用函数。

  11. 路径查找方式不一致(architecture)
    cleanup-db.sh 使用 dirname "$0"run-opencode.sh 使用 cd "$(dirname "$0")" && pwd 再上翻。建议统一为一种绝对路径方案。


📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

审查概要

此 PR 为 github-run-opencoderun-opencode 添加了 SQLite 数据库清理与迁移失败自动恢复功能。整体设计合理,脚本编写质量较好,测试覆盖充分,但在 run-opencode.sh 中发现一个潜在的无限循环风险,需要在合并前修复。

阻塞项

  1. run-opencode.sh 迁移恢复中空 db_path 导致无限循环
    当迁移恢复的 grep 匹配成功,但 db_path 解析失败(如 resolve-db-path.sh 不存在或路径验证不通过)时,代码执行 rm -f "$log_file" 然后 continue,但既没有递增 attempt 也没有设置 migration_recovery_done=true。这导致下一次循环再次进入同一分支,形成有效无限循环(直到 GitHub Actions 任务超时)。
    修复建议:在 if [[ -z "$db_path" ]]; then 分支中,应在 continue 前设置 migration_recovery_done=true,或者直接 exit 1 避免循环。

建议项

  1. resolve-db-path.sh 中波浪号未展开
    如果用户通过 db-path 输入传入 ~/.local/share/opencode/opencode.db 形式的路径,dirname 不会展开 ~,导致路径解析到 $PWD/~/.local/share/...。建议明确使用 $HOME 或调用 eval echo 展开波浪号。不过默认路径(通过 $HOME 变量)工作正常,这属于边缘情况。

  2. resolve-db-path.shcd - 可能失败
    若当前工作目录在 cd "$dir" 后被删除,cd - 会失败且错误信息输出到 stderr。虽然不影响功能,但建议添加 || true 抑制错误。

  3. cleanup-db.sh 未清理 SQLite journal 文件
    opencode.db 运行在回滚日志模式(而非 WAL 模式),残留的 -journal 文件不会被删除。建议一起清理 "$db_path-journal",以防止将来重建数据库时的潜在干扰。

  4. 测试 test_no_db_file_is_noop 仅断言 stdout 为空
    resolve-db-path.sh 内部,cd - >/dev/null 的 stderr 未重定向,若 cd - 失败会污染 stderr。但测试未检查 stderr。建议在测试中增加对 stderr 的验证,或修复 cd - 的 stderr 重定向。

  5. run-opencode.sh 迁移恢复的 grep 未区分 stdout/stderr 来源
    虽然 $log_file 包含两者的合并输出,但误匹配风险较低。可考虑仅匹配 stderr 来源的输出,进一步提高可靠性。

security

存在风险 / AT RISK

安全审查总结

该 PR 新增了 SQLite 数据库清理和迁移自动恢复功能,整体安全设计较好,未发现严重漏洞。禁止访问系统目录的路径校验、Shell 注入防护、环境变量引用皆已正确处理。

阻塞项:无

建议项

  1. cleanup-db 默认值为 "true"(启用 50MB 阈值清理)
    该默认值会在升级后自动删除超过 50MB 的 opencode.db。虽然 db 是缓存性质,但历史对话和会话数据可能意外丢失。建议改为 "false" 默认值,或至少在 README 中显著提醒用户注意此行为变更。

  2. 路径校验的禁止列表不完整
    resolve-db-path.shforbidden 列表中缺少 /var/tmp/data/backup 等目录。虽然根据注释 /var/tmp 的排除是出于 macOS/CI 兼容性(/var/private/var),但 /data/backup 等目录不在排除之列。建议补充这些常见数据目录,或者提供一个可扩展的校验机制。

  3. cleanup-db 输入的非预期字符串被静默忽略
    当传入既非 "true" 又非纯数字的字符串时(如 "yes""enable"),脚本不会报错或拒绝执行,而是直接使用默认阈值 50MB。这可能导致用户以为关闭了清理功能但实际并未关闭。建议对不合法输入显式报错退出。

  4. run-opencode.shdb-path 相对路径的 PWD 不一致问题
    cleanup-db.shrun-opencode.sh 的迁移恢复模块分别在 working-directory 生效前后运行。若用户传入相对路径的 db-path,两次的 $PWD 可能不同,导致路径解析不一致。建议在 action.yml 中统一解析为绝对路径后再传递。

performance

性能良好 / GOOD

本次 PR 引入的功能是 SQLite 数据库的前置清理迁移失败自动恢复,均为轻量级 shell 脚本,无新增数据库查询、无复杂算法、无内存分配问题。

分析要点

  • 算法复杂度:文件 stat + 大小比较,O(1);grep 日志匹配 -q 模式在首匹配时停止,效率良好。
  • 内存分配:纯 shell 脚本,无堆内存分配,无泄漏风险。
  • 数据库查询:无新增 DB 查询,无 N+1 问题。
  • 缓存:无影响。
  • 冗余计算:无。
  • 并发cleanup-db.sh 存在 TOCTOU(time-of-check-time-of-use)窗口:statrm 之间若另一进程写入 DB 可能误删,但在 GitHub Actions 单 job 执行环境下风险可忽略。resolve-db-path.shcd + cd - 模式在 $PWD 被删除时可能无法恢复工作目录(无 set -e 不影响执行),但非性能问题。
  • 资源利用:仅读取文件 inode 元数据(stat -c%s),不读数据块,I/O 极低。

阻塞项

阻塞项:无

建议项

建议项:无

architecture

架构合理 / SOUND

架构分析摘要

本次 PR 引入了 SQLite 数据库清理与迁移恢复功能,整体架构设计合理:

  1. 模块提取正确resolve-db-path.sh 作为共享模块放在 shared/ 下,被 cleanup-db.shrun-opencode.sh 共同引用,避免了路径解析逻辑的重复
  2. 职责分离清晰 — 预防性清理(cleanup-db.sh,在运行前检查大小)和反应式恢复(run-opencode.sh 重试循环内,迁移失败后删除)是两个独立机制,各自有清晰的触发条件
  3. 符合既有模式 — 使用 shared/ 目录存放跨 action 共享脚本、使用 OPENCODE_* 环境变量传参、composite action 中通过 run: 调用 shell 脚本,均与现有架构一致
  4. 接口设计合理db-pathcleanup-db 作为 action input 暴露,环境变量作为内部的配置通道

阻塞项:无

建议项

  1. run-opencode.sh 中的 source 时机 — 迁移恢复逻辑在重试循环内才 source resolve-db-path.sh,导致每次失败重试都可能重新 source。建议将 source 与函数定义提升到脚本顶部(run-opencode.sh 开头),循环内只调用 _resolve_db_path,减少 I/O 开销也更具可读性。

  2. cleanup-db 输入的多态设计 — 该 input 同时承载 "true"(启用,阈值 50MB)、数字字符串(自定义阈值)和 "false"(禁用)三种语义,违反了单一类型原则。建议拆分为 cleanup-db-enabled(boolean)和 cleanup-db-max-size-mb(number)两个独立 input,保持类型清晰。当前方案虽然紧凑,但增加了 action.yml 和脚本中的解析复杂性,也降低了类型安全性。

  3. 路径查找方式不一致cleanup-db.sh 使用 dirname "$0" 引用同级脚本,而 run-opencode.sh 使用 cd $(dirname "$0") && pwd 后再上翻目录。虽然实际路径都正确,但这种不一致在出现符号链接或容器环境时可能导致不同行为。建议统一为一种方案,如统一使用 $(cd "$(dirname "$0")" && pwd) 获取绝对路径。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 80b1ec5 to 05d7a9f Compare June 7, 2026 04:52
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已完成所有文件的完整分析。以下是我的裁决:

无遗漏

对照 PR body 中的 spec 逐项验证,所有需求均已完整实现。

方案 A(预防性清理):

  • shared/cleanup-db.sh:35 GNU stat -c%s 优先获取大小(BSD stat -f%z 回退)✅
  • shared/cleanup-db.sh:24 默认阈值 50MB ✅
  • shared/cleanup-db.sh:38-41 超阈值删除(含 -wal/-shm/-journal)✅
  • shared/cleanup-db.sh:26-28 零/负阈值禁用清理 ✅
  • github-run-opencode/action.yml:156-163 cleanup-db input(true/数字/false)✅
  • github-run-opencode/action.yml:251-263 install 与 run 之间的 cleanup step ✅
  • github-run-opencode/action.yml:255 OPENCODE_DB_PATH 接线到 cleanup 步骤 ✅
  • github-run-opencode/action.yml:292 OPENCODE_DB_PATH 接线到 run 步骤 ✅

方案 C(迁移自动恢复):

  • run-opencode/run-opencode.sh:148 grep -qim1 "duplicate column name" 检测错误 ✅(-m1 提前退出)
  • run-opencode/run-opencode.sh:160 rm -f 删库(含 -wal/-shm/-journal)✅
  • run-opencode/run-opencode.sh:161 migration_recovery_done=true 标志位 ✅
  • run-opencode/run-opencode.sh:148 检查 $migration_recovery_done == "false",限制仅重试一次 ✅

#200 修复项:

  • dead-code false 检查移除(action.yml:251 if: 守卫)✅
  • GNU stat 优先(cleanup-db.sh:35)✅
  • OPENCODE_DB_PATH 两步骤接线 ✅
  • 空字符串守卫(resolve-db-path.sh:18 + run-opencode.sh:150-157)✅
  • 路径安全校验(resolve-db-path.sh:35-47 黑名单拒绝系统目录)✅
  • 测试覆盖:TestCleanupDb(7 tests)+ TestMigrationRecovery(3 tests)= 10 tests(超出 PR 声称的 8 个)✅

文档: 3 个 README 均已更新对应功能说明 ✅

关于 run-opencode/action.yml 该 action 未传递 OPENCODE_DB_PATH,但脚本第 149 行有默认路径兜底(${OPENCODE_DB_PATH:-$HOME/.local/share/opencode/opencode.db}),且 Issue #198 和 PR body 均未要求修改该文件,不构成功能遗漏。

实现范围超出了 spec 描述:额外包含了 resolve-db-path.sh 共享路径安全校验组件、WAL/SHM/journal 附属文件清理、零阈值保护、db-path 输入等增强功能。

New%20session%20-%202026-06-07T04%3A53%3A06.880Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:可合并 / CAN MERGE

所有 reviewer 均未发现阻塞性问题。整体代码质量良好,安全措施适当,性能影响极低。建议项以非阻塞优化为主。


阻塞项:无


建议项

已确认(2+ reviewer 提及):

  1. shared/resolve-db-path.shcd 修改 sourced shell 工作目录_resolve_db_path 内部用 cd "$dir" 改变当前目录后虽试图 cd - 恢复,但信号中断或复杂嵌套场景下可能未正确恢复。建议改用 resolved="$(cd "$dir" && pwd)/$(basename "$raw")" 避免副作用。(quality, performance)

  2. shared/resolve-db-path.shreadlink/greadlink 跨平台后备:在仅安装 macOS 自带工具(无 GNU readlink)的自托管 runner 上符号链接解析会跳过,路径校验可能不完整。command -v 后备检查每次执行都重复。建议改用 realpathperl -MCwd 跨平台方案。(security, performance)

仅单 reviewer 提及:

  1. shared/cleanup-db.sh:262>/dev/null 掩盖非数字输入错误:若 OPENCODE_DB_MAX_SIZE_MB 为非数字值,[[ "$max_mb" -le 0 ]] 2>/dev/null 静默忽略错误,脚本继续执行可能误删数据库。建议先显式校验输入是否为有效数字。(quality)

  2. run-opencode/run-opencode.shcontinue 跳过 attempt 递增:migration recovery 触发后的 continue(行 157, 163)跳过了 attempt=$((attempt + 1))(行 180),导致 recovery 后的重试不消耗 attempt 计数,等价于多一次"隐形"重试。建议递增后再 continue 或补充注释说明有意为之。(quality)

  3. run-opencode/run-opencode.sh — 迁移恢复与重试循环职责混合:原有循环设计意图处理"网络抖动类重试",现混入"数据库损坏恢复"这一不同关注点。建议将迁移恢复逻辑独立提取到重试循环之外,或检测到迁移失败后退出循环交由上层处理。(architecture)

  4. run-opencode/run-opencode.sh — 迁移恢复中内联 DB 删除逻辑rm -f "$db_path" "$db_path-wal" ...cleanup-db.sh 中的删除逻辑重复。建议 cleanup-db.sh 同时支持"按大小清理"和"强制清理"两种模式,迁移恢复时直接调用以避免逻辑分散。(architecture)

  5. shared/resolve-db-path.sh:47 — 路径解析结果重复计算migration_recovery_done 标记确保 recovery 最多执行一次,但若误删标记则有重复执行风险。建议缓存路径解析结果。(performance)

  6. run-opencode/run-opencode.shgrep -qim1 每次失败都读取整个日志文件:多次重试场景下每次失败都 grep 重复读取同一日志。建议 recovery 触发后跳过 grep 减少重复 I/O。(performance)

  7. shared/cleanup-db.shstat 跨平台后备静默跳过stat 因权限等问题失败时 echo 0 后备会跳过清理,可能导致大文件残留。虽行为正确,但建议记录警告日志。(performance)

  8. shared/resolve-db-path.sh:53 — macOS /private 路径校验:禁止目录列表不含 /private(macOS),注释说明与 /var 解析到 /private/var 相关。建议显式添加注释说明设计理由,或增加 macOS /private 路径校验逻辑。(security)

  9. shared/resolve-db-path.sh_OPENCODE_DB_PATH 变量命名:以下划线开头的全大写变量在 shell 中通常表示"内部常量",但通过 source + 函数导出的模式不够直观。建议直接 echo 输出由调用方捕获,或使用更明确的命名约定。(architecture)

  10. tests/test_all.py — 多余空行:类定义前连续三个空行,PEP 8 建议两个。(quality)

  11. github-run-opencode/action.yml:292OPENCODE_DB_PATH 传入 Python 进程但未展现用途:若未在 Python 侧使用,建议移除避免混淆。(quality)


📋 各 Reviewer 详细审查结果
quality

Let me read the full run-opencode.sh to check loop structure.
现在让我检查一下 REPO_ROOT 在测试文件中的定义位置。
可合并 / CAN MERGE

整体代码质量良好,功能逻辑正确,测试覆盖充分。以下是详细审查意见:

阻塞项:无

建议项:

  1. shared/resolve-db-path.sh:24cd 修改 sourced shell 的工作目录_resolve_db_path 内部使用 cd "$dir" 改变当前目录(因为该文件被 source 加载,非子进程),虽然 cd - 尝试恢复,但如果出现信号中断或在复杂嵌套场景下调用,工作目录可能未恢复。建议改用 resolved="$(cd "$dir" && pwd)/$(basename "$raw")" 方式避免影响当前 shell。

  2. shared/cleanup-db.sh:262>/dev/null 掩盖非数字输入错误if [[ "$max_mb" -le 0 ]] 2>/dev/null 中,若 OPENCODE_DB_MAX_SIZE_MB 被设非数字值(如 abc),[[ ]] 报错但被静默忽略,条件为 false 后脚本继续执行并可能误删数据库。建议先显式校验输入是否为有效数字,或报告错误退出。

  3. run-opencode/run-opencode.sh:157,163continue 跳过 attempt 递增:migration recovery 触发后的 continue 跳过了第 180 行的 attempt=$((attempt + 1)),导致 recovery 后的重试仍使用原 attempt 计数值。虽然不会引发无限循环(recovery 仅触发一次),但行为等价于给了用户配置的 OPENCODE_ATTEMPTS 一次额外的"隐形"重试。建议在 continue 前先递增 attempt,或明确在注释中说明这是有意为之。

  4. tests/test_all.py:1322-1324 — 多余空行:类定义前有三个连续空行,PEP 8 规范建议类定义前保留两个空行。

  5. github-run-opencode/action.yml:292OPENCODE_DB_PATH 传入 Python 进程但未在 PR 中展示用途:环境变量 OPENCODE_DB_PATH 被传递给主执行步骤的 Python 脚本环境,但 PR diff 不包含 Python 脚本的改动。若未在 Python 侧使用,建议移除避免混淆。

security

安全无虞 / SECURE

本次 PR 未发现安全漏洞。代码实现了 opencode.db 的清理与迁移恢复机制,主要安全控制措施适当:

正向安全措施:

  • resolve-db-path.sh 对数据库路径做了绝对路径规范化(通过 cd/pwdreadlink -f),并拒绝指向系统敏感目录(/etc/usr/bin 等)的路径
  • cleanup-db.shrun-opencode.sh 中所有变量均使用双引号包裹,无命令注入风险
  • action.ymlcleanup-db 输入值经正则 ^[0-9]+$ 校验后才作为阈值使用
  • GitHub Actions ${{ }} 表达式语法天然防止 shell 注入
  • 删除 WAL/SHM 日志文件时也限制在已校验的路径下

阻塞项:无

建议项:

  1. resolve-db-path.sh:47 在只安装 macOS 系统(无 GNU readlink)的自托管 runner 上,符号链接解析会跳过,路径校验可能不完整。建议将 readlink -f + greadlink 的双重方案改为 realpathperl -MCwd -e 'print Cwd::abs_path' 跨平台方案。
  2. resolve-db-path.sh:53 的禁止目录列表不含 /private(macOS),但该条与注释 "macOS 会将 /var 解析到 /private/var" 权衡取舍。建议显式添加注释说明 /private 不被禁止的设计理由,或增加 macOS /private 路径的校验逻辑。
performance

性能有疑虑 / CONCERNS

本次 PR 新增了 SQLite 数据库的清理和迁移恢复逻辑,整体实现轻量,无严重性能问题。主要分析如下:

阻塞项:无

建议项:

  1. run-opencode.shgrep -qim1 每次失败都读取整个日志文件:虽然日志通常不大,但若某次运行产生大量输出且在成功前多次重试,每次失败都 grep 一次会重复读取同一日志。建议在 recovery 触发后(或确认非 migration 错误后)跳过 grep,减少重复 I/O。

  2. resolve-db-path.sh 使用 cd + pwd 进行路径解析:每次调用都会触发进程内目录切换(cdcd -),虽然开销极低,但在 shell 脚本中属于不必要操作。使用 realpath[[ "$raw" = /* ]] + dirname 字符串操作即可完成,无需实际切换目录。

  3. resolve-db-path.sh 在每个失败重试中都被 source 一次(直到 recovery 完成):在 run-opencode.sh 中,recovery 块在 migration_recovery_done=false 期间每次失败都会 source 该脚本并调用 _resolve_db_path。建议将路径解析结果缓存到变量中,避免多次重复计算。当前因 migration_recovery_done 标记最多只执行一次,但若误删标记则有重复执行风险。

  4. resolve-db-path.shgreadlink/readlink 后备分支:优先尝试 greadlink(macOS),未找到时 fallback 到 readlink。每次 fallback 都会执行 command -v 检查,虽开销可忽略,但可通过一次检测并缓存结果优化。

  5. cleanup-db.shstat 跨平台后备模式stat -c%s(GNU)|| stat -f%z(BSD)|| echo 0echo 0 后备会导致 size 为 0,不会触发删除,行为正确。但若 stat 因其他原因失败(如权限问题),会静默跳过清理,可能导致大文件残留。

以上均为非阻塞性优化建议,整体性能影响极低。

architecture

架构有疑虑 / CONCERNS

架构分析摘要

该 PR 在 shared/ 下新增了两个脚本(cleanup-db.shresolve-db-path.sh),并在 github-run-opencode/action.ymlrun-opencode/run-opencode.sh 中分别引入数据库清理和迁移失败恢复功能。整体设计的方向是正确的——将共用逻辑放在 shared/ 中复用。

阻塞项

建议项

  1. run-opencode.sh 的迁移恢复与重试循环职责混合run-opencode.sh:144-167 在重试循环内部嵌入了 SQLite 迁移失败的恢复逻辑。原有循环的设计意图是处理"网络抖动类重试",现在混入了"数据库损坏恢复"这一完全不同层面的关注点。建议将迁移恢复逻辑独立提取为一个单独的阶段(在重试循环之外),或者在显式检测到迁移失败后退出循环并交由上层统一处理,而非混入同一个 while+continue 模式中。

  2. 迁移恢复中内联了 DB 删除逻辑run-opencode.sh:159 直接内联了 rm -f "$db_path" "$db_path-wal" ...,这与 cleanup-db.sh 中的删除逻辑重复。建议 cleanup-db.sh 同时支持"按大小清理"和"强制清理"两种模式,迁移恢复时直接调用 cleanup-db.sh,避免删除逻辑在多处维护。

  3. resolve-db-path.sh_OPENCODE_DB_PATH 变量命名:以下划线开头的全大写变量在 shell 中通常表示"内部/不应被修改"的常量,但通过 source + 函数导出后在调用方使用,这种模式不够直观。建议考虑直接输出到 stdout(echo "$resolved")由调用方捕获,或使用更明确的命名约定。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 05d7a9f to 17ef5e6 Compare June 7, 2026 04:57
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:可合并 / CAN MERGE

四个 reviewer 均认为 PR 质量良好、安全无虞、性能无影响、架构设计合理,无任何阻塞项。

阻塞项:无

建议项:

  1. resolve-db-path.sh 使用 perl 解析符号链接,若 runner 未安装 perl 会输出 "command not found" 到 stderr。建议开头检查 perl 可用性并降级处理,或至少将 perl 调用包装 2>/dev/null。(quality)

  2. resolve-db-path.sh 第 28 行 for prefix in $forbidden 未加引号,若后续添加含空格的路径会引入 word-splitting 行为,建议使用数组或 set -f 保护。(quality)

  3. run-opencode.sh 中迁移恢复路径下,当 _resolve_db_path 失败时(db_path 为空),脚本仅标记 recovery done 后 retry 但未删除 db,retry 几乎必然再次失败。建议尝试使用默认路径 ~/.local/share/opencode/opencode.db 作为 fallback 执行删除。(quality)

  4. run-opencode.sh 直接调用带 _ 前缀的 _resolve_db_path(私有函数约定),形成内容耦合。建议将函数改名为 resolve_db_path 或在 resolve-db-path.sh 顶部注释中声明其为公开 API。(architecture)

  5. 迁移恢复逻辑硬编码 duplicate column name 作为错误匹配条件,若 opencode 错误信息变更会静默失效。建议考虑将匹配模式抽取为可配置项。(architecture)


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

该 PR 整体质量良好,引入的 SQLite 数据库清理和迁移错误自动恢复机制设计合理,测试覆盖全面,错误处理路径清晰。

阻塞项:无

建议项:

  1. resolve-db-path.sh 使用 perl 解析符号链接,若 runner 未安装 perl 会输出 "command not found" 到 stderr。建议在 resolve-db-path.sh 开头检查 perl 可用性并降级处理,或至少将 perl 调用包装 2>/dev/null 避免混淆日志。
  2. resolve-db-path.sh 第 28 行的 for prefix in $forbidden 未加引号,虽在硬编码路径下无害,但若后续添加含空格的路径会引入 word-splitting 行为,建议使用数组或显式 set -f 保护。
  3. run-opencode.sh 迁移恢复路径中,当 _resolve_db_path 失败时(db_path 为空),脚本仅标记 recovery done 后 retry,但此时未删除 db,retry 几乎必然再次失败。建议在此路径下也尝试使用默认路径 ~/.local/share/opencode/opencode.db 作为 fallback 执行删除,而非直接跳过。
security

安全无虞 / SECURE

该 PR 的变更专注于添加 SQLite 数据库清理和迁移恢复功能。经逐行审查,所有代码在安全方面表现良好:

输入验证与清理cleanup-db 输入在 action.yml 中通过正则 ^[0-9]+$ 校验后才赋值给 OPENCODE_DB_MAX_SIZE_MBdb-path 输入在 resolve-db-path.sh 中有完备的空值回退、绝对路径解析、符号链接解析(perl Cwd::abs_path)以及 17 个系统关键目录的禁止列表检查。

注入防护:所有 shell 变量均正确引号包裹,无命令注入向量。Perl 调用参数通过 $ARGV[0] 传递,无 Perl 代码注入风险。无 SQL 查询操作,因此无 SQL 注入。

路径遍历resolve-db-path.sh 通过 perl -MCwd -e 'print Cwd::abs_path(...)' 解析符号链接和相对路径(如 ..),有效防止路径遍历攻击。禁止列表涵盖 /etc /usr /bin /sbin /lib /boot /proc /sys /dev /opt /root /mnt /media /srv /run

敏感数据暴露:无 token、密钥或敏感信息被记录。打印的路径信息仅涉及数据库文件路径,属于非敏感信息。

竞态条件:TOCTOU 在 CI 环境(隔离容器/VM)中不可实际利用。

依赖安全性:仅使用标准 POSIX 工具(bash、stat、perl、dirname),无不安全的外部依赖。

阻塞项:无

建议项:无

performance

性能良好 / GOOD

该 PR 引入的功能(数据库清理脚本 cleanup-db.sh、路径解析辅助 resolve-db-path.sh、以及 run-opencode.sh 中的迁移失败自动恢复)均涉及一次性、轻量级的 shell 操作,不包含任何可感知的性能风险。

  • cleanup-db.sh 仅执行一次 stat 文件属性读取 + 整数比较 + 可选的文件删除,单次操作开销可忽略不计
  • resolve-db-path.sh 的路径解析和系统目录校验(对约 14 个前缀的循环检查)均为 O(1) 操作,Perl 进程开销在 CI 场景下可忽略
  • 迁移恢复逻辑中 grep -qim1 在日志文件上仅匹配到首个结果即停止,且恢复只触发一次(migration_recovery_done 标志控制),性能影响极小
  • 所有操作均为文件系统元数据操作,不存在数据库查询、内存分配、网络 I/O 或并发竞争问题

阻塞项:无

建议项:无

architecture

架构合理 / SOUND

该 PR 的架构设计合理,遵循了现有的模式和约定,具体分析如下:

  • 模块位置shared/cleanup-db.shshared/resolve-db-path.sh 放在 shared/ 目录下是正确的,符合该目录"被多个 action 共享使用"的定位。测试用例放入 tests/test_all.py,与现有约定一致。
  • 耦合度resolve-db-path.sh 作为 source-only 库被 cleanup-db.shrun-opencode.sh 复用,避免了路径解析逻辑的两处复制,是良好的解耦设计。
  • 分层:数据库清理(cleanup-db.sh)作为独立步骤在 action.yml 中调用,与运行逻辑分离。迁移恢复逻辑嵌入 run-opencode.sh 的运行循环内部,与业务逻辑紧密相关,层次合理。
  • 接口设计:环境变量驱动的配置方式(OPENCODE_DB_PATHOPENCODE_DB_MAX_SIZE_MB)与 repo 内其他脚本风格一致。cleanup-db input 支持 "true" / 数字 / "false" 三种格式,设计灵活。
  • 一致性:使用了 repo 内已有的 set -uo pipefail::warning:: / ::error:: workflow command、PERL 跨平台路径解析等模式。

阻塞项:无

建议项:

  1. run-opencode.sh 直接调用 _resolve_db_path(带 _ 前缀的私有函数),与 _resolve_db_path 形成内容耦合(content coupling)。建议将 _resolve_db_path 改为 resolve_db_path(去掉下划线前缀),或在 resolve-db-path.sh 顶部注释中明确声明该函数为公开 API,以表明这是 source-only 库的导出接口而非私有实现细节。
  2. 迁移恢复逻辑硬编码了字符串 duplicate column name 作为触发条件。如果未来 opencode 的 SQLite 错误信息发生变化,该逻辑会静默失效。建议考虑将错误匹配模式抽取为环境变量或与 version 文件关联的可配置项,但鉴于目前仅一个匹配模式,当前实现也是可接受的折衷。

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 PR body 中的 spec 逐项验证,所有需求均已完整实现:

方案 A(启动前预防性清理)

  • shared/cleanup-db.sh 文件存在检测(line 36)、GNU stat -c%s 优先(line 40)、可配置阈值默认 50MB(line 24)、超阈值删除含附属文件(line 49)✅
  • github-run-opencode/action.yml 新增 cleanup-db input(line 156-163)支持 "true"/数字/"false",以及在 install 与 run 之间的 cleanup step(line 251-263)✅

方案 C(迁移失败自动恢复)

  • run-opencode/run-opencode.sh line 150 grep -qim1 "duplicate column name" 检测错误(-m1 提前退出)✅
  • line 166 rm -f 删库(含 -wal/-shm/-journal)✅
  • migration_recovery_done 标志位(line 133, 150, 167)限制仅重试一次 ✅

#200 修复项

  • dead-code 移除(step if: 守卫 line 251)✅
  • GNU stat 优先(cleanup-db.sh:40)✅
  • OPENCODE_DB_PATH 接线至 cleanup(line 255)和 run(line 292)两步骤,实际使用 inputs.db-path 优于原计划 env.OPENCODE_DB_PATH,规避了 composite action 编译期求值问题 ✅
  • 空字符串守卫(resolve-db-path.sh:25 + run-opencode.sh:156-159)✅
  • 路径安全校验(resolve-db-path.sh:48-63 黑名单拒绝 17 个系统目录)✅
  • 测试覆盖:TestCleanupDb 7 tests + TestMigrationRecovery 3 tests = 10 tests(超出 PR 声称的 8 个)✅

文档:3 个 README 均已更新对应功能说明 ✅

实现范围整体超出 spec 描述(额外包含 resolve-db-path.sh 共享路径安全校验组件、db-path 输入、WAL/SHM/journal 附属文件清理、零阈值禁用保护等)。

New%20session%20-%202026-06-07T05%3A21%3A47.058Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:可合并 / CAN MERGE

所有 reviewer 均无阻塞项。综合来看,PR 质量高、安全措施到位、性能无影响,但架构和代码质量方面有几项值得采纳的建议。

阻塞项:无

建议项:

  1. 层抽象违规:run-opencode.sh 不应耦合 SQLite 细节(architecture)
    run-opencode/run-opencode.sh 通过 grep -qim1 "duplicate column name" 硬编码了 SQLite 特定错误消息,将其与具体数据库后端耦合。若 opencode 变更数据库实现或调整错误提示,此逻辑会静默失效。建议将迁移恢复逻辑提升至 github-run-opencode 层,或设计为通过环境变量配置的通用重试条件。

  2. resolve-db-path.sh 路径回退逻辑重复(architecture)
    run-opencode.sh 的 recovery 分支有自己的路径硬编码回退($HOME/.local/share/opencode/opencode.db),而 resolve-db-path.sh 已封装此逻辑。建议统一通过 resolve_db_path 处理默认值。

  3. resolve-db-path.shos.path.realpath 兼容性(quality)
    os.path.realpath 在 Python 3.6+ 中对不存在的路径行为有变化。建议加 try/except 捕获异常,或考虑纯 bash 替代方案(如 readlink -f)。

  4. macOS stat-- 参数兼容性(quality)
    stat -f%z -- "$db_path" 中的 -- 在部分平台可能不被支持。建议移除 --,或统一用 python3 -c "import os; print(os.path.getsize(...))" 避免平台差异。

  5. 迁移 recovery 跳过延迟需显式记录(quality)
    recovery 分支的 continue 跳过了 OPENCODE_RETRY_DELAY_SECONDS,意味着恢复重试是立即进行的。建议在注释中显式记录这是有意为之,避免未来混淆。

  6. 测试 test_migration_error_triggers_recovery 的 PATH 劫持校验(quality)
    测试通过 PATH 覆盖劫持 opencode 命令,若 CI 环境变化可能导致静默失效。建议增加校验(如检查 mock 脚本的可执行权限或 PATH 首位)。


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

代码质量较高,结构清晰,边界情况和安全性都考虑周全。

阻塞项:无

建议项:

  1. run-opencode.sh 中 migration recovery 的 continue 跳过了 OPENCODE_RETRY_DELAY_SECONDS,意味着恢复重试是立即进行的(无延迟回退)。虽然对当前场景是合理的(删除 stale db 后立即重试),但建议在注释中显式记录这一点以避免未来混淆。
  2. resolve-db-path.shos.path.realpath 在 Python 3.6+ 中对不存在的路径行为有变化(旧版本返回到达点,新版本在某些情况下可能抛出 FileNotFoundError)。建议改用 os.path.realpathtry/except 或直接 fallback 而不依赖 python3 行为。
  3. macOS stat-- 参数兼容性不确定(stat -f%z -- "$db_path"),建议去掉 -- 改用 stat -f%z "$db_path",或统一用 python3 -c "import os; print(os.path.getsize('$db_path'))" 避免平台差异。
  4. 测试 test_migration_error_triggers_recovery 使用 PATH 覆盖劫持 opencode 命令,若 CI 环境变化导致 PATH 中先找到其他 opencode 会静默失败,建议增加校验(如 assert self.fake_opencode.stat().st_mode & 0o111 或验证 PATH 首位)。
security

安全无虞 / SECURE

本次 PR 新增了 cleanup-db.shresolve-db-path.sh 数据库清理/恢复功能,以及对应测试。代码质量较高,安全措施到位:

阻塞项:无

建议项:无

详细分析:

  • Shell 变量均使用双引号包裹,无命令注入风险
  • resolve-db-path.shdb-path 作了路径规范化(含 python3 os.path.realpath 解析符号链接)和系统目录黑名单校验(/etc/usr/bin 等),有效防止路径穿越和误删系统文件
  • max_mb 阈值经过正则 ^[0-9]+$ 校验,0 值额外处理为禁用清理
  • rm 删除仅操作通过校验的路径,且限制为 SQLite 配套文件(-wal-shm-journal
  • 迁移恢复逻辑在 resolve_db_path 执行失败且 OPENCODE_DB_PATH 已设置的情况下,跳过而非回退到不安全路径
  • 没有引入新依赖,python3 已是 action 的硬依赖
  • 所有输出均用 printf 配合 %s 格式化,无格式化字符串漏洞
  • 不需要处理密钥/令牌/PII,无敏感数据暴露风险
performance

性能良好 / GOOD

该 PR 主要新增了 SQLite DB 清理脚本(cleanup-db.sh)、路径解析辅助脚本(resolve-db-path.sh)以及 run-opencode.sh 中的迁移失败自动恢复逻辑。所有操作均为一次性开销,不存在性能阻塞项。

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

架构分析摘要

本次 PR 引入 SQLite 数据库清理与迁移失败自动恢复功能,整体方向合理,shared/ 下抽取 resolve-db-path.sh 公共模块的设计也值得肯定。但存在一个值得关注的层抽象违规问题。


阻塞项:无


建议项

  1. 层抽象违规:run-opencode.sh 不应包含 SQLite 特定逻辑
    run-opencode/run-opencode.sh 是通用的 opencode 命令执行器,其设计目的是对执行的命令内容保持透明。新增的 grep -qim1 "duplicate column name" 将脚本与 SQLite 的具体错误信息硬耦合。如果 opencode 变更数据库后端或调整错误提示消息,run-opencode.sh 会静默失效或产生误触发。建议将迁移失败恢复的逻辑提升到 github-run-opencode 层(它明确知道自己在运行 opencode github run),或设计为通过环境变量配置的通用重试条件模式(避免在通用脚本中嵌入特定技术栈的错误关键字)。

  2. resolve-db-path.sh 引入的 Python 间接依赖
    resolve-db-path.sh 中使用 python3 进行符号链接解析,理由为"already a hard dependency"。但 run-opencode.sh 在 recovery 路径中会 sourcing 此脚本,而在 run-opencode 的上下文中 Python 并非明确依赖。虽然 2>/dev/null || true 提供了降级,但从架构清晰度角度,符号链接解析的逻辑可以考虑使用纯 bash 实现(通过 readlink -frealpath 的 POSIX 兼容方案)以消除隐式依赖。

  3. run-opencode.sh 中的路径回退逻辑重复
    run-opencode.sh 的 recovery 分支有自己的路径硬编码回退 ($HOME/.local/share/opencode/opencode.db),这已经由 resolve-db-path.sh 封装。建议 recovery 流程统一通过 resolve_db_path 处理默认值,避免两处维护相同的默认路径常量。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 60d48e1 to 0ff7f93 Compare June 7, 2026 05:26
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已获取所有必要信息。让我基于实际的 spec(Issue #198)和代码实现进行全面对比分析。

无遗漏

对照 Issue #198 的逐项需求与当前代码完整验证:

方案 A(workflow 增加清理步骤)

  • 检测 ~/.local/share/opencode/opencode.db 是否存在 → cleanup-db.sh:36 [[ ! -f "$db_path" ]]
  • 检查文件大小,超过阈值(如 50MB)时删除 → cleanup-db.sh:40-49 GNU stat -c%s 优先 + 阈值比较 + rm -f
  • 或者检测版本变更删除:Issue 中列出的是"或者"的可选子项,文件大小方案已完全满足需求 ✅

方案 B(独立的 cron workflow)
Issue 明确推荐"方案 A + C 结合",方案 B 是给 latex-agent 仓库的独立替代方案,非本 PR 范围,不应实现 ✅

方案 C(migration 失败自动恢复)

  • 日志包含 duplicate column name 错误 → run-opencode.sh:152 grep -qim1 "duplicate column name" "$log_file"
  • 自动删除 opencode.dbrun-opencode.sh:169 rm -f "$db_path"(含 -wal/-shm/-journal)✅
  • 重试 opencode 启动 → run-opencode.sh:172 continue

推荐:方案 A + C 结合:两者均已实现 ✅

PR 自述变更(含 #200 修复项)

  • shared/cleanup-db.sh 新建脚本 ✅
  • cleanup-db input(true/数字/false)→ action.yml:156-163
  • 在 install 与 run 之间新增 cleanup step → action.yml:251-263
  • run-opencode.sh 迁移恢复逻辑 → 完整实现行 147-173 ✅
  • dead-code false 移除(step if: 守卫 action.yml:251)✅
  • GNU stat 优先(cleanup-db.sh:40)✅
  • OPENCODE_DB_PATH 接线至 cleanup(action.yml:255)和 run(action.yml:292)两步骤,实际使用 inputs.db-path(优于原计划的 env.OPENCODE_DB_PATH,规避了 composite action 编译期求值问题)✅
  • 空字符串守卫:resolve-db-path.sh:25 + run-opencode.sh:156-159
  • 路径安全校验:resolve-db-path.sh:48-68 黑名单拒绝 17 个系统目录 ✅
  • 测试覆盖:TestCleanupDb(7 tests)+ TestMigrationRecovery(3 tests)= 10 tests(超出 PR 声称的 8 个)✅

文档:3 个 README 均已更新:

  • github-run-opencode/README.md:19-20,50-51 新增 cleanup-dbdb-path 输入说明 ✅
  • run-opencode/README.md:27 新增 migration recovery 描述 ✅
  • shared/README.md:13-23 新增 cleanup-db.shresolve-db-path.sh 文档 ✅

额外实现超出 specresolve-db-path.sh 共享路径安全校验组件、零阈值禁用保护、db-path 输入、WAL/SHM/journal 附属文件清理等。

run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但 run-opencode.sh:153-166 有默认路径兜底且 Issue #198 未要求修改该文件,不构成功能遗漏。初始 review 中 Svitter 发现的空字符串静默失败阻塞问题已在 resolve-db-path.sh:25run-opencode.sh:156-159 中修复。

New%20session%20-%202026-06-07T05%3A27%3A04.505Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:可合并 / CAN MERGE

所有四位 reviewer 均未发现阻塞性问题。代码整体质量高、测试覆盖完善。建议项集中在防御性编程、安全纵深和架构解耦方面。

阻塞项: 无 / Blocking Issues: None


建议项 / Suggestions

  1. rm -f-- 分隔符 (quality) — cleanup-db.sh:49 rm -f "$db_path" "$db_path-wal" ... 建议改为 rm -f -- "$db_path" ... 防止 $db_path- 开头时的误解析。

  2. stat 失败与 0 字节文件无法区分 (quality) — cleanup-db.sh:40stat 失败时 echo 0 作为 fallback,与文件真实大小为 0 的情况无法区分。建议 stat 失败时 exit 1 或使用 -1 等哨兵值。

  3. 禁止目录添加 /var (security) — resolve-db-path.sh:57forbidden 数组未包含 /var。虽当前需用户显式配置才会触发,加入 /var 可提供更深的防御纵深。

  4. Python realpath 空返回未显式处理 (security) — resolve-db-path.sh:50-51python3 -c 在异常时 pass 不输出,后续 [[ -n "$real_resolved" ]] 检查会回退到未解析路径。建议对空返回做显式处理。

  5. 迁移恢复被静默跳过时缺乏告警 (security) — run-opencode.sh:160-163OPENCODE_DB_PATH 已设置但路径校验失败时,恢复逻辑被跳过但未输出任何警告。建议添加 ::warning:: 告知用户。

  6. 迁移恢复耦合了 SQLite 错误字符串 (architecture) — run-opencode.sh:152 通过 grep "duplicate column name" 检测迁移失败,将 SQLite 实现细节与 opencode 内部 schema 变更行为耦合。建议使用退出码或专用信号等更稳定的方式。

  7. Python3 作为路径解析的硬依赖导致层间倒挂 (architecture) — resolve-db-path.sh:44python3realpath 而非 readlink -f,将 shell 工具库与高层语言运行时耦合。如保留 python3 方案,建议在脚本入口处提前校验 python3 可用性。

  8. 恢复重试的控制流与通用 retry 逻辑不一致 (architecture) — run-opencode.sh:150-172 中 recovery 用 continue 且不递增 $attempt、不等待 RETRY_DELAY_SECONDS,使 retry 计数语义复杂化。建议将恢复重试与通用 retry 循环分离。

  9. 默认 DB 路径在两处重复定义 (architecture) — 默认路径 $HOME/.local/share/opencode/opencode.dbresolve-db-path.sh:22run-opencode.sh:166 各定义一次。建议 run-opencode.sh 通过 sourcing resolve-db-path.sh 获取默认值。


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

代码整体质量高,逻辑正确,测试覆盖充分。新的 cleanup-db 和迁移自动恢复功能设计合理,错误处理路径清晰。

阻塞项:无

建议项:

  1. run-opencode.shrm -f "$db_path" "$db_path-wal" "$db_path-shm" "$db_path-journal" 建议在 "$db_path"-wal 之间加 -- 分隔符(即 rm -f -- "$db_path" "$db_path-wal" ...),防止 $db_path 出现以 - 开头的极端情况(当前实际不可能发生,属防御性编程)

  2. cleanup-db.shstat fallback 使用 echo 0 导致文件真正大小为 0 时无法区分是 stat 失败还是文件就是 0 字节。建议对 stat 失败的情况使用 exit 1 或明确传递不同的哨兵值(如 -1

  3. resolve-db-path.sh 中目录不存在时(如首次运行)手动构建绝对路径,会漏掉对 $HOME 等变量展开后的路径做安全检查。当前场景下 $HOME 是受信任的,无需修改

security

存在风险 / AT RISK

该 PR 引入了 cleanup-db.shresolve-db-path.sh,用于在运行 opencode 前清理过大的 SQLite 数据库,以及从迁移失败中自动恢复。整体安全设计良好,包含路径验证、输入检查和引用防范,但存在以下可改进的防御深度问题:

阻塞项:无

建议项:

  1. 禁止目录列表缺少 /var。Linux 下 /var 包含日志、数据库、邮件等系统敏感数据,虽被删文件需匹配 .db 后缀且需用户显式配置,但加入 /var 可提供更深度的防御。建议在 resolve-db-path.sh:49forbidden 数组中添加 /var
  2. resolve-db-path.shpython3 -c 的 Python 代码在异常时 pass,不输出任何内容。如果 realpath 因其他原因返回空字符串(非异常),[[ -n "$real_resolved" ]] 会跳过解析结果,回退到未解析路径。建议对空返回明确处理,确保路径始终被正确解析。
  3. run-opencode.shresolve_db_path 返回非零(路径校验失败)时,如果用户设置了 OPENCODE_DB_PATH,恢复逻辑被静默跳过而未给出告警。建议加一条 ::warning:: 告知用户由于路径校验未通过,迁移恢复被跳过。
performance

性能良好 / GOOD

性能分析总结

本次 PR 新增了 SQLite 数据库清理和迁移失败自动恢复功能。整体性能开销极低,均为一次性 O(1) 操作:

  • cleanup-db.sh: 一次 stat 读取文件大小 + 必要时 rm 删除,无循环或重复操作
  • resolve-db-path.sh: 路径解析使用 cd 子 shell 和 python3 realpath,后者是主要额外开销(Python 启动约 100-200ms),但在 CI action 整体执行时间中可忽略
  • run-opencode.sh 迁移恢复: 仅在命令失败后对日志文件执行一次 grep -qim1(匹配即停止),然后删除数据库文件并重试一次,无额外轮次浪费
  • 日志文件通过 mktemp 创建并由 rm -f 清理,无泄漏
  • WAL/SHM/journal 相关文件一并清理,无残留

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

架构分析摘要

此 PR 总体架构设计良好,共享脚本 (resolve-db-path.shcleanup-db.sh) 的提取是正确决策,shared/ 目录定位合理,安全验证(拒绝系统目录)是负责任的做法。测试覆盖全面。但在以下方面存在架构性疑虑:

阻塞项:无

建议项

  1. run-opencode.sh 的 SQLite 迁移恢复耦合了错误层 — 在通用的 retry 循环里通过字符串匹配 "duplicate column name" 来触发数据库删除,将 SQLite 实现细节opencode 内部 schema 变更行为 耦合到了 runner 脚本中。如果 opencode 更换数据库引擎或错误信息格式变更,此逻辑会静默失效。建议考虑更稳定的检测方式(如 opencode CLI 提供退出码或专用信号)。

  2. resolve-db-path.sh 依赖 python3 做 realpath 解析存在层间倒挂 — 一个 shell 工具库不应该依赖高层语言运行时完成本应由 readlink -frealpath 完成的路径解析。虽然 python3 在此 action 中已是硬依赖,但将调用下沉到更高抽象层引入不必要的层间耦合。如保留 python3 方案,建议将 python3 可用性验证前移到脚本入口处。

  3. run-opencode.sh 中的 recovery 控制流存在非递增 continue 模式 — recovery 使用 continue 且不递增 $attempt,用状态变量 migration_recovery_done 防止无限循环。这种"免费重试"逻辑使 retry 计数语义复杂化,且与其他 retry 路径行为不一致(不等待 RETRY_DELAY_SECONDS、不递增 attempt 计数)。建议将 recovery retry 单独处理(如在外层循环前先尝试一次),避免侵入通用 retry 逻辑。

  4. 默认 DB 路径存在重复定义resolve-db-path.sh 中的默认路径 $HOME/.local/share/opencode/opencode.dbrun-opencode.sh 的 fallback 中以硬编码形式重复出现。若未来默认路径变更,两处需同步修改,存在 1 处遗漏的风险。建议 run-opencode.sh 也 sourcing resolve-db-path.sh 来获取默认值。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 0ff7f93 to dc330ab Compare June 7, 2026 05:31
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已获取所有必要信息。以下是我的分析:

无遗漏

对照 Issue #198 的所有需求与当前代码进行完整验证:

方案 A(workflow 增加清理步骤)

  • DB 存在检测:shared/cleanup-db.sh:36 [[ ! -f "$db_path" ]]
  • 文件大小检查,超阈值删除:shared/cleanup-db.sh:40-53 stat -c%s(GNU 优先)→ size_mb -ge max_mbrm -f
  • 阈值可配置:cleanup-db input 支持 "true"/数字/"false"action.yml:156-163)✅
  • install 与 run 之间新增 step:action.yml:251-263
  • 版本变更检测:Issue feat: 添加定期清理 opencode.db 的 workflow run #198 中列为"或者"的可选子项,文件大小方案已完全满足需求 ✅

方案 B(独立 cron workflow):Issue 明确推荐"方案 A + C 结合",方案 B 是给 latex-agent 仓库的独立替代方案,不在本 PR 范围内 ✅

方案 C(migration 失败自动恢复)

  • 检测 duplicate column namerun-opencode/run-opencode.sh:152 grep -qim1-m1 提前退出)✅
  • 自动删除 DB(含 -wal/-shm/-journal):第 170 行 rm -f -- "$db_path" "$db_path-wal" "$db_path-shm" "$db_path-journal"
  • 重试一次:第 173 行 continue
  • migration_recovery_done 标志位(第 133、152、171 行)限制仅恢复一次 ✅

推荐:方案 A + C 结合 → 两者均已实现 ✅

#200 修复项(PR 承诺修复的全部验证通过):

  • dead-code false 检查移除(step if: 守卫 action.yml:251)✅
  • GNU stat -c%s 优先(cleanup-db.sh:40)✅
  • OPENCODE_DB_PATH 接线至 cleanup(action.yml:255)和 run(action.yml:292)两步骤,实际使用 inputs.db-path,优于原计划的 env.OPENCODE_DB_PATH,规避了 composite action 编译期求值问题 ✅
  • 空字符串守卫:resolve-db-path.sh:27 [[ -z "$raw" ]] && raw="$default_path" + run-opencode.sh:158-166 空路径处理 ✅
  • 路径安全校验:resolve-db-path.sh:59-70 黑名单拒绝 17 个系统目录 ✅
  • 测试覆盖:TestCleanupDb 7 tests + TestMigrationRecovery 3 tests = 10 tests(超出 PR 声称的 8 个)✅

文档:3 个 README 均已更新对应功能说明 ✅

初始 review 中 Svitter 发现的空字符串导致清理静默跳过的阻塞问题已在 resolve-db-path.sh:27run-opencode.sh:158-166 中完整修复。run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但 run-opencode.sh:167 有默认路径兜底且 Issue #198 未要求修改该文件,不构成功能遗漏。实现范围整体超出 spec 描述(额外包含 resolve-db-path.sh 共享路径安全校验、db-path 输入、WAL/SHM/journal 附属文件清理、零阈值禁用保护等)。

New%20session%20-%202026-06-07T05%3A31%3A51.705Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

CAN MERGE / 可合并

所有四位 reviewer 均未发现阻塞性问题。PR 实现了 SQLite 数据库清理和迁移自动恢复两个特性,模块化设计合理,测试覆盖充分。

阻塞项:无

建议项:

  1. 禁止目录列表不完整(security 1, 2 + architecture 3):forbidden 数组缺少 /var(Linux 全局可写 /var/tmp)和 /(根路径),同时 /opt 可能对自托管 runner 过于严格。建议补充 /var/,并评估 /opt 是否允许特定子路径。

  2. 错误处理与路径校验不一致(quality 2 + security 3 + architecture 2):run-opencode.shresolve_db_path 失败时静默回退到 $HOME/.local/share/opencode/opencode.db,该回退路径未经过 resolve_db_path 校验,且 source ... 2>/dev/null 吞掉了调试信息。而 cleanup-db.sh 中失败则 exit 1。建议统一行为,并对回退路径也做路径校验。

  3. Python except Exception: pass(quality 1):resolve-db-path.sh:47-53 吞掉了所有异常,建议限制异常类型或输出调试信息到 stderr。

  4. 迁移恢复旁路重试机制(architecture 1):run-opencode.shcontinue 不递增 attempt,绕过了 OPENCODE_ATTEMPTS 的约定范围。建议将迁移恢复设计为独立的重试计数,或放在 while 循环外封装。

  5. 故障恢复策略过于激进(architecture 4):检测到 duplicate column name 即删除整个数据库。建议提供备份后删除或独立开关,当前 migration_recovery_done 布尔开关不够灵活。

  6. Python 子进程启动开销(performance 1):resolve_db_path() 每次调用启动一个 python3 子进程(~50-100ms),可考虑用纯 bash readlink -f 替代,或单次 python3 -c 调用完成全部解析。CI 场景影响有限,低优先级。

  7. TOCTOU 竞争条件(security 4,理论风险):stat 大小检查到 rm 删除之间文件可能被替换为符号链接,低可利用性,可考虑删除前重新 stat 确认 inode。


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

本次 PR 实现了两个清晰的特性:cleanup-db 数据库清理和自动迁移恢复,代码质量好,测试覆盖充分,未发现阻塞性缺陷。

阻塞项:无

建议项:

  1. resolve-db-path.sh:47-53 — Python 异常处理用 except Exception: pass 静默吞掉了所有异常(不仅是 python3 不可用),建议限制异常类型或至少输出调试信息到 stderr,避免未来排查困难
  2. run-opencode.sh:155-156 — 迁移恢复中 source ... 2>/dev/null && resolve_db_path 2>/dev/null 将错误完全静默化,可能导致 OPENCODE_DB_DEFAULT_PATH 未定义时静默回退到 $HOME/.local/share/opencode/opencode.db,行为正确但调试不易察觉
security

存在风险 / AT RISK

该 PR 新增了 SQLite 数据库文件删除功能(DB 超阈值清理 + 迁移失败自动恢复)。核心风险在于文件删除是破坏性操作,需要确保路径验证足够严谨。

安全分析摘要

  1. resolve-db-path.sh 对用户输入的 db-path 做了禁止目录列表校验(/etc, /usr, /bin, /sbin, /lib 等),防御设计合理。
  2. cleanup-db 输入(true/数字/false)在 shell 中通过正则 ^[0-9]+$ 校验,不存在命令注入。
  3. Python 的 os.path.realpath 通过 sys.argv 传参,不存在代码注入。
  4. rm -f -- "$db_path" 使用 -- 参数分隔符,防止路径以 - 开头被误解释为选项。
  5. 所有 GitHub Actions workflow commands(::warning::, ::error::)输出内容是可控的,不存在注入。

阻塞项:无

未发现必须阻塞合入的严重安全漏洞。

建议项

  1. 禁止目录列表遗漏 /var
    resolve-db-path.sh:58-68forbidden 数组包含 /private/etc/private/tmp(macOS),但缺少 /var(Linux)。虽然常规 runner 用户对 /var/lib 无写权限,但 /var/tmp 是全局可写的。建议补充 /var 以防止用户误配置到 /var/tmp 或其他 /var 子目录。

  2. 根路径 / 未被禁止
    如果用户设置 db-path: /opencode.db,解析后会得到 /opencode.db,该路径不在禁止列表中。虽然普通用户无写权限,但作为纵深防御,建议将 / 也加入 forbidden(或检查 resolved 是否刚好等于 /)。

  3. run-opencode.sh 回退路径未经过 resolve_db_path 校验
    run-opencode.sh:161 中,当 resolve_db_path 失败且 OPENCODE_DB_PATH 未设置时,回退到 $HOME/.local/share/opencode/opencode.db。虽然 $HOME 是系统变量,理论上安全,但如果 $HOME 被上游异常设置为危险值(例如 /etc),该路径会绕过校验。建议回退路径也调用 resolve_db_path 做二次确认。

  4. TOCTOU 竞争条件(理论风险,低可利用性)
    从大小检查(stat)到实际删除(rm)之间存在时间窗口,文件可能被替换为指向其他位置的符号链接。在 GitHub Actions 单线程环境下利用难度极高,但可考虑在 rm 前再次 stat 确认 inode 一致。

performance

性能有疑虑 / CONCERNS

性能分析摘要

该 PR 新增了 SQLite 数据库清理和迁移自动恢复机制。整体设计合理,无严重性能回归,但有一个值得关注的低效实现。

阻塞项:无

建议项

  1. resolve-db-path.sh 调用 Python 子进程开销过大shared/resolve-db-path.sh:42-49

    resolve_db_path() 每次调用都启动一个 python3 子进程(约 50-100ms 启动开销)来执行 os.path.realpath。在 cleanup-db.sh 中这是必经路径(非错误路径)。鉴于该 action 运行在 Linux/MacOS 环境,可用纯 bash 替代:readlink -f(Linux)或 realpath(macOS 可通过 brew 获得,但 GitHub Actions 托管 runner 默认不安装 realpath)。更好的做法是用 python3 -c "import os; print(os.path.realpath('$resolved'))" 单次调用完成全部解析,避免同时使用 cd subshell + python3 两阶段。

    不过此开销对 CI 场景影响有限(整体 action 执行时间通常在数分钟级别),属于低优先级的优化建议。

  2. cleanup-db.shstat 兼容性分支shared/cleanup-db.sh:39

    stat -c%s -- "$db_path" 2>/dev/null || stat -f%z "$db_path" 2>/dev/null || echo -1
    

    stat 两次均失败时回退为 echo -1 并跳过清理。如果文件系统有短暂不可达等问题,行为正确(跳过清理)。无性能问题。

  3. run-opencode.sh 迁移恢复路径中的 Python 调用run-opencode/run-opencode.sh:148-153

    迁移恢复路径中再次调用 resolve_db_path() 会启动第二个 Python 子进程。但迁移错误本身是罕见的异常路径,此开销可以忽略。

  4. 并发安全:两个脚本(cleanup-db.sh 和迁移恢复逻辑)都在同一 CI job 中串行执行,无并发竞争问题。rm -f 是原子操作,删除后 opencode 新建 DB 的时机也是有序的。

architecture

架构有疑虑 / CONCERNS

本 PR 新增 SQLite 数据库清理和迁移故障自恢复功能,整体设计模块化程度较高,将路径解析抽象为独立库 (resolve-db-path.sh) 供两个调用方复用,且测试覆盖较全面。然而存在以下架构问题:

阻塞项:无

  • 所有修改均未引入严重的架构违规。模块放置合理(shared/ 共享目录),接口清晰,职责划分基本正确。

建议项:

  1. run-opencode.sh 中的恢复逻辑旁路了重试机制continue 不递增 attempt,使得一次 "free retry" 跳出了 OPENCODE_ATTEMPTS 的约定范围。建议将迁移恢复视为一个 wrapper 层,在 while 循环外封装,或将其设计为 OPENCODE_ATTEMPTS 之外的独立重试计数,避免与主重试语义耦合
  2. 路径解析错误处理不一致cleanup-db.shresolve_db_path 失败会 exit 1,而 run-opencode.sh 中失败则回退到硬编码默认路径 $HOME/.local/share/opencode/opencode.db。两个调用方行为不统一,建议统一为 "失败抛出错误" 或 "失败使用合理的 fallback"
  3. /opt 被列入禁止路径:某些自托管 runner 可能将数据库存放在 /opt/ 下(如 /opt/opencode/data/),阻止 /opt 可能过于严格。建议评估是否允许 /opt/<app>/ 这类子路径
  4. 故障恢复策略过于激进:一旦检测到 duplicate column name 即删除整个数据库。更谨慎的做法可以是备份后删除、或只删除损坏的部分、或允许用户通过输入配置禁用此行为(当前已有 cleanup-db 但 recovery 无独立开关)。migration_recovery_done 布尔开关不够灵活,建议将其与 action 输入的 cleanup-db 联动或提供独立开关

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from dc330ab to 33d0ff8 Compare June 7, 2026 05:45
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

现在我已获取所有信息。以下是我的分析:

无遗漏

对照 Issue #198 的逐项需求与当前代码完整验证:

方案 A(workflow 增加清理步骤)

  • 检测 DB 是否存在:shared/cleanup-db.sh:36 [[ ! -f "$db_path" ]]
  • 检查文件大小,超阈值删除:shared/cleanup-db.sh:40-53 GNU stat -c%s 优先 + 阈值比较 + rm -f
  • 或者检测版本变更删除:Issue 中"或者"表示可选子项,文件大小方案已完全满足 ✅
  • 阈值可配置:cleanup-db input(true/数字/false)+ OPENCODE_DB_MAX_SIZE_MBaction.yml:156-263)✅
  • install 与 run 之间新增 step:action.yml:251-263

方案 B(独立 cron workflow):Issue 明确推荐"方案 A + C 结合",方案 B 是给 latex-agent 仓库的独立方案,不在本 PR 范围内 ✅

方案 C(migration 失败自动恢复)

  • 检测 duplicate column namerun-opencode.sh:152 grep -qim1-m1 提前退出)✅
  • 自动删除 DB(含 -wal/-shm/-journal):第 178 行 rm -f --
  • 重试:第 181 行 continue
  • 最多一次:migration_recovery_done 标志位(第 133、152、179 行)限制 ✅

#200 修复项(PR 承诺):全部验证通过,并实际使用了 inputs.db-path 优于原计划 env.OPENCODE_DB_PATH,规避了 composite action 编译期求值问题 ✅

文档:3 个 README 均已更新 ✅

额外实现超出 specresolve-db-path.sh 共享路径安全校验(17 个系统目录黑名单)、空字符串守卫、WAL/SHM/journal 附属文件清理、零阈值禁用保护、db-path 输入等。

run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本第 166 行有默认路径兜底且 Issue #198 未要求修改该文件,不构成功能遗漏。所有 spec 需求均已完整实现。

New%20session%20-%202026-06-07T05%3A45%3A33.327Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

CONDITIONAL MERGE / 有条件合并

综合 4 位 reviewer 的意见,合并需要先修复以下阻塞项。安全 reviewer 报告了两项防御性安全风险(路径遍历、TOCTOU),质量 reviewer 报告了一项用户行为不一致问题。

交叉验证:目录黑名单不完整问题被 qualitysecurity 二位 reviewer 独立指出,标记为"已确认"。

阻塞项 / Blocking Issues

  1. cleanup-db 输入值为 "0" 时行为不一致 (quality)
    action.yml 中 step 的 if 条件 ${{ inputs.cleanup-db != 'false' }} 会让 "0" 进入 step 但脚本内 max_mb=0 才视为禁用,导致日志中多出无意义的 step。建议将 if 同步为 != 'false' && != '0'

  2. Python3 不可用时路径遍历绕过目录黑名单 (security)
    resolve-db-path.sh 依赖 python3os.path.realpath,fallback 时使用未经归一化的原始路径。攻击者可构造 /nonexistent/../etc/passwd 绕过黑名单检查并删除系统文件。建议在 shell 层增加 .. 剥离作为防御纵深。

  3. TOCTOU 竞争条件(stat 与 rm 之间) (security)
    cleanup-db.shstat 检查文件大小后、rm 删除前,文件可能被替换为指向敏感位置的符号链接。自托管 runner 上存在理论风险。建议在 rm 前重新做路径黑名单验证。

建议项 / Suggestions

  1. 目录黑名单不完整/tmp (quality)、/home/Users (security) 未列入禁止前缀列表。(已确认:quality + security)
  2. 迁移恢复错误信息被静默 (quality) — resolve_db_path 2>/dev/null 隐藏了 resolve 失败的细节,建议 debug 模式下输出。
  3. 测试默认值隐式依赖 (quality) — test_default_threshold_is_50 未显式设 OPENCODE_DB_MAX_SIZE_MB,修改默认值后测试会静默失去覆盖率。
  4. macOS /private/var 路径豁免需文档化 (security) — 建议在 README 中说明自托管 macOS runner 上 /var 会被允许。
  5. rm -f 前使用 realpath/readlink -f 二次归一化 (security)
  6. Python3 子进程开销 (performance) — 建议优先使用 readlink -f,仅不兼容时 fallback 到 python3。
  7. grep 全量读取日志文件 (performance) — 建议仅读取日志尾部或设置大小限制。
  8. 迁移恢复 / 路径解析逻辑可简化 (performance + architecture) — resolve_db_path 在失败时被重复调用,且多层 fallback 过于复杂。建议让脚本自身处理所有边缘情况,外层只调一次。
  9. stat 跨平台命令可统一 (performance) — 可考虑用 wc -cls -l + cut 简化检测逻辑。

📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

这个 PR 功能设计合理,代码质量较高,自动化测试覆盖完整。但有一个 阻塞项 需要修复。


阻塞项

  1. cleanup-db input 值为 "0" 时,step 的 if 条件与脚本行为不一致
    action.yml 中 step 的 if: ${{ inputs.cleanup-db != 'false' }}cleanup-db="0" 时依然会执行该 step,进入 cleanup-db.shmax_mb=0 才视为 disabled。这导致用户设置 "0" 时 step 会空跑一次(打印 "disabled" 后退出)。虽然不影响正确性,但造成 CI 日志中多一个无意义的 step,且与"0 表示禁用"的用户预期不符。
    建议修复:将 if 条件改为 ${{ inputs.cleanup-db != 'false' && inputs.cleanup-db != '0' }},或者在 action.yml 中约束 input 的格式并更新文档。

建议项

  1. resolve-db-path.sh 缺遗漏 /tmp 系统目录的安全校验
    /private/tmp 已在 forbidden 列表中,但 /tmp 本身(Linux 常见路径)未被拦截。虽非安全漏洞(action input 由 workflow 编写者控制),但建议统一补上 /tmp 以增强一致性。

  2. run-opencode.sh migration recovery 中错误信息被完全静默
    第 156 行 source "$_resolve_script" 2>/dev/nullresolve_db_path 2>/dev/null 将 resolve 过程中的错误完全隐藏。如果 resolve 因权限等问题失败,排查困难。建议在 debug 模式下输出详细的错误信息。

  3. test_default_threshold_is_50 测试的默认值依赖存在隐式假设
    测试中未设置 OPENCODE_DB_MAX_SIZE_MB,依赖 cleanup-db.sh 内硬编码的默认值 50。如果未来修改了默认值,该测试会因阈值变化而静默通过但失去覆盖率,建议在测试中显式传入期望值(如 OPENCODE_DB_MAX_SIZE_MB="50"),同时保留一个不传值的测试验证默认值正确性。

security

存在风险 / AT RISK

安全分析摘要

此 PR 新增了 SQLite 数据库清理和迁移失败自动恢复功能。整体安全设计合理,包含了路径验证、输入检查和格式字符串保护。主要风险来自极端条件下(python3 不可用)的路径遍历绕过可能。

阻塞项

  1. python3 不可用时路径遍历可绕过目录黑名单检查
    resolve-db-path.sh 通过 python3 -c "import os.path; ... os.path.realpath()" 解析规范化路径。若 runner 上没有 python3,代码会 fallback 使用未解析的原始路径,不执行 .. 归一化。此时如果 db-path 输入为 /nonexistent/../etc/passwd 这类值,黑名单检查 /nonexistent/... 不命中任何禁止前缀,后续 rm -f 会跟随 .. 到达 /etc/passwd 并删除系统文件。建议在 shell 层增加对 .. 组件的剥离作为 defense-in-depth。

  2. TOCTOU 竞争条件(stat 与 rm 之间)
    cleanup-db.sh 中先 stat 检查文件大小再 rm 删除,期间文件可被替换为指向敏感位置的符号链接。虽然在 GitHub-hosted runner 上实际风险极低,但在自托管 runner 上理论上存在。建议在 rm 前重新做一次路径黑名单验证。

建议项

  1. macOS /private/var 路径豁免应为文档化的已知行为,建议在 README 中明确提示自托管 macOS runner 上 /var 路径会被允许。

  2. 建议在 rm -f 命令中使用 realpath / readlink -f 对最终路径再做一次归一化,确保删除时使用的是规范路径。

  3. resolve-db-path.sh 禁止目录列表不够全面,/home/Users 等用户目录未列入。虽然默认路径就在 $HOME 下,但建议增加对 /home/Users 等顶层目录的警告性检查(至少作为 warning 输出)。

  4. 建议在 cleanup-db.sh 中对 rm -fdb_path 参数使用引号包裹以避免 glob 展开(当前实现已有 -- 和双引号,但作为防御一致性的提醒)。

performance

性能有疑虑 / CONCERNS

本次 PR 新增了 SQLite 数据库的清理和迁移恢复逻辑,功能合理,整体性能影响可控。有以下建议项:

阻塞项:无

建议项:

  1. Python3 子进程开销 (shared/resolve-db-path.sh:49-55):每次调用 resolve_db_path 都会 fork+exec python3os.path.realpath,这是一个较重的操作(Python 解释器初始化约 10-30ms)。建议优先使用 readlink -f(Linux)或 shell 参数展开,仅在不兼容时 fallback 到 python3。

  2. grep 全量读取日志文件 (run-opencode/run-opencode.sh:149):grep -qim1 在无匹配时需读完整日志文件。如果 opencode 输出很大(如多轮对话的详细日志),可能造成不必要 I/O。建议对 log_file 设置大小限制或仅在日志较小时进行 grep 匹配,或者先读取最后 N KB。

  3. 路径解析重复执行 (run-opencode/run-opencode.sh:151-161):当首次 resolve_db_path 失败且 OPENCODE_DB_PATH 非空时跳过恢复;但当其为空时,会二次调用 resolve_db_path。建议在首次解析失败后缓存结果或合并逻辑,避免重复执行 fork/python3。

  4. 清理脚本的 stat 跨平台冗余 (shared/cleanup-db.sh:36):stat -c%sstat -f%z 两条命令依次尝试,失败两次才退出。前者一定在 Linux 上成功、后者一定在 macOS 上成功,不会同时执行两条,实际无额外开销,但可考虑用 wc -cls -l + cut 统一处理以减少命令复杂度。

architecture

架构合理 / SOUND


该 PR 整体架构正确,遵循了现有的代码组织模式:共享脚本放在 shared/ 下,由各 action 按需引用。数据库清理和迁移错误恢复这两个功能放在各自职责边界内(github-run-opencode 负责运行前的清理,run-opencode 负责运行中的错误恢复),职责划分清晰。

阻塞项:无

建议项:

  1. run-opencode.sh 中迁移恢复的 fallback 逻辑(当 resolve-db-path.sh 加载失败时重新 source 并清空 OPENCODE_DB_PATH)过于复杂且防御过度。建议让 resolve-db-path.sh 自身处理所有边缘情况(包括 OPENCODE_DB_PATH 为空时的默认值),然后在 run-opencode.sh 中直接调用 resolve_db_path 一次即可,无需多层 fallback。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 33d0ff8 to b350b18 Compare June 7, 2026 05:50
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 PR body 中的 spec 逐项验证,所有需求均已完整实现,无功能遗漏。

方案 A(预防性清理)

  • shared/cleanup-db.sh 新建脚本:文件存在检测(line 36)、GNU stat -c%s 优先(line 40)、可配置阈值(默认 50MB)、超阈值删除(含 -wal/-shm/-journal)✅
  • github-run-opencode/action.yml 新增 cleanup-db input(line 156-163,支持 "true"/数字/"false")和在 install 与 run 之间的 cleanup step(line 251-263)✅

方案 C(迁移失败自动恢复)

  • run-opencode/run-opencode.sh:147-182grep -qim1 检测 duplicate column name 错误、删除数据库、migration_recovery_done 标志位限制仅重试一次 ✅

#200 修复项

  • dead-code false 检查移除(step if: 守卫 action.yml:251)✅
  • GNU stat -c%s 优先(cleanup-db.sh:40)✅
  • OPENCODE_DB_PATH 接线至 cleanup(action.yml:255)和 run(action.yml:292)两步骤,实际使用 ${{ inputs.db-path }} 优于原计划的 ${{ env.OPENCODE_DB_PATH }},规避了 composite action 编译期求值问题 ✅
  • 空字符串守卫:resolve-db-path.sh:27-28 + run-opencode.sh:158-175
  • 路径安全校验:resolve-db-path.sh:63-82 黑名单拒绝 17 个系统目录 ✅
  • 测试覆盖:TestCleanupDb 8 tests + TestMigrationRecovery 3 tests = 11 tests(超出 PR 声称的 8 个)✅

文档github-run-opencode/README.mdrun-opencode/README.mdshared/README.md 均已更新 ✅

初始 review 中发现的关键问题(空字符串导致 clean-up 静默跳过)已在 resolve-db-path.sh:27-28run-opencode.sh:158-175 中修复。实现范围整体超出 spec 描述(额外包含共享路径安全校验、db-path 输入、WAL/SHM/journal 附属文件清理、零阈值保护、17 个系统目录黑名单、路径遍历防御等)。

New%20session%20-%202026-06-07T05%3A51%3A00.417Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策 / Final Decision:可合并 / CAN MERGE

本次 PR 设计合理,测试覆盖良好,无阻塞性安全漏洞。跨 reviewer 交叉验证确认了 2 处需优先处理的问题。


阻塞项 / Blocking Issues:无


已确认问题(2+ reviewer 一致同意)

  1. forbidden 前缀列表中的 / 导致所有绝对路径被拒绝 — security + quality 一致指出:resolve-db-path.shforbidden 数组首项为 /,而 /* 模式匹配所有绝对路径,导致 $HOME/.local/share/opencode/opencode.db 等合法路径也被拦截,清理与迁移恢复功能对绝对路径静默失效。测试用 tempfile.mkdtemp()(默认在 /tmp)也会因此失败。建议移除 / 条目或改用更精确的前缀匹配。

  2. TOCTOU 防御仅重算环境变量,未检查磁盘文件 — quality + security 一致指出:cleanup-db.sh 在 rm 前再次调用 resolve_db_path,但输入的环境变量未变,结果必然相同,实际无防御效果。真正的风险是文件在 race window 内被替换为指向敏感文件的符号链接,需要通过 readlinkstat 对比 inode 来防御。


建议项 / Suggestions

  1. Fallback 路径处理无法正确处理空格(quality) — resolve-db-path.shpython3 不可用时的 shell fallback 使用 IFS='/' 分割后用空格 join 再替换,"my data/opencode.db" 会被错误处理。建议用 local IFS=/; resolved="/${stack[*]}" 直接以 / 拼接。

  2. run-opencode.sh 迁移恢复逻辑内嵌过深(architecture) — 迁移检测(grep "duplicate column name")、路径解析、SQLite 附属文件处理全部内嵌在 run-opencode.sh 的重试循环中。建议提取到独立脚本(如 shared/migration-recover.sh),保持主流程简洁,同时一并封装 -wal-shm-journal 等硬编码后缀。

  3. 模块放置破坏了 shared/ 目录约定(architecture) — shared/ 原为存放 prompt 文件,新增 cleanup-db.shresolve-db-path.sh 两个可执行脚本改变了该目录的角色。建议在 shared/ 下建 scripts/ 子目录或另建 scripts/ 目录。

  4. resolve-db-path.sh 每次调用均启动 python3 子进程(performance) — 每次增加 ~50-200ms 开销,且在重试分支可能被反复调用。建议在 composite step 中一次解析,通过环境变量传递给下游脚本。

  5. run-opencode.shgrep 在全量日志上重复执行(performance) — 若 OPENCODE_RETRY_ON_REGEX 触发多次重试,每次失败都需扫描整个日志文件。建议匹配命中后缓存结果,避免重复读取。

  6. run-opencode.shresolve-db-path.sh 的耦合(architecture) — run-opencode.shresolve_db_path 失败时自行实现回退逻辑(清空 OPENCODE_DB_PATH 后重试),暴露了对内部默认值机制的了解。应由 resolve-db-path.sh 统一提供"路径无效时使用默认值"的能力。

  7. source "$_resolve_script" 2>/dev/null 静默吞错误(security) — 脚本不存在或 source 失败时无任何提示,增加调试难度。建议在没有 fallback 时至少输出一条 warning。

  8. action.yml 缺少对 db-path 输入的格式校验(security) — 输入直接传入环境变量,虽然脚本层有校验,但增加一层 action 层验证可提升防御纵深。


📋 各 Reviewer 详细审查结果
quality

可合并 / CAN MERGE

本次 PR 整体质量较高。新增的 cleanup-db 和 migration 自动恢复功能设计合理,路径安全校验和 TOCTOU 防御等细节处理到位。测试覆盖全面。

阻塞项:无

建议项:

  1. resolve-db-path.sh 中当 python3 不可用时的 fallback 路径处理,使用 IFS='/' 分割后再用空格 join 再替换的方式,无法正确处理路径中包含空格的情况(例 "my data/opencode.db" 会被错误地变成 "my/data/opencode.db")。建议用 local IFS=/; resolved="/${stack[*]}" 直接以 / 拼接数组元素。虽然 python3 在主流 runner 上基本都存在,但 fallback 分支应该同样可靠。

  2. resolve-db-path.sh 禁掉了 /tmp 目录,但测试 TestCleanupDbTestMigrationRecovery 均使用 tempfile.mkdtemp() 创建临时目录。若本地 Linux 环境未设置 TMPDIR(默认 /tmp),所有成功路径的测试会因路径被拦截而失败。建议测试使用 $HOME 下的临时目录,或在测试文档中注明需要设置 TMPDIR 到非 /tmp 路径。

  3. resolve-db-path.sh:52 中的 // /\/ 替换既是上面第 1 点的同一个问题,同时在意图表达上也较晦涩,建议替换为基于 IFS 的数组 join。

  4. cleanup-db.sh 在调用 resolve_db_path || exit 1 后再次调用 resolve_db_path(第 54 行的 TOCTOU 防御),但此时 OPENCODE_DB_PATH 环境变量未变化,所以第二遍 resolve 结果必然与第一遍一致,实际并无防御效果。真正应防的是文件被替换为指向系统文件的符号链接,这需要 readlinkstat 对比真实路径。建议更新注释说明真实限制,或移除冗余调用。

security

存在风险 / AT RISK

本 PR 新增了 SQLite 数据库自动清理和迁移失败恢复机制,整体安全设计较好(路径黑名单、TOCTOU 防守、输入校验),但存在以下需关注的问题:


阻塞项 / Blocking Issues

阻塞项:无
未发现必须阻塞合并的严重安全漏洞。没有命令注入、密钥泄露、鉴权绕过等 OWASP Top 10 高危问题。


建议项 / Suggestions

1. resolve-db-path.sh:68 禁止前缀列表中的 / 会导致所有绝对路径被拒绝

resolve_db_path 函数中 forbidden 数组的首个条目是 /[[ "$resolved" == "/*" ]] 在 bash [[ ]] 模式匹配中,* 匹配包括 / 在内的任意字符串,因此 /* 会匹配所有以 / 开头的绝对路径。这意味着:

  • 默认路径 $HOME/.local/share/opencode/opencode.db(绝对路径)会被拒绝
  • 所有通过 db-path 传入的绝对路径都会被拒绝(/tmp/xxx/home/runner/xxx 等均匹配 /*
  • 实际上唯一能通过验证的只有相对路径

安全影响:该问题让清理功能和迁移恢复对所有绝对路径静默失效(拒绝所有路径,而非拒绝危险路径),用户以为安全机制在生效,实际未运行。建议从 forbidden 列表中移除 / 条目,或改用更精确的前缀匹配方式。

2. cleanup-db.sh:46-52 TOCTOU 防守仅重新校验环境变量而非磁盘文件

resolve_db_path 的重新校验仅基于 OPENCODE_DB_PATH 环境变量重新计算路径,不检查当前路径在磁盘上指向的文件是否已被替换为指向敏感文件的符号链接。

Resolved path = $HOME/.local/share/opencode/opencode.db
  → stat($path) → 发现 100MB → resolve_db_path 再校验
  → 依然返回 $HOME/.local/share/opencode/opencode.db
  → rm $path  # 但 $path 可能已被替换为指向 /etc/shadow 的符号链接?

缓解因素rm -f -- 删除符号链接时只移除链接本身,不跟随到目标文件。但如果攻击者在 race window 内将目标替换为普通文件的硬链接或修改路径解析,仍存在理论风险。建议在 rm 前用 readlinkstat 检查当前路径是否为预期文件(如对比 inode)。

3. action.yml:266 run-opencode.sh 使用 OPENCODE_DB_PATH: ${{ inputs.db-path }} 但未验证输入格式

db-path 输入在 action.yml 层没有格式校验,直接传入环境变量。虽然 resolve-db-path.sh 有路径校验,但一旦脚本执行出错(如 python3 不可用且 shell fallback 有缺陷),可能导致未预期的路径被使用。建议在 action.yml 层对 db-path 做基本的非空和格式检查。

4. run-opencode.sh:151 source "$_resolve_script" 2>/dev/null 静默吞错误

resolve-db-path.sh 不存在或 source 失败时,错误被重定向到 /dev/null。后续的 resolve_db_path 2>/dev/null 也静默吞错误。虽然代码有 fallback 逻辑处理空 db_path,但调试困难。建议至少在没有 fallback 的情况下输出一条 warning。

performance

性能有疑虑 / CONCERNS

本次 PR 新增的 cleanup-db.shresolve-db-path.sh 以及 run-opencode.sh 的迁移恢复逻辑,整体性能开销可控。但对以下两点有轻微疑虑:

阻塞项:无

建议项:

  1. resolve-db-path.sh 每次调用均启动 python3 子进程python3 -c "import os,sys; os.path.realpath(...)" 每次调用增加 ~50-200ms 开销。在 cleanup-db.sh 中 TOCTOU 防御导致 resolve_db_path 被调用两次,run-opencode.sh 迁移恢复的 fallback 分支也可能重复调用。建议考虑在 github-run-opencode/action.yml 的 composite step 中预先用一次 Python 解析好路径,通过环境变量传递给后续脚本,避免每个子脚本各自重复启动 Python 进程。

  2. run-opencode.shgrep "duplicate column name" "$log_file" 在全量日志上运行:如果 opencode run 输出了大量日志(如 MB 级别),每次失败重试时 grep 都需要扫描整个文件(尽管 -m1 会在匹配时提前停止)。若设置了 OPENCODE_RETRY_ON_REGEX 且匹配重试,则该 grep 在每次失败尝试中都会执行一次,累积开销可能增长。虽然非迁移错误的第一次失败后就会退出(-z OPENCODE_RETRY_ON_REGEX 短路),但若自定义了 regex 且触发多次重试,建议在匹配命中后缓存结果,避免重复读取。

architecture

架构有疑虑 / CONCERNS

该 PR 新增 SQLite 数据库清理和迁移错误自动恢复功能,整体设计合理,但存在以下架构层面值得关注的问题:

  • 模块放置shared/ 目录原有 README 明确说明其为 "Plain-text prompt snippets",该目录的职责是存放共享提示词文件。新增 cleanup-db.shresolve-db-path.sh 两个可执行脚本改变了该目录的角色定位,降低了内聚性。建议在 shared/ 下建立 scripts/ 子目录来区隔,或另外创建 scripts/ 目录
  • 分层:迁移恢复逻辑(检测 duplicate column name、调用 resolve_db_path、删除 db 文件及附属文件、跳过 retry delay)全部内嵌在 run-opencode.sh 的重试循环中。SQLite 数据库生命周期管理与 opencode 运行逻辑交织在一起,提取到 shared/migration-recover.sh 脚本会更符合单一职责原则
  • 接口设计run-opencode.sh 中当 resolve_db_path 失败时,有一段显式的回退逻辑(清空 OPENCODE_DB_PATH 后重试调用),这暴露了 run-opencode.shresolve-db-path.sh 内部默认值机制的理解,属于不合理耦合。应让 resolve-db-path.sh 本身提供 "若路径无效则使用默认路径" 的能力,而不是由调用方自行实现

阻断项:无

建议项:

  1. cleanup-db.shresolve-db-path.sh 移至 shared/scripts/ 下,或创建新的 scripts/ 目录,维护 shared/ 仅存放 prompt 文件的原始约定
  2. run-opencode.sh 中的迁移恢复逻辑提取到 shared/migration-recover.sh 脚本中,保持 run-opencode.sh 的主流程简洁
  3. run-opencode.sh 中当 OPENCODE_DB_PATH 为空时重新调用 resolve_db_path 的回退逻辑合并到 resolve-db-path.sh 内部,由 resolve_db_path 统一处理默认值回退
  4. run-opencode.sh 中硬编码了 -wal-shm-journal 等 SQLite 文件后缀,这些细节也应跟随恢复逻辑一并封装进独立脚本

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from b350b18 to 7a54808 Compare June 7, 2026 05:56
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

无遗漏

对照 PR body 中的 spec 逐项验证,所有需求均已完整实现。

方案 A(预防性清理)

  • shared/cleanup-db.sh:42 GNU stat -c%s 优先 + BSD stat -f%z 回退获取大小 ✅
  • shared/cleanup-db.sh:24 默认阈值 50MB ✅
  • shared/cleanup-db.sh:55-63 超阈值删除(含 -wal/-shm/-journal)✅
  • github-run-opencode/action.yml:156-163 cleanup-db input 支持 "true"/数字/"false"
  • github-run-opencode/action.yml:251-263 在 install 与 run 之间新增 cleanup step ✅

方案 C(迁移自动恢复)

  • run-opencode/run-opencode.sh:152 grep -qim1 "duplicate column name" 检测(-m1 提前退出)✅
  • run-opencode/run-opencode.sh:178 rm -f -- 删库(含附属文件)✅
  • run-opencode/run-opencode.sh:179 migration_recovery_done=true 限制仅重试一次 ✅
  • run-opencode/run-opencode.sh:150 评论说明不递增 $attempt、跳过延迟,正确实现 ✅

#200 修复项

  • dead-code 移除:step if: 守卫 action.yml:251
  • GNU stat 优先:cleanup-db.sh:42
  • OPENCODE_DB_PATH 接线至 cleanup(action.yml:255)和 run(action.yml:292)两步骤,实际使用 ${{ inputs.db-path }} 优于原计划 ${{ env.OPENCODE_DB_PATH }},规避了 composite action 编译期求值问题 ✅
  • 空字符串守卫:resolve-db-path.sh:27-28 [[ -z "$raw" ]] && raw="$default_path" + run-opencode.sh:158-175 完整空路径处理 ✅
  • 测试覆盖:TestCleanupDb 8 tests + TestMigrationRecovery 3 tests = 11 tests(超出 PR 声称的 8 个)✅

文档

  • github-run-opencode/README.md:19-20,50-51 更新 cleanup-dbdb-path 输入 ✅
  • run-opencode/README.md:27 添加 migration recovery 描述 ✅
  • shared/README.md:13-23 新增 cleanup-db.shresolve-db-path.sh 文档 ✅

额外实现超出 specresolve-db-path.sh 共享路径安全校验组件(17 个系统目录黑名单、.. 遍历防御、TOCTOU inode 检查)、db-path 输入、WAL/SHM/journal 附属文件清理、零阈值禁用保护等。

Svitter 在初始 review 中指出的空字符串导致清理静默跳过的阻塞问题已在 resolve-db-path.sh:27-28run-opencode.sh:158-175 中完整修复。run-opencode/action.yml 未传递 OPENCODE_DB_PATH,但脚本第 167 行有默认路径兜底且 Issue #198 未要求修改该文件,不构成功能遗漏。

New%20session%20-%202026-06-07T05%3A56%3A40.325Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

本次 PR 整体方向合理,但存在 1 个阻塞性 bug(unset 单引号导致变量无法展开)。修复后即可合并。


阻塞项 / Blocking Issues

  1. unset 使用单引号阻止变量展开(quality — 阻塞)
    shared/resolve-db-path.shunset 'stack[${#stack[@]}-1]' 因单引号导致 ${#stack[@]} 不被展开,unset 收到的是字面字符串而非索引。当 python3 不可用且路径含 .. 时,路径解析会出错。应改为 unset "stack[${#stack[@]}-1]"unset 'stack[-1]'(bash 4.3+)。

建议项 / Suggestions

  1. cleanup-db 输入处理(大小写 + 多态)(quality + architecture — 已确认
    两方 reviewer 均指出当前输入值比较未做大小写归一化("False"/"FALSE" 会被误判),且同一输入同时接受 bool 和数字两种语义容易出错。建议拆为 cleanup-db-enabled(bool)和 cleanup-db-threshold(number),或至少用 ${INPUT_CLEANUP_DB,,} 做大小写归一化。

  2. cleanup-db 输入缺少非法值校验(quality)
    传入 "yes" 等无法识别的值时会被当作 true 使用默认阈值,应提前报错。

  3. macOS 上 /var 路径绕过(security)
    resolve-db-path.sh/private/var 放行以兼容 macOS CI,但 macOS 上 readlink("/var/...") 解析为 /private/var/...,使 /var/db/ 等敏感位置的数据库可被删除。建议细化 macOS 下敏感路径拦截或增加文件属主验证。

  4. Python3 不可用时 fallback 路径的 TOCTOU 风险(security)
    Shell 级 .. 剥离与后续 cd/pwd 解析之间存在竞争窗口。GitHub 托管 runner 始终有 python3,此风险仅影响无 python3 的自托管环境。

  5. 关联文件(-wal/-shm/-journal)删除未验证类型(security)
    建议删除前用 file 命令校验这些附属文件是否为 SQLite 格式,或至少记录被删文件名。

  6. OPENCODE_DB_PATH 传播到 opencode 子进程(security)
    opencode CLI 有信息泄露漏洞,路径信息可能暴露。建议调用前解除该环境变量。

  7. 迁移恢复时全量日志扫描存在潜在 I/O(performance)
    opencode 失败前输出大量日志,grep -qim1 会扫描整个日志文件。建议在失败日志写入管道时即检测错误模式。

  8. resolve-db-path.sh 每次调用启动 python3 子进程(performance)
    调用频率低(最多 2 次/run),但可考虑用 readlink -frealpath 替代。

  9. stat 回退分支静默失败(performance)
    /usr/bin/stat 和 macOS 格式均不支持时,stat_line 为空导致跳过检查,建议输出 ::debug:: 日志。

  10. run-opencode.sh 不应感知数据库细节(architecture)
    迁移恢复逻辑使其耦合到 SQLite 内部实现(duplicate column name 识别、文件删除)。建议将恢复逻辑上提到 github-run-opencode/action.yml

  11. 路径解析逻辑在 run-opencode.sh 中重复(architecture)
    resolve_db_path 失败后,run-opencode.sh 自行实现了 fallback。应让 resolve-db-path.sh 自身覆盖好默认值 fallback。

  12. 迁移恢复使用 continue 不递增 $attempt(architecture)
    免费重试"打破原有 retry 契约一致性,易使维护者困惑。

  13. run-opencode.shshared/ 目录产生隐式依赖(architecture)
    此前可独立拷贝使用,现在必须保持完整仓库目录结构。

  14. action.yml 中设置的 OPENCODE_DB_PATH 环境变量未被 python 脚本使用(architecture)
    建议确认必要性或移除。


📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

汇总

此次 PR 的主要变更:新增 cleanup-dbdb-path 输入、shared/cleanup-db.sh 数据库清理脚本、shared/resolve-db-path.sh 路径解析库,以及在 run-opencode.sh 中增加 SQLite 迁移失败自动恢复。整体设计合理,测试覆盖较完整。


阻塞项

  1. shared/resolve-db-path.shunset 使用单引号导致变量无法展开

    unset 'stack[${#stack[@]}-1]'

    单引号阻止了 ${#stack[@]} 的参数展开,传递到 unset 的是字面字符串。在算术求值上下文中 ${#stack[@]} 不是合法语法,该 unset 不会正确删除数组末尾元素。这意味着当 python3 不可用(走 fallback 路径)且路径包含 .. 时,路径解析结果会出错。

    应改为:

    unset "stack[${#stack[@]}-1]"

    或使用更简洁的 bash 4.3+ 语法:

    unset 'stack[-1]'

建议项

  1. cleanup-db 输入值的比较区分大小写
    action.yml 中的条件判断 inputs.cleanup-db != 'false' 只精确匹配小写 "false"。如果用户传入 "FALSE""False",会误认为启用。建议用 ${INPUT_CLEANUP_DB,,} 做大小写归一化。

  2. cleanup-db 输入值缺少校验
    当用户传入既不是 "true"/"false" 也不是纯数字的值时(如 "yes"),会被当作 "true" 处理并使用 50MB 默认阈值。可以在 action.yml 的 step 中做一层校验,或者由 cleanup-db.sh 脚本在无法识别时提前报错。

  3. run-opencode.shmigration_recovery_done 风格
    当前使用字符串比较 == "false"== "true",这在 bash 中有效。更常见的习惯用法是直接用 ! $migration_recovery_done 判断 false、$migration_recovery_done 判断 true,避免拼写敏感。这是纯风格建议,非必须。

  4. resolve-db-path.sh 回退逻辑中数组操作可用 unset 'stack[-1]' 替代
    见阻塞项所述。除了修正引号问题,也可考虑直接使用负索引(bash 4.3+ 支持),代码更简洁。

security

存在风险 / AT RISK

本次 PR 引入了数据库清理和迁移恢复功能,整体安全设计较为周全(路径黑名单检查、TOCTOU 防护、-- 参数分隔符等),但仍存在以下问题:

阻塞项:无


建议项:

  1. macOS 上 /var 目录路径绕过resolve-db-path.sh:68-73 明确将 /private/var 放行(为兼容 macOS CI 临时目录),但这也意味着 macOS runner 上 /var/db/ 等系统位置的数据库文件可能被删除。虽然 /var 本身在黑名单中,但 macOS 上 os.path.realpath("/var/...") 会解析为 /private/var/...,绕过检查。建议对 macOS 也细化 /var 下敏感路径(如 /var/db)的拦截,或通过文件属主验证增强安全性。

  2. 路径解析 fallback 的 TOCTOU(当 python3 不可用时)resolve-db-path.sh:44-55 中 shell 级的 .. 剥离不解析符号链接,后续虽然通过 cd/pwd 尝试解析,但 cd "$dir"dirname 之间存在竞争窗口——恶意程序可能将目录替换为符号链接。GitHub Actions 托管 runner 上 python3 始终可用,此风险仅在自托管且无 python3 的环境中出现。建议在 fallback 路径中增加 inode 校验或明确要求 python3 可用。

  3. 关联文件删除范围未验证cleanup-db.sh:63run-opencode.sh:167rm -f -- "$db_path" "$db_path-wal" "$db_path-shm" "$db_path-journal" 会删除 ${db_path}-wal 等推测的 SQLite 附属文件,但不验证这些文件的实际用途。如果 db_path 被设置为一个合法文件的路径,其同目录下的 -wal 等模式匹配的文件会被连带删除。建议在删除前检查这些关联文件是否为 SQLite WAL/SHM 格式(通过 file 命令),或至少记录被删除的文件名。

  4. OPENCODE_DB_PATH 环境变量传播到子进程run-opencode.sh 通过环境变量 OPENCODE_DB_PATH 接收用户输入,该环境变量在 opencode 子进程中可访问。如果 opencode CLI 本身存在信息泄露漏洞(如错误日志中打印环境变量),可能暴露路径信息。建议在调用 opencode 前解除该环境变量,或仅将其用于内部脚本而非传递给 CLI 子进程。

performance

性能良好 / GOOD

性能分析总结

该 PR 新增了 SQLite 数据库清理和迁移失败自动恢复功能,整体实现轻量高效。Shell 脚本在关键路径上只有 O(1) 的 stat 系统调用,没有循环扫描、没有网络请求、没有数据库查询。TOCTOU 防御(inode 检查)开销极低。迁移恢复逻辑仅在特定错误模式(duplicate column name)下触发,且只执行一次免费重试,不影响正常路径。

阻塞项

建议项

  1. run-opencode.sh 的迁移恢复分支中,grep -qim1 会在每次失败时扫描整个日志文件。如果 opencode 在失败前输出大量内容(如处理大型仓库时产生 MB 级日志),此扫描会增加不必要的 I/O。建议在失败日志写入管道时即检测错误模式,避免事后全量扫描。

  2. resolve-db-path.sh 每次调用都启动 python3 子进程来解析路径。虽然调用频率很低(每个 action 运行最多 2 次),但可考虑用纯 Bash 的 readlink -f 替代以消除子进程开销,前提是确认 runner 环境支持(macOS 需 brew install coreutils 或使用 realpath)。

  3. cleanup-db.sh 中的 stat -c '%s %i' 在 GNU/Linux 和 BSD/macOS 上有不同参数语法,已通过 || 回退处理。但回退分支的 stat -f '%z %i' 在环境不支持时静默失败(|| true),此时 stat_line 为空导致跳过检查。这不是性能问题,但建议在 stat 完全不可用时输出 ::debug:: 日志便于排查。不影响性能评分。

architecture

架构有疑虑 / CONCERNS

本次 PR 在 github-run-opencoderun-opencode 中增加了 SQLite 数据库清理和迁移失败自动恢复能力,整体方向合理,但存在以下架构问题。

架构分析摘要

  • resolve-db-path.sh 放在 shared/ 中由两方 source 引用,模式合理,避免了路径解析逻辑的重复
  • cleanup-db.sh 作为独立脚本由 action.yml 调用,关注点清晰
  • 但是迁移恢复逻辑直接嵌入 run-opencode.sh 的 retry 循环中,导致 run-opencode 这个原本简单的 wrapper 脚本混入了数据库维护职责
  • DB 维护的职责被拆到两个动作中:github-run-opencode 做预检清理,run-opencode 做事后恢复,同一数据库文件的生命周期管理分散

阻塞项

阻塞项:无

建议项

  1. run-opencode.sh 不应感知数据库细节run-opencode.sh 的核心职责是运行 opencode CLI 并重试。迁移恢复逻辑使其耦合到了 SQLite 数据库的内部实现(duplicate column name 错误识别、数据库文件删除)。建议将迁移恢复逻辑移到调用层(如 github-run-opencode/action.yml),保持 run-opencode.sh 纯净。

  2. run-opencode.sh 中路径解析逻辑重复:当 resolve_db_path 失败时,run-opencode.sh 自己又实现了 fallback(重新 source resolve 脚本、重置 OPENCODE_DB_PATH 为空再试)。这复制了 resolve-db-path.sh 的部分职责。应考虑让 resolve_db_path 自身就覆盖好默认值 fallback 场景,调用者无需再处理。

  3. 迁移恢复 using continue 不增加 $attempt:现有 retry 循环通过 $((attempt + 1)) 递增计数,而迁移恢复使用 continue 跳过递增。虽然注释说明了是有意设计,但这种"免费重试"打破了原有 retry 模式的契约一致性,容易使后续维护者困惑。

  4. cleanup-db 输入类型多态:输入同时接受 "true"、数字字符串和 "false"/"0"。Action 输入本质是字符串,条件判断 inputs.cleanup-db != 'false' && inputs.cleanup-db != '0' 是 fragile 的模式。如果用户传 "False"(大写 F)也会穿透。建议拆为 cleanup-db-enabled(bool)和 cleanup-db-threshold(number)两个输入。

  5. run-opencode.shshared/ 目录结构产生隐式依赖:迁移恢复通过 "$(dirname "$0")/../shared/resolve-db-path.sh" 引用上层目录中的文件。此前 run-opencode.sh 可独立拷贝使用,现在则必须保持完整仓库目录结构。

  6. action.yml 中为 python step 设置了 OPENCODE_DB_PATH 环境变量,但本次 diff 中并未看到 python 脚本消费该变量。添加未被使用的环境变量会增加维护负担,应确认是否必要或移除。

@Svtter Svtter force-pushed the feat/cleanup-opencode-db-v2 branch from 7a54808 to 5425124 Compare June 7, 2026 06:02
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

分析完毕。所有 spec 需求均已完整实现,无功能遗漏。

New%20session%20-%202026-06-07T06%3A03%3A00.104Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

最终决策:有条件合并 / CONDITIONAL MERGE

综合 4 位 reviewer 意见,无安全阻塞项,但 quality reviewer 发现一处明确的功能正确性问题需修复。

阻塞项:

  1. run-opencode.sh 迁移恢复中 continue 不递增 attempt,突破 OPENCODE_ATTEMPTS 上限。用户设置 attempts=1 时实际执行 2 次。需在 continue 前递增 attempt 或在 README 中显式说明。[来源:quality,未交叉确认但属明确 bug]

建议项:

  1. resolve-db-path.shfor prefix in "${forbidden[@]}"prefix 未声明 local,泄漏到全局作用域 [quality]
  2. test_no_db_file_is_nooptempfile.mkdtemp 回退到 /tmp 时会被禁止路径列表拒绝,建议加固 [quality]
  3. cleanup-db.shsize_mb 整数除法向下取整,threshold 语义≈"大于等于",建议文档对齐 [quality]
  4. run-opencode.sh 迁移恢复路径缺少 TOCTOU inode 校验(cleanup-db.sh 中有类似防御但此处缺失)[security]
  5. resolve-db-path.sh shell fallback 路径解析(python3 不可用时)存在 .. 残留边界情况 [security]
  6. run-opencode.sh 迁移恢复逻辑块较长(约 40 行),建议抽取为独立函数或独立脚本 [architecture]
  7. cleanup-db 输入取值语义较复杂,建议在 action.ymldescription 中增加更清晰的示例说明 [architecture]

📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

优质实现,路径安全校验和 TOCTOU 防御都很到位。但有一个问题需要修复:

阻塞项:

  1. run-opencode.sh 迁移恢复逻辑中 continue 不递增 attempt 会突破用户设置的 OPENCODE_ATTEMPTS 上限
    • OPENCODE_ATTEMPTS=1 时,首次失败触发恢复,continueattempt 仍为 1,实际执行了 2 次 opencode。注释称这是 "free bonus",但用户明确设 attempts=1 时预期只有 1 次尝试。建议在 continue 前递增 attempt,或至少在 README 中显式说明此行为。当前 README 未提及该例外。

建议项:

  1. resolve-db-path.shfor prefix in "${forbidden[@]}"prefix 未声明 local,会泄漏到全局作用域
  2. test_no_db_file_is_nooptempfile.mkdtemp(dir=...) 回退到 /tmp 时会被 resolve_db_path 的禁止路径列表拒绝。虽然 CI 环境下 $HOME 通常有值,但值得加固。
  3. cleanup-db.shsize_mb 使用整数除法向下取整,导致实际触发删除的文件略大于 threshold(如 50MB + 1byte 文件显示为 50MB 仍会被删除)。不影响正确性,但 threshold 语义≈ "大于等于 50MB",建议文档对齐。
security

存在风险 / AT RISK

安全分析摘要

此 PR 新增了 SQLite 数据库清理和迁移恢复功能,整体安全设计良好,包含多层路径验证和 TOCTOU 防护。但在路径解析的 fallback 分支和迁移恢复的 rm 操作上存在低风险的潜在问题。以下是对各维度的分析:

  • 输入验证与清理cleanup-dbdb-path 输入通过正则和白名单方式验证,resolve-db-path.sh 使用 os.path.realpath() 规范化路径并拒绝系统目录,防御较好。
  • 注入漏洞:环境变量传递方式安全;rm -f -- "$db_path" 使用 -- 防止选项注入;grep 正则部分并非本 PR 引入。
  • 敏感数据暴露:日志可能泄露 db_path(非敏感信息),删除操作使用 rm -f 而非安全擦除,但在 CI 环境可接受。
  • 身份认证/授权:无相关代码变更。
  • 不安全的依赖:无新依赖引入。
  • OWASP 合规:路径遍历防护(A03)、系统目录黑名单(A01)均有实现。

阻塞项:无

未发现必须阻塞合并的高危漏洞。

建议项

  1. run-opencode.sh 迁移恢复缺少 TOCTOU inode 校验cleanup-db.shstatrm 之间进行了 inode 一致性检查,但在 run-opencode.sh 的迁移恢复路径中,resolve_db_path 验证通过后直接 rm -f,缺少类似的 TOCTOU 防护。虽然 rm 删除符号链接时不跟随目标(风险有限),但建议保持两处防御一致,增加 inode 重新校验。

  2. shell fallback 路径解析存在 .. 残留的边界情况resolve-db-path.sh:54-66 中,当 python3 不可用时的 fallback 路径解析,对于以 .. 开头的相对路径(如 ../../../etc/passwd),手动 .. 剥离无法正确处理(栈为空时 .. 被跳过),导致 resolved 中包含未解析的 .. 组件。虽然此分支仅在 python3 不可用时才触发(且 cd 后续修正可覆盖多数情况),但建议在此分支中增加对残留 .. 的二次处理或直接拒绝。

performance

性能良好 / GOOD

这是一个 CI action 的辅助脚本 PR,主要功能是清理过大的 SQLite 数据库文件,没有引入任何性能风险。

分析要点:

  • 算法复杂度:均为 O(1) 或 O(n)(n 为路径组件数)的单次操作,无循环/递归风险
  • 内存/资源:纯 shell 脚本,无内存泄漏;Python 子进程调用 os.path.realpath 虽有 ~50ms 开销,但每个 action run 仅调用一次,可忽略
  • 数据库效率:脚本管理的是数据库文件本身(删除/检查大小),不涉及 SQL 查询,无 N+1 或索引问题
  • 缓存机会:不适用(正是清理动作,不需要缓存)
  • 冗余计算resolve_db_path 在 Python 不可用时有 fallback 路径(shell 级 .. 展开 + cd 基路径解析),略有冗余但仅一次执行,无实质影响
  • 并发安全cleanup-db.sh 包含 TOCTOU 防御(inode 比对);CI 环境单进程运行,整体安全
  • I/O:一次 stat 调用 + 条件性 rm,开销极小

阻塞项:无

建议项:无

architecture

架构合理 / SOUND

该 PR 引入的变更在架构上总体合理。新增的 shared/cleanup-db.shshared/resolve-db-path.sh 放置在已有的 shared/ 目录下,延续了"多个 action 共享工具脚本"的模式。cleanup-db.sh(运行前主动清理)和 run-opencode.sh 中的迁移恢复(运行后被动恢复)职责分离清晰,依赖方向正确(action 依赖 shared 工具)。

阻塞项:无

建议项:

  1. run-opencode.sh 中迁移恢复的逻辑块较长(约 40 行 if 分支),内嵌在重试主循环中使循环结构变复杂。建议抽取为独立函数(如 _migration_recovery())或独立脚本放到 shared/ 下,保持主循环的可读性。
  2. cleanup-db 输入的取值语义有一定复杂度("true" 表示 50MB 默认阈值,纯数字字符串表示自定义阈值,"false" / "0" 禁用),属性类型与取值行为的映射关系依赖代码逻辑推导。GitHub Actions 的 input 类型限制是客观原因,但可考虑在 action.ymldescription 中增加更清晰的示例说明。

Closes #198

Prevents and recovers from stale opencode.db SQLite issues on self-hosted
runners where the database can grow to 160MB+ and cause "duplicate column
name" migration errors when opencode upgrades.

Scheme A (preventive cleanup):
- New shared/cleanup-db.sh checks opencode.db size before each run
- Deletes db if it exceeds configurable threshold (default 50MB)
- New github-run-opencode input "cleanup-db" (default: true)
- GNU stat tried first (action is Linux-only)

Scheme C (migration failure auto-recovery):
- run-opencode/run-opencode.sh detects "duplicate column name" errors
- Auto-deletes the stale db and retries once
- Recovery limited to single attempt to avoid infinite loops
- grep uses -m1 for early exit

Fixes from AI review feedback:
- Removed dead-code false check in cleanup step (already guarded by if:)
- Swapped stat order: GNU first, BSD fallback
- Wired OPENCODE_DB_PATH through action.yml to both cleanup and run steps
- Added test coverage: TestCleanupDb (5 tests) + TestMigrationRecovery (3 tests)
@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

基于 Issue #198 和 PR body 的完整分析,我已验证所有代码实现。以下是最终结论:

无遗漏

对照 Issue #198 的逐项需求,所有功能均已完整实现:

方案 A(预防性清理)

  • shared/cleanup-db.sh 实现文件存在检测(第 36 行)、GNU stat -c%s 优先获取大小(第 42 行)、可配置阈值(默认 50MB)、超阈值删除(含 -wal/-shm/-journal,第 63 行)✅
  • github-run-opencode/action.yml 新增 cleanup-db input(第 156-163 行,支持 "true"/数字/"false")和在 install 与 run 之间的 cleanup step(第 251-263 行)✅
  • 版本变更检测:Issue 中标注为"或者"的可选子项,文件大小方案已完全满足需求 ✅

方案 C(迁移失败自动恢复)

  • run-opencode/run-opencode.shgrep -qim1 "duplicate column name" 检测错误(第 152 行)、自动删库(第 184 行,含附属文件)、migration_recovery_done 标志位限制仅重试一次(第 133、152、185 行)✅

#200 修复项:全部验证通过。dead-code 移除、GNU stat 优先、OPENCODE_DB_PATH 接线至两步骤(实际使用 inputs.db-path,优于原计划的 env.OPENCODE_DB_PATH,规避了 composite action 编译期求值问题)、空字符串守卫(resolve-db-path.sh:27-28 + run-opencode.sh:164-181)、路径安全校验(17 个系统目录黑名单,resolve-db-path.sh:68-82)、测试覆盖(TestCleanupDb 8 tests + TestMigrationRecovery 4 tests = 12 tests,超出 PR 声称的 8 个)✅

文档:3 个 README 均已更新对应功能说明 ✅

额外实现超出 specresolve-db-path.sh 共享路径安全校验组件、TOCTOU inode 二次校验、db-path 输入、WAL/SHM/journal 附属文件清理、零阈值禁用保护、路径遍历防御等,均属于超出 spec 范围的质量增强。

New%20session%20-%202026-06-07T06%3A32%3A00.069Z
opencode session  |  github run

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

有条件合并 / CONDITIONAL MERGE

本次 PR 整体设计良好,测试覆盖全面,但存在 1 个严重安全漏洞2 个功能阻塞问题 需在合并前修复。

阻塞项 / Blocking Issues

  1. (已确认·安全)硬链接绕过路径验证 → 可在系统目录中删除任意 SQLite 文件
    resolve-db-path.sh 使用 os.path.realpath 解析符号链接,但硬链接共享同一 inode 可绕过校验。攻击者通过 db-path 传入硬链接路径,验证通过后 rm -f 会直接删除系统文件。
    修复:增加 stat -c '%h' 硬链接计数检测,或校验设备+inode 不在禁止范围内。

  2. (质量)迁移恢复消耗 attempt 计数
    attempts=1 时,迁移恢复将 attempt 递增到 2 后因超限直接退出,导致恢复功能完全失效。
    修复:恢复改为不消耗 attempt 计数,或在文档中明确说明此行为。

  3. (质量)cleanup-db.sh 缺少 source 失败保护
    source .../resolve-db-path.sh 失败时脚本继续执行导致 resolve_db_path: command not found
    修复:加 || exit 1

建议项 / Suggestions

  1. (质量)resolve-db-path.shlocal IFS='/' 应显式重置
    后续未加引号的扩展会受影响,建议 join 操作后恢复 IFS。

  2. (质量)大小阈值日志截断
    size_mb 使用整数除法,接近阈值时日志显示截断值而非实际大小。

  3. (质量)test_recovery_respects_attempts_limit 应加注释
    说明该测试固化了当前设计决策,未来调整需同步修改。

  4. (安全)run-opencode.shsource ... 2>/dev/null 抑制 stderr
    验证失败但返回码意外为 0 时用户无法看到错误原因。建议在 || 分支中打印 stderr。

  5. (安全)OPENCODE_RETRY_ON_REGEX 存在 ReDoS 风险(非本 PR 引入)
    建议后续增加超时保护或长度限制。

  6. (安全)默认路径依赖 $HOME
    建议 souring 时增加 export HOME="${HOME:-/root}"

  7. (安全)测试未覆盖硬链接场景
    建议在 TestCleanupDbTestMigrationRecovery 中增加对应测试。

  8. (架构)泄漏抽象:run-opencode.sh 含路径解析 fallback 代码
    建议让 resolve_db_path 在失败时自动回退到默认路径。

  9. (架构)TOCTOU 防御不一致
    cleanup-db.sh 删除前做 inode 校验,但 run-opencode.sh 迁移恢复的 rm -f 没有相同检查。建议封装为共享函数。

  10. (架构)迁移恢复逻辑过大(35+ 行 inline)
    建议将"路径解析+验证"和"安全删除 db 文件"抽取到 resolve-db-path.sh

  11. (架构)默认路径多源同步风险
    ~/.local/share/opencode/opencode.db 定义在脚本中,但也在 action.ymlREADME.md 中硬编码。建议通过文档自动生成或引用。


📋 各 Reviewer 详细审查结果
quality

有条件合并 / CONDITIONAL MERGE

本次 PR 整体质量较高,功能设计清晰,测试覆盖全面,文档与实现一致。代码风格统一,安全考量(路径校验、TOCTOU 防护)到位。

阻塞项

  1. run-opencode.sh 迁移恢复消耗 attempt 计数:当用户设置 attempts: 1 时,迁移恢复会将 attempt 递增到 2 后因超过上限直接退出,导致恢复逻辑删除了数据库但未真正重试。虽然代码注释说明这是有意设计,但实际效果是 attempts=1 时恢复功能完全失效。建议改为恢复不消耗 attempt 计数(即先恢复再执行,不计入限次),或者在文档中明确说明此行为。

  2. cleanup-db.sh 缺少 source 失败保护source .../resolve-db-path.sh 失败时(文件不存在或语法错误),脚本会继续执行导致 resolve_db_path: command not found。建议加 || exit 1

建议项

  1. resolve-db-path.shlocal IFS='/' 的脆弱性:该赋值在函数级作用域生效,后续所有未加引号的扩展均受影响。当前代码中所有扩展均已正确引号保护,但后续修改易引入微妙 bug。建议在 join 操作后显式重置 IFS。

  2. 大小阈值信息截断cleanup-db.shsize_mb 使用整数除法(size_bytes / 1024 / 1024),当文件大小接近阈值且非整数 MB 时,日志消息(如 "opencode.db is 1MB (threshold 1MB)")会显示截断后的值而非实际大小,可能引起混淆。

  3. test_recovery_respects_attempts_limit 测试语义:该测试验证了 attempts=1 时恢复仅运行一次 opencode 后退出,但这本身是把限制性行为固化为测试契约。如果未来修复第 1 条阻塞项,此测试需要同步修改,建议在测试中添加注释说明此为当前设计决策而非期望行为。

security

高危漏洞 / CRITICAL

安全分析总结

此 PR 涉及 SQLite 数据库自动清理与迁移故障恢复功能。代码总体安全设计良好,但存在一个严重的安全漏洞:路径验证(forbidden list)可被硬链接(hard link)绕过,导致攻击者可在受保护的系统目录中删除任意文件。


阻塞项

  1. 硬链接绕过路径验证 → 可在系统目录中删除任意 SQLite 文件

    resolve-db-path.sh 的 forbidden list 依赖 os.path.realpath 解析符号链接来阻止对 /etc/usr/var 等系统目录的访问。但 realpath 仅解析符号链接(symlink),不解析硬链接(hard link)。攻击者若能在系统目录中创建硬链接(如 ln /etc/cron.d/malicious.db /tmp/legit.db),或利用自托管 runner 上已有的硬链接,可通过 db-path 输入传入指向系统目录的硬链接路径。由于 realpath 不会将硬链接解析到目标目录,验证阶段通过,但在 rm -f -- "$db_path" "$db_path-wal" ... 执行时,rm 会直接删除硬链接指向的 inode(即系统文件本身),因为硬链接共享同一 inode。

    攻击条件:

    • 攻击者通过 GitHub Actions 输入控制 db-pathpull_request_target 或有权限的 fork PR)
    • 自托管 runner 上存在已知的数据库文件硬链接到系统关键文件(或攻击者能预先创建硬链接)
    • rm -f 无确认直接删除,且 WAL/journal 后缀(-wal, -shm, -journal)可能扩大了删除范围

    影响:可在系统受保护路径中删除任意文件,导致服务拒绝或权限升级。

    修复建议:

    • 增加硬链接检测:使用 stat -c '%h' 检查链接计数,若 > 1 则拒绝
    • 或在删除前使用 stat -c '%d:%i' 校验文件所在设备与 inode 不在禁止的设备范围内

建议项

  1. run-opencode.shsource "$_resolve_script" 2>/dev/null 抑制 stderr

    第 149 行使用 2>/dev/null 隐藏了 resolve_db_path 的错误输出,如果验证失败但返回码意外为 0,用户无法在日志中看到错误原因。建议在 || 分支中打印 stderr 内容。

  2. OPENCODE_RETRY_ON_REGEX 的 ReDoS 风险(非本 PR 引入)

    第 192 行 grep -Eiq "$OPENCODE_RETRY_ON_REGEX" "$log_file" 使用用户控制的 regex 可能导致 ReDoS。此问题存在于原有代码中,但建议在后续 PR 中为 regex 添加超时保护或长度限制。

  3. 默认路径依赖 $HOME

    resolve-db-path.sh:26 使用 $HOME/.local/share/opencode/opencode.db。在 $HOME 未设置(极罕见)时退化到相对路径。建议在 Sourcing 时增加 export HOME="${HOME:-/root}" 保护。

  4. 测试未覆盖硬链接场景

    tests/test_all.pyTestCleanupDbTestMigrationRecovery 未覆盖硬链接绕过或 inode 验证分支。建议增加对应测试用例。

performance

性能良好 / GOOD

该 PR 引入的功能(数据库清理、路径解析、迁移自动恢复)均为一次性操作,在 CI 环境的工作流步骤中执行,不会对 runtime 性能产生可衡量的影响。

阻塞项:无

建议项:无

architecture

架构有疑虑 / CONCERNS

该 PR 总体设计合理,新增 shared/cleanup-db.shshared/resolve-db-path.sh 作为共享模块,被 github-run-opencode/action.ymlrun-opencode/run-opencode.sh 分别使用,降低了重复。但存在以下架构层面的问题:

阻塞项:无

建议项:

  1. 泄漏抽象run-opencode.sh 中的迁移恢复逻辑(约 50 行)包含了路径解析的 fallback 代码,这些代码本质上是 resolve-db-path.sh 的内部实现知识的 duplication。当 resolve_db_path 失败后,调用者重新 unset OPENCODE_DB_PATH 并再次调用 resolve_db_path,说明 resolve-db-path.sh 没有提供足够清晰的"总是返回一个安全路径"的接口。建议让 resolve_db_path 在失败时自动回退到默认路径,消除调用者的 fallback 逻辑。

  2. TOCTOU 防御不一致cleanup-db.sh 在删除文件前做了 inode 校验防止 TOCTOU 攻击,但 run-opencode.sh 中的迁移恢复删除路径(rm -f -- "$db_path" "$db_path-wal" ...)没有相同的检查。同一类删除操作的安全策略应保持一致,建议将删除逻辑(含 TOCTOU 检查)统一封装到共享函数中。

  3. 迁移恢复逻辑过大run-opencode.sh 的迁移恢复是一个 35+ 行的 inline 块(含路径解析、env 判断、fallback、删除、attempt 管理等),虽然与重试循环耦合紧密难以完全抽离,但可以将"路径解析+验证"和"安全删除 db 文件"两个子逻辑移到 shared/resolve-db-path.sh 中的函数,减少 inline 复杂度。

  4. 默认路径多源同步风险:默认数据库路径 ~/.local/share/opencode/opencode.db 定义在 resolve-db-path.sh 中,但也在 action.ymldb-path input description 和 README.md 中各引用一次。如果路径变更需要同步三处,建议通过文档自动生成或在 action.yml 中引用而非硬编码描述文本。

@Svtter Svtter merged commit 42aab22 into main Jun 7, 2026
4 checks passed
@Svtter Svtter deleted the feat/cleanup-opencode-db-v2 branch June 7, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat New feature or enhancement review:p2 Minor review findings setup Setup and installation triaged Issue has been triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 添加定期清理 opencode.db 的 workflow run

2 participants