从不可变 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:
- 将
--ref一次解析成完整 40 位 commit ID。 - 从该 commit 读取
deploy/runtime-paths.txt,不读取工作树版本。 - 对清单中的路径运行
git archive <commit>。 - 手工检查 tar 成员,只接受清单覆盖的普通文件和结构目录;拒绝绝对路径、
..、重复成员、symlink、hardlink、设备和其他特殊成员。 - 对文件数和总字节数应用硬上限。
- 对 staging 运行
scan_workspace_secrets.py --workspace等价检查;出现 secret、stale allowlist 或 scanner 配置错误时删除 staging 并终止。 - 计算 commit ID、manifest SHA-256 和规范化 staging tree SHA-256。
staging 只在本次进程中使用,退出时会删除。要部署本地代码,必须先把它提交到 Git;仅 git add 或修改工作树不会影响部署内容。
远端根目录准备
首次使用时,在确认无误的部署目录创建单行哨兵。不要在 / 或上级目录创建:
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:
./sync_to_remote.sh
./sync_to_remote.sh --dry-run --ref mainpreview 会显示 commit、manifest/tree digest、远端目标和 itemized rsync dry-run,最后输出:
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:
./sync_to_remote.sh \
--apply \
--confirm-delete \
--ref 0123456789abcdef0123456789abcdef01234567 \
--expect-plan 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefapply 会重新从相同 commit 构建并扫描 staging,再运行一次 dry-run。只有重新计算的 plan SHA-256 与 --expect-plan 完全相同才执行真实 rsync。代码、manifest、目标路径、远端文件状态或 rsync 策略只要发生变化,apply 都会拒绝,必须重新 preview。
删除保护与远端状态
真实同步使用 --delete-delay,但 receiver-side protect 规则会保留以下远端状态,即使它们不在 staging:
.git、.venv、venv、.env*、.local_archive。config/config.json、config/secrets.json。- 根
logs/、data/、bot.log*。 - 所有插件的
data/、cache/、config.json、secrets.json、test_reports/和 SQLite 数据库/sidecar。 - Pendo
test_tools/、Xiaoqing Chatfigures/。 - arXiv Filter 的本地模型、训练 cache 和训练 CSV。
这些规则保护的是远端删除边界;真正的本地 ignored/untracked 文件根本不会进入 commit archive,也不会作为发送端输入出现。运行配置和秘密仍应通过远端文件、volume 或 secret 管理系统维护。
环境覆盖
可配置:
XIAOQING_SYNC_HOST:受限字符集的 SSH host。XIAOQING_SYNC_DIR:安全绝对远端路径。PYTHON:用于 staging 和 digest 的 Python 命令。
XIAOQING_SSH_BIN 和 XIAOQING_RSYNC_BIN 主要用于受控测试/封装;生产环境通常保持默认 ssh 和 rsync。调用者必须是受信任管理员,并自行保护 SSH 凭据。