import getpass import hashlib from datetime import datetime, timezone from pathlib import Path import paramiko root = Path(__file__).resolve().parent.parent client = paramiko.SSHClient() client.load_host_keys(str(Path.home() / ".ssh" / "known_hosts")) client.connect("47.108.78.12", username="root", password=getpass.getpass("SSH password: "), timeout=20) def run(command): print(command, flush=True) _, stdout, stderr = client.exec_command(command, timeout=120) output = stdout.read().decode() + stderr.read().decode() print(output, flush=True) if stdout.channel.recv_exit_status(): raise RuntimeError("部署命令失败,保留已有版本和备份") return output.strip() switched = False previous = "" try: run("nginx -t && systemctl is-active nginx && df -h /opt && ss -ltnp '( sport = :9999 or sport = :19999 )'") previous = run("readlink -f /opt/fdm/current || true") stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ") release = f"/opt/fdm/releases/{stamp}" backup = f"/var/backups/fdm/{stamp}" run(f'install -d -m 755 {release} {backup} && cp -a /etc/nginx/conf.d/fdm-cdis.conf {backup}/') sftp = client.open_sftp() binary = root / "artifacts/fdm-studio" sftp.put(str(binary), release + "/fdm-studio") sftp.chmod(release + "/fdm-studio", 0o755) expected = hashlib.sha256(binary.read_bytes()).hexdigest() run(f"echo '{expected} {release}/fdm-studio' | sha256sum -c -") sftp.put(str(root / "deploy/fdm-studio.service"), "/etc/systemd/system/fdm-studio.service") sftp.put(str(root / 'deploy/nginx-cdis-http.conf'), release + '/nginx-http.conf') run("systemctl daemon-reload") run(f"systemd-run --unit=fdm-candidate-{stamp.lower()} --property=DynamicUser=yes {release}/fdm-studio -listen 127.0.0.1:19999") run("sleep 2 && curl -fsS http://127.0.0.1:19999/healthz") run(f"systemctl stop fdm-candidate-{stamp.lower()}") switched = True run(f"ln -sfn {release} /opt/fdm/current") run(f'cp {release}/nginx-http.conf /etc/nginx/conf.d/fdm-cdis.conf && nginx -t && systemctl reload nginx') run("sleep 3 && systemctl enable fdm-studio && systemctl restart fdm-studio && sleep 3 && systemctl is-active fdm-studio") run('curl -fsS http://127.0.0.1:9999/healthz && curl -fsS --resolve cdis.cn:443:127.0.0.1 https://cdis.cn/healthz') run("systemctl is-enabled certbot.timer || true") sftp.close() print("部署完成:" + release, flush=True) except Exception: if switched: print("切换验证失败,恢复先前配置", flush=True) run("systemctl stop fdm-studio || true") if previous: run(f"ln -sfn {previous} /opt/fdm/current && systemctl start fdm-studio") run(f'cp {backup}/fdm-cdis.conf /etc/nginx/conf.d/fdm-cdis.conf && nginx -t && systemctl reload nginx') raise finally: client.close()