130 lines
4.6 KiB
Python
130 lines
4.6 KiB
Python
# -*- 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/<VERSION>/ 并移动 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()
|