# -*- coding: utf-8 -*- """ RRPP 发布上传脚本 负责把 PyInstaller 打包好的 EXE 上传到服务器,并更新 latest_version.json, 使客户端启动时能检测并下载新版本。 ## 服务器连接信息 主机: 172.207.81.145 端口: 22 用户: RRRosmontis 密码: ftA406u@all#2309 服务器可能启用了键盘交互认证,sshpass 等非交互式工具会报 Permission denied, 因此使用 .venv 中的 paramiko 进行 SSH/SFTP。 ## 完整发布流程 1. 修改 proxy.py 与本脚本中的 VERSION 为当前年月日时分(例如 202607022100)。 2. 重新打包 EXE: .venv/Scripts/python.exe -m PyInstaller RRPP.spec --noconfirm 或双击运行 build.bat,输出统一在 dist/ 目录。 3. 运行本脚本上传并更新服务器版本信息: .venv/Scripts/python.exe upload_to_server.py 脚本执行步骤: - SSH 连接到 172.207.81.145 - 把 dist/Royirin Rosmontis Private Proxy.exe 上传到 /tmp/proxy.exe - 用 sudo 创建 /var/www/html/release// 并移动 EXE - 用 sudo 写入 /var/www/html/latest_version.json(https 下载链接) - 回显服务器上的 latest_version.json 内容用于验证 ## 注意事项 - 上传前请确保 dist/ 下的 EXE 已重新打包,且版本号与 VERSION 一致。 - 若服务器连接信息或证书有变,请修改下方的 HOST / PORT / USER / PASSWORD。 """ import shlex import sys from pathlib import Path import paramiko # ── 服务器连接与版本配置 ─────────────────────────────── HOST = "172.207.81.145" PORT = 22 USER = "RRRosmontis" PASSWORD = "ftA406u@all#2309" VERSION = "202607192300" # ── 本地 / 远程路径 ──────────────────────────────────── LOCAL_EXE = Path("dist/Royirin Rosmontis Private Proxy.exe") REMOTE_DIR = f"/var/www/html/release/{VERSION}" REMOTE_EXE_NAME = "Royirin Rosmontis Private Proxy.exe" REMOTE_JSON = "/var/www/html/latest_version.json" def sudo_exec(client, command, password, timeout=30): """通过 sudo -S 执行远程命令,并从 stdin 传入密码。""" cmd = f"sudo -S bash -c {shlex.quote(command)}" stdin, stdout, stderr = client.exec_command(cmd, timeout=timeout) stdin.write(password + "\n") stdin.flush() stdin.channel.shutdown_write() exit_status = stdout.channel.recv_exit_status() out = stdout.read().decode("utf-8", errors="replace") err = stderr.read().decode("utf-8", errors="replace") return exit_status, out, err def main(): """主流程:上传 EXE 并更新服务器 latest_version.json。""" if not LOCAL_EXE.exists(): print(f"错误:本地文件不存在: {LOCAL_EXE}", file=sys.stderr) sys.exit(1) print(f"连接 {HOST}:{PORT} ...") client = paramiko.SSHClient() client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) client.connect(HOST, port=PORT, username=USER, password=PASSWORD) print("SSH 连接成功") try: # 1. 上传 EXE 到 /tmp remote_tmp = "/tmp/proxy.exe" print(f"上传 {LOCAL_EXE} -> {remote_tmp} ...") sftp = client.open_sftp() sftp.put(str(LOCAL_EXE), remote_tmp) sftp.close() print("上传完成") # 2. 创建发布目录并移动文件到最终位置 print(f"创建目录 {REMOTE_DIR} 并移动 EXE ...") cmd = f"mkdir -p {REMOTE_DIR} && mv {remote_tmp} '{REMOTE_DIR}/{REMOTE_EXE_NAME}'" status, out, err = sudo_exec(client, cmd, PASSWORD) if status != 0: print(f"移动文件失败: {err}", file=sys.stderr) sys.exit(1) print("文件已移动到发布目录") # 3. 更新 latest_version.json,客户端据此检测新版本 json_content = ( '{"latest_version":"' + VERSION + '",' '"download_url":"https://tunnel.rrrosmontis.icu/release/' + VERSION + '/Royirin%20Rosmontis%20Private%20Proxy.exe"}' ) print(f"更新 {REMOTE_JSON} ...") cmd = f"echo '{json_content}' | tee {REMOTE_JSON}" status, out, err = sudo_exec(client, cmd, PASSWORD) if status != 0: print(f"更新 JSON 失败: {err}", file=sys.stderr) sys.exit(1) print("latest_version.json 已更新") print(f"服务器返回: {out.strip()}") # 4. 验证远程 JSON 内容 print("验证远程文件...") stdin, stdout, stderr = client.exec_command(f"cat {REMOTE_JSON}") print(stdout.read().decode("utf-8").strip()) finally: client.close() print("连接已关闭") if __name__ == "__main__": main()