Files

164 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Upload WordPress deploy bundle and provision on the Nomadro server."""
from __future__ import annotations
import os
import secrets
import string
import sys
import time
from pathlib import Path
import paramiko
HOST = "107.173.30.245"
USER = "root"
PASSWORD = os.environ.get("NOMADRO_SSH_PASS", "")
REMOTE_DIR = "/var/www/nomadro-wp"
LOCAL_DIR = Path(__file__).resolve().parent
def gen_pass(n: int = 24) -> str:
alphabet = string.ascii_letters + string.digits
return "".join(secrets.choice(alphabet) for _ in range(n))
def safe_print(text: str) -> None:
try:
print(text)
except UnicodeEncodeError:
print(text.encode("utf-8", errors="replace").decode("utf-8", errors="replace"))
def run(ssh: paramiko.SSHClient, cmd: str, timeout: int = 600) -> str:
print(f"\n$ {cmd}")
stdin, stdout, stderr = ssh.exec_command(cmd, timeout=timeout, get_pty=True)
out = stdout.read().decode(errors="replace")
err = stderr.read().decode(errors="replace")
code = stdout.channel.recv_exit_status()
if out:
safe_print(out[-4000:])
if err.strip():
safe_print(err[-2000:])
if code != 0:
raise RuntimeError(f"Command failed ({code}): {cmd}")
return out
def sftp_mkdirs(sftp: paramiko.SFTPClient, remote: str) -> None:
parts = remote.strip("/").split("/")
cur = ""
for part in parts:
cur += "/" + part
try:
sftp.stat(cur)
except FileNotFoundError:
sftp.mkdir(cur)
def upload_tree(sftp: paramiko.SFTPClient, local: Path, remote: str) -> None:
sftp_mkdirs(sftp, remote)
for path in local.rglob("*"):
if path.name.startswith(".env") or path.name.endswith(".pyc"):
continue
if path.name == "remote_provision.py":
continue
rel = path.relative_to(local).as_posix()
target = f"{remote}/{rel}"
if path.is_dir():
sftp_mkdirs(sftp, target)
else:
sftp_mkdirs(sftp, str(Path(target).parent).replace("\\", "/"))
print(f"put {rel}")
sftp.put(str(path), target)
def main() -> int:
if not PASSWORD:
print("Set NOMADRO_SSH_PASS", file=sys.stderr)
return 1
mysql_pass = gen_pass()
mysql_root = gen_pass()
wp_admin = "Xiao4669805@"
wp_user = "hackrobot"
wp_email = "admin@nomadro.com"
env_text = (
f"MYSQL_PASSWORD={mysql_pass}\n"
f"MYSQL_ROOT_PASSWORD={mysql_root}\n"
f"WP_ADMIN_USER={wp_user}\n"
f"WP_ADMIN_PASSWORD={wp_admin}\n"
f"WP_ADMIN_EMAIL={wp_email}\n"
)
secrets_path = LOCAL_DIR / ".env.generated"
secrets_path.write_text(env_text, encoding="utf-8")
print(f"Wrote local secrets to {secrets_path} (do not commit)")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(
HOST,
username=USER,
password=PASSWORD,
timeout=30,
allow_agent=False,
look_for_keys=False,
)
sftp = ssh.open_sftp()
print("==> Upload wordpress bundle")
upload_tree(sftp, LOCAL_DIR, REMOTE_DIR)
with sftp.file(f"{REMOTE_DIR}/.env", "w") as f:
f.write(env_text)
sftp.chmod(f"{REMOTE_DIR}/install-docker.sh", 0o755)
sftp.chmod(f"{REMOTE_DIR}/bootstrap-wp.sh", 0o755)
sftp.close()
run(ssh, f"bash {REMOTE_DIR}/install-docker.sh", timeout=900)
run(ssh, f"cd {REMOTE_DIR} && docker compose pull", timeout=900)
run(ssh, f"cd {REMOTE_DIR} && docker compose up -d", timeout=300)
print("==> Wait for WP HTTP")
for _ in range(60):
stdin, stdout, stderr = ssh.exec_command(
"curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:8088 || true"
)
code = stdout.read().decode().strip()
if code.startswith("2") or code.startswith("3"):
print("WP HTTP", code)
break
time.sleep(3)
else:
raise RuntimeError("WordPress did not become ready on :8088")
# Preserve extra caddy site imports already on server
run(
ssh,
f"cp {REMOTE_DIR}/Caddyfile.adsense /etc/caddy/Caddyfile && "
"caddy validate --config /etc/caddy/Caddyfile && systemctl reload caddy",
)
run(ssh, f"cd {REMOTE_DIR} && bash bootstrap-wp.sh", timeout=900)
# Quick public check via local caddy
run(ssh, "curl -fsSI https://nomadro.com | head -20 || true")
run(
ssh,
"curl -fsS https://nomadro.com | tr '\\n' ' ' | grep -o '<title>[^<]*</title>' | head -1 || true",
)
print("\n=== CREDENTIALS (save now) ===")
print(f"WP admin user: {wp_user}")
print(f"WP admin pass: {wp_admin}")
print(f"WP admin URL: https://nomadro.com/wp-admin/")
print(f"Next.js portal: https://portal.nomadro.com (add DNS A -> {HOST})")
ssh.close()
return 0
if __name__ == "__main__":
raise SystemExit(main())