Skip to content

从不可变 Git 提交安全同步到远端

sync_to_remote.sh 不再同步当前工作树。部署输入只能来自一个 Git commit,以及该 commit 内版本化的 deploy/runtime-paths.txt。因此 ignored 文件、untracked 文件、未提交修改和 manifest 之外的 tracked 文件都不会进入 rsync 源目录。

版本化部署资产包括:

  • sync_to_remote.sh:preview/apply 协议和远端保护策略。
  • scripts/build_deploy_stage.py:仅使用 Python 标准库和项目 secret scanner 构建 staging。
  • scripts/scan_workspace_secrets.py:CR-201 的脱敏 secret scanner。
  • deploy/runtime-paths.txt:带版本头的 runtime 路径清单。
  • .xiaoqing-sync-root:本地和远端部署根哨兵。

staging 如何生成

每次 preview 或 apply 都创建全新的临时 staging:

  1. --ref 一次解析成完整 40 位 commit ID。
  2. 从该 commit 读取 deploy/runtime-paths.txt,不读取工作树版本。
  3. 对清单中的路径运行 git archive <commit>
  4. 手工检查 tar 成员,只接受清单覆盖的普通文件和结构目录;拒绝绝对路径、..、重复成员、symlink、hardlink、设备和其他特殊成员。
  5. 对文件数和总字节数应用硬上限。
  6. 对 staging 运行 scan_workspace_secrets.py --workspace 等价检查;出现 secret、stale allowlist 或 scanner 配置错误时删除 staging 并终止。
  7. 计算 commit ID、manifest SHA-256 和规范化 staging tree SHA-256。

staging 只在本次进程中使用,退出时会删除。要部署本地代码,必须先把它提交到 Git;仅 git add 或修改工作树不会影响部署内容。

远端根目录准备

首次使用时,在确认无误的部署目录创建单行哨兵。不要在 / 或上级目录创建:

bash
printf 'xiaoqing-sync-root-v1\n' > /c/Users/torch/Desktop/XiaoQing/XiaoQing_V3/.xiaoqing-sync-root

脚本通过 SSH 执行 readlink -f,并在远端确认:

  • 解析结果存在、是目录且不是 /
  • 哨兵恰好一行且内容完全相同。

SSH 返回后,本地还会重新要求响应恰好一行、无 CR、符合安全绝对路径字符集且不是 /。任何额外输出或特殊字符都会在 rsync 前终止。

Preview

默认 preview HEAD,也可以选择安全的 branch/tag/ref。ref 会立即固定为 commit:

bash
./sync_to_remote.sh
./sync_to_remote.sh --dry-run --ref main

preview 会显示 commit、manifest/tree digest、远端目标和 itemized rsync dry-run,最后输出:

text
Plan SHA256: <64-hex-digest>

plan digest 绑定以下全部内容:

  • 固定 commit、manifest digest、tree digest。
  • SSH host 和重新验证后的远端绝对根目录。
  • 完整 rsync 参数/保护规则。
  • 当前远端状态产生的 dry-run 输出。

preview 不修改本地 Git 或远端文件。请逐条检查新增、覆盖和删除计划。

Apply

apply 不接受 HEAD、branch 或 tag,必须显式提供 preview 输出的完整 commit 和 plan digest:

bash
./sync_to_remote.sh \
  --apply \
  --confirm-delete \
  --ref 0123456789abcdef0123456789abcdef01234567 \
  --expect-plan 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

apply 会重新从相同 commit 构建并扫描 staging,再运行一次 dry-run。只有重新计算的 plan SHA-256 与 --expect-plan 完全相同才执行真实 rsync。代码、manifest、目标路径、远端文件状态或 rsync 策略只要发生变化,apply 都会拒绝,必须重新 preview。

删除保护与远端状态

真实同步使用 --delete-delay,但 receiver-side protect 规则会保留以下远端状态,即使它们不在 staging:

  • .git.venvvenv.env*.local_archive
  • config/config.jsonconfig/secrets.json
  • logs/data/bot.log*
  • 所有插件的 data/cache/config.jsonsecrets.jsontest_reports/ 和 SQLite 数据库/sidecar。
  • Pendo test_tools/、Xiaoqing Chat figures/
  • arXiv Filter 的本地模型、训练 cache 和训练 CSV。

这些规则保护的是远端删除边界;真正的本地 ignored/untracked 文件根本不会进入 commit archive,也不会作为发送端输入出现。运行配置和秘密仍应通过远端文件、volume 或 secret 管理系统维护。

环境覆盖

可配置:

  • XIAOQING_SYNC_HOST:受限字符集的 SSH host。
  • XIAOQING_SYNC_DIR:安全绝对远端路径。
  • PYTHON:用于 staging 和 digest 的 Python 命令。

XIAOQING_SSH_BINXIAOQING_RSYNC_BIN 主要用于受控测试/封装;生产环境通常保持默认 sshrsync。调用者必须是受信任管理员,并自行保护 SSH 凭据。

基于 MIT 许可发布

加载中...