feat(deployer): fall back to scp when rsync is missing
This commit is contained in:
@@ -22,14 +22,14 @@ _SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
if str(_SCRIPT_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(_SCRIPT_DIR))
|
||||
|
||||
from lib import DEFAULT_BASE_PATH, project_root, rsync_ssh_args, service_info, ssh_cmd # noqa: E402
|
||||
from lib import DEFAULT_BASE_PATH, rsync_ssh_args, scp_base_args, service_info, ssh_cmd # noqa: E402
|
||||
|
||||
|
||||
def sync_directory(info: dict) -> int:
|
||||
if shutil.which("rsync"):
|
||||
return _sync_rsync(info)
|
||||
print("本机没有 rsync,改用 tar over SSH(不会删除远程多余文件)")
|
||||
return _sync_tar(info)
|
||||
print("本机没有 rsync,改用 scp(不会删除远程多余文件)")
|
||||
return _sync_scp(info)
|
||||
|
||||
|
||||
def _sync_rsync(info: dict) -> int:
|
||||
@@ -63,42 +63,81 @@ def _sync_rsync(info: dict) -> int:
|
||||
print(f"错误: rsync 执行失败,退出码: {e.returncode}")
|
||||
return 1
|
||||
except FileNotFoundError:
|
||||
print("错误: rsync 命令未找到,请确保已安装 rsync")
|
||||
return 1
|
||||
print("本机没有 rsync,改用 scp(不会删除远程多余文件)")
|
||||
return _sync_scp(info)
|
||||
|
||||
|
||||
def _sync_tar(info: dict) -> int:
|
||||
def collect_sync_files(source_dir: str, excludes: list[str]) -> list[Path]:
|
||||
"""列出待同步文件;排除规则与 rsync `--exclude=name/` 一致(任意层级的同名目录)。"""
|
||||
source = Path(source_dir)
|
||||
exclude_set = set(excludes)
|
||||
files: list[Path] = []
|
||||
for root, dirs, names in os.walk(source):
|
||||
dirs[:] = [d for d in dirs if d not in exclude_set]
|
||||
for name in names:
|
||||
path = Path(root) / name
|
||||
if path.is_file():
|
||||
files.append(path)
|
||||
return files
|
||||
|
||||
|
||||
def _sync_scp(info: dict) -> int:
|
||||
source_dir = info["service_dir"]
|
||||
remote_dir = info["remote_dir"]
|
||||
tar_cmd = ["tar", "czf", "-", "-C", source_dir]
|
||||
for item in info["sync_exclude"]:
|
||||
tar_cmd.append(f"--exclude={item}")
|
||||
tar_cmd.append(".")
|
||||
node = info["node"]
|
||||
source = Path(source_dir)
|
||||
files = collect_sync_files(source_dir, info["sync_exclude"])
|
||||
|
||||
remote_shell = (
|
||||
f"mkdir -p {shlex.quote(remote_dir)} && "
|
||||
f"tar xzf - -C {shlex.quote(remote_dir)}"
|
||||
)
|
||||
ssh = ssh_cmd(info, remote_shell)
|
||||
print(f"正在同步 {source_dir} 到 {info['node']}:{remote_dir}")
|
||||
print(f"正在同步 {source_dir} 到 {node}:{remote_dir}")
|
||||
if info["sync_exclude"]:
|
||||
print(f"排除目录: {', '.join(info['sync_exclude'])}")
|
||||
print(f"执行命令: tar | {' '.join(ssh)}")
|
||||
print("-" * 60)
|
||||
|
||||
tar = subprocess.Popen(tar_cmd, stdout=subprocess.PIPE)
|
||||
remote_dirs = {remote_dir}
|
||||
grouped: dict[str, list[Path]] = {}
|
||||
for path in files:
|
||||
parent = path.relative_to(source).parent.as_posix()
|
||||
grouped.setdefault(parent, []).append(path)
|
||||
if parent != ".":
|
||||
remote_dirs.add(f"{remote_dir}/{parent}")
|
||||
|
||||
mkdir_remote = "mkdir -p " + " ".join(shlex.quote(d) for d in sorted(remote_dirs))
|
||||
mkdir_cmd = ssh_cmd(info, mkdir_remote)
|
||||
print(f"执行命令: {' '.join(mkdir_cmd)}")
|
||||
try:
|
||||
completed = subprocess.run(ssh, stdin=tar.stdout, check=False)
|
||||
finally:
|
||||
if tar.stdout:
|
||||
tar.stdout.close()
|
||||
tar.wait()
|
||||
if tar.returncode:
|
||||
print(f"错误: tar 打包失败,退出码: {tar.returncode}")
|
||||
return tar.returncode
|
||||
completed = subprocess.run(mkdir_cmd, check=False)
|
||||
except FileNotFoundError:
|
||||
print("错误: ssh 命令未找到")
|
||||
return 1
|
||||
if completed.returncode:
|
||||
print(f"错误: 远程 tar 解包失败,退出码: {completed.returncode}")
|
||||
print(f"错误: 远程 mkdir 失败,退出码: {completed.returncode}")
|
||||
return completed.returncode
|
||||
|
||||
if not files:
|
||||
print("没有需要同步的文件")
|
||||
print("-" * 60)
|
||||
print("同步完成!")
|
||||
return 0
|
||||
|
||||
try:
|
||||
for parent, group in sorted(grouped.items()):
|
||||
dest = remote_dir if parent == "." else f"{remote_dir}/{parent}"
|
||||
scp_cmd = [
|
||||
*scp_base_args(info),
|
||||
"-p",
|
||||
"--",
|
||||
*[str(p) for p in group],
|
||||
f"{node}:{dest}/",
|
||||
]
|
||||
print(f"执行命令: {' '.join(scp_cmd)}")
|
||||
subprocess.run(scp_cmd, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"错误: scp 执行失败,退出码: {e.returncode}")
|
||||
return 1
|
||||
except FileNotFoundError:
|
||||
print("错误: scp 命令未找到,请确保已安装 openssh-client")
|
||||
return 1
|
||||
|
||||
print("-" * 60)
|
||||
print("同步完成!")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user