From 3989dd3c3a9716fbfbb8a1af0fee9c27c03978d4 Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 15 Sep 2026 11:58:17 +0800 Subject: [PATCH] =?UTF-8?q?fix(extension):=20=E5=AE=B9=E5=99=A8=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E5=8F=AF=E5=86=99=E5=B7=A5=E4=BD=9C=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E2=80=94=E2=80=94=E4=BB=A3=E7=A0=81=E5=A4=8D=E5=88=B6=E8=BF=9B?= =?UTF-8?q?=20run=5Fdir=20=E6=89=A7=E8=A1=8C+=E4=BA=A7=E7=89=A9=E6=94=B6?= =?UTF-8?q?=E5=89=B2=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 18 用例脚本在 cwd 写 outputs/ 相对目录,代码挂载只读致 trial 失败。 等价做法:代码复制进 output/work 执行,{code_dir} 指向可写副本, 成功后 work 产物合并进 output_dir,输出契约校验不变。 Co-Authored-By: Claude Fable 5 --- bioflow_kernel/cli/run_extension.py | 44 +++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/bioflow_kernel/cli/run_extension.py b/bioflow_kernel/cli/run_extension.py index f2d997e..daf6a01 100644 --- a/bioflow_kernel/cli/run_extension.py +++ b/bioflow_kernel/cli/run_extension.py @@ -266,6 +266,30 @@ def _find_code_dir(ext_dir: Path) -> Path | None: return None +def _copy_code_tree(src: Path, dst: Path) -> None: + """把代码树复制进可写目录(shutil.copytree,目标已存在)。""" + import shutil + if not src.is_dir(): + return + for item in src.iterdir(): + dest = dst / item.name + if item.is_dir(): + shutil.copytree(item, dest, dirs_exist_ok=True) + else: + shutil.copy2(item, dest) + + +def _merge_tree(src: Path, dst: Path) -> None: + """把 src 全部内容合并进 dst(含子目录;同名覆盖)。""" + import shutil + for item in src.iterdir(): + dest = dst / item.name + if item.is_dir(): + shutil.copytree(item, dest, dirs_exist_ok=True) + else: + shutil.copy2(item, dest) + + def _kill_group(pid: int, sig: int) -> None: try: os.killpg(pid, sig) @@ -362,13 +386,15 @@ def main(argv: list[str] | None = None) -> int: input_dir = Path(args.input_dir) output_dir = Path(args.output_dir) - # 2026-09-15 修复:默认工作目录 = 代码目录(而非 output_dir)。 - # 用户脚本语义(18 用例)是「cd 代码目录后 bash run.sh」——run.sh 内 - # 的相对脚本路径(python check_inputs.py)依赖 cwd=代码目录; - # 显式 --work-dir 参数仍可覆盖。 - work_dir = Path(args.work_dir) if args.work_dir else code_dir output_dir.mkdir(parents=True, exist_ok=True) + # 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.mkdir(parents=True, exist_ok=True) + _copy_code_tree(code_dir, work_dir) + # 模板中 {code_dir} 指向可写副本(而非只读挂载) + effective_code_dir = work_dir try: parameters = _parse_params(args.param) @@ -386,7 +412,7 @@ def main(argv: list[str] | None = None) -> int: input_files=input_files, parameters=parameters, output_dir=output_dir, - code_dir=code_dir, + code_dir=effective_code_dir, ) except ExtensionRenderError as exc: print(f"[run_extension] 模板渲染失败: {exc}", file=sys.stderr) @@ -413,6 +439,12 @@ 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 + try: + _merge_tree(work_dir, output_dir) + except OSError as exc: + print(f"[run_extension] 产物收割失败: {exc}", file=sys.stderr) + return EXIT_SETUP if not _output_contract_ok(output_dir): print(f"[run_extension] 输出契约校验失败:输出目录为空 {output_dir} (exit {EXIT_OUTPUT_CONTRACT})", file=sys.stderr)