Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants. Co-authored-by: Cursor <cursoragent@cursor.com>
121 lines
3.3 KiB
Python
121 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Simple SSH helper for deployment (paramiko)."""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import base64
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import paramiko
|
|
|
|
|
|
def connect(host: str, user: str, password: str) -> paramiko.SSHClient:
|
|
client = paramiko.SSHClient()
|
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
client.connect(
|
|
host,
|
|
username=user,
|
|
password=password,
|
|
timeout=30,
|
|
allow_agent=False,
|
|
look_for_keys=False,
|
|
)
|
|
return client
|
|
|
|
|
|
def _exec(client: paramiko.SSHClient, command: str, timeout: int = 120) -> tuple[int, str, str]:
|
|
stdin, stdout, stderr = client.exec_command(command, timeout=timeout, get_pty=True)
|
|
out = stdout.read().decode(errors="replace")
|
|
err = stderr.read().decode(errors="replace")
|
|
code = stdout.channel.recv_exit_status()
|
|
return code, out, err
|
|
|
|
|
|
def run(
|
|
host: str,
|
|
user: str,
|
|
password: str,
|
|
command: str,
|
|
*,
|
|
sudo: bool = False,
|
|
timeout: int = 120,
|
|
) -> int:
|
|
client = connect(host, user, password)
|
|
try:
|
|
if sudo:
|
|
b64 = base64.b64encode(command.encode()).decode()
|
|
full = (
|
|
f"echo {password!r} | sudo -S -p '' bash -c "
|
|
f"'echo {b64} | base64 -d | bash'"
|
|
)
|
|
else:
|
|
full = f"bash -lc {command!r}"
|
|
code, out, err = _exec(client, full, timeout=timeout)
|
|
sys.stdout.write(out)
|
|
if err.strip():
|
|
sys.stderr.write(err)
|
|
return code
|
|
finally:
|
|
client.close()
|
|
|
|
|
|
def upload(
|
|
host: str,
|
|
user: str,
|
|
password: str,
|
|
local: str,
|
|
remote: str,
|
|
*,
|
|
sudo: bool = False,
|
|
) -> int:
|
|
data = Path(local).read_bytes()
|
|
b64 = base64.b64encode(data).decode()
|
|
remote_q = remote.replace("'", "'\\''")
|
|
write_cmd = f"mkdir -p \"$(dirname '{remote_q}')\" && echo {b64} | base64 -d > '{remote_q}'"
|
|
return run(host, user, password, write_cmd, sudo=sudo, timeout=300)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
sub = parser.add_subparsers(dest="action", required=True)
|
|
|
|
p_run = sub.add_parser("run")
|
|
p_run.add_argument("--host", required=True)
|
|
p_run.add_argument("--user", required=True)
|
|
p_run.add_argument("--password", required=True)
|
|
p_run.add_argument("--sudo", action="store_true")
|
|
p_run.add_argument("--timeout", type=int, default=120)
|
|
p_run.add_argument("command")
|
|
|
|
p_up = sub.add_parser("upload")
|
|
p_up.add_argument("--host", required=True)
|
|
p_up.add_argument("--user", required=True)
|
|
p_up.add_argument("--password", required=True)
|
|
p_up.add_argument("--local", required=True)
|
|
p_up.add_argument("--remote", required=True)
|
|
p_up.add_argument("--sudo", action="store_true")
|
|
|
|
args = parser.parse_args()
|
|
if args.action == "run":
|
|
return run(
|
|
args.host,
|
|
args.user,
|
|
args.password,
|
|
args.command,
|
|
sudo=args.sudo,
|
|
timeout=args.timeout,
|
|
)
|
|
return upload(
|
|
args.host,
|
|
args.user,
|
|
args.password,
|
|
args.local,
|
|
args.remote,
|
|
sudo=args.sudo,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|