From a008fbba2aa31218a1d6d7037bb1c315dc5d930e Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 15 Sep 2026 11:59:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(extension):=20=E4=BA=A7=E7=89=A9=E6=94=B6?= =?UTF-8?q?=E5=89=B2=E6=8E=92=E9=99=A4=E4=BB=A3=E7=A0=81=E5=89=AF=E6=9C=AC?= =?UTF-8?q?+=E5=88=A0=20work=20=E7=9B=AE=E5=BD=95=E2=80=94=E2=80=94?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E5=A5=91=E7=BA=A6=E6=A0=A1=E9=AA=8C=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E4=B8=8D=E5=8F=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 --- bioflow_kernel/cli/run_extension.py | 44 +++++++++++++++++++---------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/bioflow_kernel/cli/run_extension.py b/bioflow_kernel/cli/run_extension.py index daf6a01..f81e40b 100644 --- a/bioflow_kernel/cli/run_extension.py +++ b/bioflow_kernel/cli/run_extension.py @@ -266,27 +266,39 @@ def _find_code_dir(ext_dir: Path) -> Path | None: return None -def _copy_code_tree(src: Path, dst: Path) -> None: - """把代码树复制进可写目录(shutil.copytree,目标已存在)。""" +def _copy_code_tree(src: Path, dst: Path) -> set[str]: + """把代码树复制进可写目录;返回原始代码相对路径集合(收割时排除)。""" import shutil + rels: set[str] = set() if not src.is_dir(): - return - for item in src.iterdir(): - dest = dst / item.name + return rels + for item in src.rglob("*"): + rel = item.relative_to(src).as_posix() + dest = dst / rel if item.is_dir(): - shutil.copytree(item, dest, dirs_exist_ok=True) + dest.mkdir(parents=True, exist_ok=True) else: + dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(item, dest) + rels.add(rel) + return rels -def _merge_tree(src: Path, dst: Path) -> None: - """把 src 全部内容合并进 dst(含子目录;同名覆盖)。""" +def _harvest_outputs(src: Path, dst: Path, exclude: set[str]) -> None: + """把 src 内产物(排除原始代码路径)合并进 dst,保留相对路径。""" import shutil - for item in src.iterdir(): - dest = dst / item.name + for item in src.rglob("*"): + rel = item.relative_to(src).as_posix() + if rel in exclude: + continue + # 排除路径是原始代码文件所在目录时也跳过其祖先目录本身 + if any(rel == e or rel.startswith(e + "/") for e in exclude): + continue + dest = dst / rel if item.is_dir(): - shutil.copytree(item, dest, dirs_exist_ok=True) + dest.mkdir(parents=True, exist_ok=True) else: + dest.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(item, dest) @@ -390,9 +402,9 @@ def main(argv: list[str] | None = None) -> int: # 2026-09-15 修复:代码挂载只读,但 18 用例语义是「cd 代码目录后脚本写 # outputs/ 相对目录」。等价做法 = 把代码复制进可写 run_dir 执行, # 结束后把 run_dir 产物收割进 output_dir。显式 --work-dir 可覆盖。 - work_dir = Path(args.work_dir) if args.work_dir else (output_dir / "work") + work_dir = Path(args.work_dir) if args.work_dir else (output_dir / "_work") work_dir.mkdir(parents=True, exist_ok=True) - _copy_code_tree(code_dir, work_dir) + code_rel_paths = _copy_code_tree(code_dir, work_dir) # 模板中 {code_dir} 指向可写副本(而非只读挂载) effective_code_dir = work_dir @@ -439,9 +451,11 @@ def main(argv: list[str] | None = None) -> int: if code != EXIT_OK: print(f"[run_extension] command exited with code {code}", file=sys.stderr) return code - # 收割:work_dir 内全部产物(含 outputs/ 子目录与中间文件)合并进 output_dir + # 收割:work_dir 产物(排除原始代码文件)合并进 output_dir,然后删除 work 目录 try: - _merge_tree(work_dir, output_dir) + _harvest_outputs(work_dir, output_dir, code_rel_paths) + import shutil + shutil.rmtree(work_dir, ignore_errors=True) except OSError as exc: print(f"[run_extension] 产物收割失败: {exc}", file=sys.stderr) return EXIT_SETUP