Files
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

102 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""deploy_code —— 把**程序文件**复制到某个工作区,让那个工作区**跑自己的一份**。
🔴 为什么需要它(用户 2026-10-03 定案):
「**跨工作区使用会话协作技能,除了看板共用,其余都是独立的,包括程序和相关文件**」
⇒ 改口径之前,**所有工作区跑的都是技能目录里同一份 `collabd.py`**(靠 `COLLABD_CONFIG` 区分工作区)。
⚠️ 那种形态下:改一份代码 ⇒ **所有工作区同时变了**;某个区的代码坏了 ⇒ 别的区一起坏。
⇒ 现在改成:**技能目录=源(唯一真身)**,每个工作区 `.workbuddy/collab/` 下有**自己的副本**。
✅ 判据(能不能证明"真的独立"):
① 副本文件在位且 **md5 与源一致**(本脚本打印);
② 心跳里的 **`argv0`** 指向**该工作区的副本路径**(由 `collabd.py` 写进心跳)——
⛔ 只看"文件在"不够:进程可能还在跑旧的那份(改完不重启=没生效,P0-17 同族)。
用法:
python deploy_code.py --ws E:/ProgramData/AIProject/<工作区> # 部署默认清单
python deploy_code.py --ws <工作区> --dry-run # 只报要做什么
python deploy_code.py --ws <工作区> --only collabd.py # 只部一个
⚠️ **改完代码要重跑本命令**(各区副本不会自动跟着变)——
这是"独立"的代价:⛔ 一处改动要分发 N 处 ⇒ 改完请顺手跑一遍,别等某个区行为不对再来查。
"""
from __future__ import annotations
import argparse
import hashlib
import os
import shutil
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
# 🔴 各工作区默认需要的**独立程序**(⛔ 不含 `board.py` —— 看板是**共用一份**的)
DEFAULT_FILES = ["collabd.py", "goalctl.py"]
def md5(p: Path) -> str:
try:
return hashlib.md5(p.read_bytes()).hexdigest()
except Exception:
return "?"
def main() -> int:
try:
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
ap = argparse.ArgumentParser()
ap.add_argument("--ws", required=True, help="工作区绝对路径")
ap.add_argument("--only", default="", help="只部这一个文件名(可多次用逗号分隔)")
ap.add_argument("--dry-run", action="store_true")
a = ap.parse_args()
ws = Path(a.ws).resolve()
if not ws.is_dir():
print("✗ 工作区不存在:%s" % ws)
return 1
dst_dir = ws / ".workbuddy" / "collab"
files = [x.strip() for x in (a.only.split(",") if a.only else DEFAULT_FILES) if x.strip()]
print("工作区:%s" % ws)
print("副本目录:%s" % dst_dir)
bad = 0
for name in files:
src = HERE / name
if not src.is_file():
print(" ✗ 源里没有 %s" % name)
bad += 1
continue
dst = dst_dir / name
if a.dry_run:
print(" · [试运行] %s → %s(源 md5=%s)" % (name, dst, md5(src)[:8]))
continue
dst_dir.mkdir(parents=True, exist_ok=True)
# 🔴 覆盖前先留一份带时间戳的备份(⛔ 免得新版在这台机器上跑不通时没退路)
if dst.is_file() and md5(dst) != md5(src):
bak = dst.with_suffix(dst.suffix + ".bak-%s" % time_stamp())
try:
shutil.copy2(str(dst), str(bak))
print(" · 旧副本已备份:%s" % bak.name)
except Exception as e:
print(" ⚠ 备份失败(继续):%r" % e)
shutil.copy2(str(src), str(dst))
same = md5(src) == md5(dst)
print(" %s %s md5=%s%s" % ("✓" if same else "✗", name, md5(dst)[:8],
"" if same else "(⚠ 与源不一致)"))
if not same:
bad += 1
if not a.dry_run:
print("\n%s 下一步:让该工作区的协作程序**重启**才会用上副本"
"(⛔ 改文件不重启=没生效)——判据=心跳里的 `argv0` 指向副本路径。" % ws.name)
return 0 if bad == 0 else 1
def time_stamp() -> str:
import time
return time.strftime("%Y%m%d-%H%M%S")
if __name__ == "__main__":
sys.exit(main())