From 84ff968111dea268915b44e323f8cd9ee05bbf7c Mon Sep 17 00:00:00 2001 From: openqj Date: Tue, 27 Oct 2026 00:41:40 +0800 Subject: [PATCH] fix: deploy verify parsing must ignore ssh warning lines (#55) Co-authored-by: host --- deploy/deploy.py | 18 ++++++++++++++++-- deploy/tests/test_deploy.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/deploy/deploy.py b/deploy/deploy.py index 0e2bca1..5f30343 100644 --- a/deploy/deploy.py +++ b/deploy/deploy.py @@ -35,6 +35,7 @@ import argparse import hashlib import json import os +import re import shutil import subprocess import sys @@ -183,11 +184,24 @@ def scp_cmd(host, user, local, remote): def remote_sha(host, user, remote_path): + """远端 sha256sum 的输出里取 64 位 hex。不能取 split()[0]:经 pty + 的 ssh helper 会把 stderr 的 Warning(例如 Permanently added to + the list of known hosts)合并进 stdout 放在 sha 行前面,首 token + 于是变成 Warning:,导致传输明明成功却被判验真失败(2026-10-27 + 新链路首推实测)。""" r = subprocess.run(ssh_cmd(host, user, "sha256sum %s" % remote_path), capture_output=True, text=True) if r.returncode != 0: return None - return r.stdout.split()[0] if r.stdout.split() else None + m = re.search(r"\b[0-9a-f]{64}\b", r.stdout) + return m.group(0) if m else None + + +def live_commit(text): + """从 ssh 输出里取 40 位 commit。理由同 remote_sha:helper 的 + Warning 行会混进 stdout,直接 strip() 比对必失败。""" + m = re.search(r"\b[0-9a-f]{40}\b", text) + return m.group(0) if m else None def remote_rm(host, user, remote_path): @@ -312,7 +326,7 @@ def main(argv=None): live = subprocess.run( ssh_cmd(host, user, "cat /opt/codex-app-server/DEPLOYED_COMMIT"), capture_output=True, text=True) - if live.stdout.strip() != commit: + if live_commit(live.stdout) != commit: print("二次核对失败:live DEPLOYED_COMMIT=%s,期望 %s。" "请立即用 deploy/rollback.sh 检查,不要再推新版。" % (live.stdout.strip(), commit), file=sys.stderr) diff --git a/deploy/tests/test_deploy.py b/deploy/tests/test_deploy.py index 611ec94..145bbd5 100644 --- a/deploy/tests/test_deploy.py +++ b/deploy/tests/test_deploy.py @@ -9,6 +9,7 @@ import sys import tarfile import tempfile import unittest +from unittest import mock REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname( os.path.abspath(__file__)))) @@ -52,6 +53,34 @@ class TestDeployPure(unittest.TestCase): finally: os.unlink(path) + def test_remote_sha_ignores_ssh_warning_lines(self): + # 2026-10-27 新链路首推实测:pty helper 把 ssh 的 Warning 行 + # 合并进 stdout 放在 sha 行前面,旧 split()[0] 取到 Warning: + # 导致传输明明成功却被判验真失败、两路都“失败”。 + sha = "5d0645b85a65b2d58d0d5812f9e8020bdafe15d60dc440e763cfcc7cd178148b" + fake = subprocess.CompletedProcess( + args=[], returncode=0, + stdout="Warning: Permanently added 'x' (ED25519) to the list of known hosts.\n" + + sha + " /tmp/x.tar.gz\n", stderr="") + with mock.patch.object(deploy_mod.subprocess, "run", + return_value=fake): + self.assertEqual(deploy_mod.remote_sha("h", "u", "/tmp/x"), sha) + + def test_remote_sha_none_without_hash(self): + fake = subprocess.CompletedProcess(args=[], returncode=0, + stdout="Warning: something\n", + stderr="") + with mock.patch.object(deploy_mod.subprocess, "run", + return_value=fake): + self.assertIsNone(deploy_mod.remote_sha("h", "u", "/tmp/x")) + + def test_live_commit_ignores_ssh_warning_lines(self): + commit = "32125f7a980e24fd28664a60701d7c1123beb63e" + out = "Warning: Permanently added 'x' (ED25519) to the list of known hosts.\n" \ + + commit + "\n" + self.assertEqual(deploy_mod.live_commit(out), commit) + self.assertIsNone(deploy_mod.live_commit("Warning: something\n")) + class TestBuildRelease(unittest.TestCase): def test_build_from_temp_git_repo(self):