fix(remote_env): 批量pip失败时降级逐包安装(部分成功优于全败)
This commit is contained in:
parent
ea17307a6c
commit
023d4a67fa
@ -312,14 +312,55 @@ async def install_remote_deps(env: dict, specs: list, pip_index: str,
|
||||
installed = list(specs)
|
||||
failed = []
|
||||
else:
|
||||
installed = [s for s in specs if all(
|
||||
not s.lower().startswith(re.split(r"[\[<>=!~\s]", f, 1)[0].lower())
|
||||
for f in failed)]
|
||||
# 批量失败 → 逐包降级:一次 SSH 会话内逐包安装(pip 幂等,已装的秒过),
|
||||
# 部分成功优于全败。
|
||||
return await _install_per_package(env, specs, pip_index, rel, timeout)
|
||||
return {"ok": rc == 0, "installed": installed, "failed": failed,
|
||||
"venv": "~/" + rel + "/.venv-skills",
|
||||
"tail": out[-800:]}
|
||||
|
||||
|
||||
async def _install_per_package(env: dict, specs: list, pip_index: str,
|
||||
rel: str, timeout: int) -> dict:
|
||||
"""逐包安装(批量失败后的降级路径)。返回格式与 install_remote_deps 一致。"""
|
||||
payload = base64.b64encode(json.dumps(specs).encode()).decode()
|
||||
index_arg = ('-i ' + shlex.quote(pip_index) + ' ') if pip_index else ''
|
||||
script = (
|
||||
'RD="$HOME/' + rel + '"; VENV="$RD/.venv-skills"; '
|
||||
'[ -x "$VENV/bin/pip" ] || { echo "VENV_MISSING"; exit 4; }; '
|
||||
'set -f; '
|
||||
'echo "' + payload + '" | base64 -d | python3 -c '
|
||||
'"import json,sys;print(chr(10).join(json.load(sys.stdin)))" | '
|
||||
'while IFS= read -r pkg; do '
|
||||
' [ -n "$pkg" ] || continue; '
|
||||
' if "$VENV/bin/pip" install --no-input --disable-pip-version-check '
|
||||
+ index_arg + '"$pkg" >/dev/null 2>&1; then echo "OK:$pkg"; '
|
||||
' else echo "FAILED:$pkg"; fi; '
|
||||
'done'
|
||||
)
|
||||
ssh_cmd = ["ssh"] + _ssh_common_args(env) + [_ssh_target(env), script]
|
||||
try:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
*ssh_cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
|
||||
stdout, _stderr = await asyncio.wait_for(proc.communicate(), timeout=timeout + 30)
|
||||
except asyncio.TimeoutError:
|
||||
return {"ok": False, "error": f"逐包安装超时({timeout}s),可重跑"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": "SSH 执行失败: " + str(e)[:300]}
|
||||
|
||||
out = stdout.decode("utf-8", "replace")
|
||||
installed, failed = [], []
|
||||
for line in out.splitlines():
|
||||
if line.startswith("OK:"):
|
||||
installed.append(line[3:])
|
||||
elif line.startswith("FAILED:"):
|
||||
failed.append(line[7:])
|
||||
return {"ok": len(failed) == 0 and len(installed) > 0,
|
||||
"installed": installed, "failed": failed,
|
||||
"venv": "~/" + rel + "/.venv-skills",
|
||||
"mode": "per-package", "tail": out[-500:]}
|
||||
|
||||
|
||||
# ── 总编排 ────────────────────────────────────────────────────
|
||||
|
||||
async def init_remote_env(env: dict, org_id: str = "", sor=None,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user