2a29181a90
Co-authored-by: Cursor <cursoragent@cursor.com>
89 lines
2.8 KiB
Python
89 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
||
"""从本机 SSH 触发服务器一键更新(服务器内 git pull,不 scp 代码)。
|
||
|
||
环境变量(可选):
|
||
DEPLOY_HOST 默认 47.236.184.99
|
||
DEPLOY_USER 默认 root
|
||
DEPLOY_PASS 服务器密码
|
||
DEPLOY_ROOT 默认 /opt/eth_hedge_sim
|
||
REPO_URL 默认 https://git.bz121.com/dekun/eth_hedge_sim.git
|
||
DEPLOY_MODE update|bootstrap 默认 update(跑 pull_and_restart)
|
||
bootstrap 时执行 manage 非交互更新路径
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import sys
|
||
|
||
HOST = os.environ.get("DEPLOY_HOST", "47.236.184.99")
|
||
USER = os.environ.get("DEPLOY_USER", "root")
|
||
PASSWORD = os.environ.get("DEPLOY_PASS", "")
|
||
ROOT = os.environ.get("DEPLOY_ROOT", "/opt/eth_hedge_sim")
|
||
REPO = os.environ.get("REPO_URL", "https://git.bz121.com/dekun/eth_hedge_sim.git")
|
||
MODE = os.environ.get("DEPLOY_MODE", "update").strip().lower()
|
||
|
||
|
||
def main() -> int:
|
||
if not PASSWORD:
|
||
print("Set DEPLOY_PASS env var (do not commit password).", file=sys.stderr)
|
||
return 2
|
||
try:
|
||
import paramiko
|
||
except ImportError:
|
||
print("pip install paramiko", file=sys.stderr)
|
||
return 2
|
||
|
||
if MODE == "bootstrap":
|
||
remote = f"""
|
||
set -euo pipefail
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
if [[ ! -d '{ROOT}/.git' ]]; then
|
||
mkdir -p /opt
|
||
git clone '{REPO}' '{ROOT}'
|
||
fi
|
||
bash '{ROOT}/deploy/pull_and_restart.sh'
|
||
curl -fsS http://127.0.0.1:5155/health || true
|
||
"""
|
||
else:
|
||
remote = f"""
|
||
set -euo pipefail
|
||
if [[ ! -d '{ROOT}/.git' ]]; then
|
||
mkdir -p /opt
|
||
git clone '{REPO}' '{ROOT}'
|
||
fi
|
||
bash '{ROOT}/deploy/lib/update.sh'
|
||
curl -fsS http://127.0.0.1:5155/health || true
|
||
"""
|
||
|
||
client = paramiko.SSHClient()
|
||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
print(f"connecting {USER}@{HOST} ...")
|
||
client.connect(HOST, username=USER, password=PASSWORD, timeout=30)
|
||
print("running remote pull/update ...")
|
||
stdin, stdout, stderr = client.exec_command(remote, get_pty=True)
|
||
while True:
|
||
chunk = stdout.read(1024)
|
||
if not chunk:
|
||
break
|
||
text = chunk.decode("utf-8", errors="replace") if isinstance(chunk, bytes) else chunk
|
||
try:
|
||
print(text, end="", flush=True)
|
||
except UnicodeEncodeError:
|
||
print(text.encode("ascii", errors="replace").decode("ascii"), end="", flush=True)
|
||
err_raw = stderr.read()
|
||
err = err_raw.decode("utf-8", errors="replace") if isinstance(err_raw, bytes) else err_raw
|
||
code = stdout.channel.recv_exit_status()
|
||
if err:
|
||
try:
|
||
print(err, file=sys.stderr)
|
||
except UnicodeEncodeError:
|
||
print(err.encode("ascii", errors="replace").decode("ascii"), file=sys.stderr)
|
||
client.close()
|
||
print(f"exit={code}")
|
||
return code
|
||
|
||
|
||
if __name__ == "__main__":
|
||
raise SystemExit(main())
|