nomadweb/scripts/deploy_direct_sync.py
eric fd87f765c8 Close discover-decide-plan loop: matcher to trip, shareable /compare hub.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 02:58:40 -05:00

134 lines
3.8 KiB
Python

"""Direct SFTP sync + native frontend rebuild (when Gitea push fails)."""
from __future__ import annotations
import sys
import time
from pathlib import Path
import paramiko
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
HOST = "107.173.30.245"
USER = "root"
PASS = "Xiao4669805"
REMOTE = "/opt/nomadweb"
DOMAIN = "nomadweb.nomadro.com"
FILES = [
"README.md",
"frontend/src/app/changelog/page.tsx",
"frontend/src/app/globals.css",
"frontend/src/app/profile/page.tsx",
"frontend/src/app/plan/page.tsx",
"frontend/src/app/compare/page.tsx",
"frontend/src/components/DestinationDetailClient.tsx",
"frontend/src/components/DestinationMatcher.tsx",
"frontend/src/components/CompareModal.tsx",
"frontend/src/components/CompareClient.tsx",
"frontend/src/components/GlobalSearch.tsx",
"frontend/src/components/HomeClient.tsx",
"frontend/src/components/KeyboardShortcuts.tsx",
"frontend/src/components/Navbar.tsx",
"frontend/src/components/TripPlanner.tsx",
"frontend/src/components/WorldMap.tsx",
"frontend/src/components/MovePlanClient.tsx",
"frontend/src/lib/types.ts",
"frontend/src/lib/planMeta.ts",
"frontend/src/lib/compareScore.ts",
"frontend/src/lib/tripActions.ts",
"frontend/src/lib/matcherPrefs.ts",
]
BUILD = f"""
set -euo pipefail
exec 9>/var/lock/nomadro-deploy.lock
if ! flock -n 9; then
echo LOCK_BUSY
exit 75
fi
REMOTE={REMOTE}
DOMAIN={DOMAIN}
export NEXT_TELEMETRY_DISABLED=1
export NEXT_PUBLIC_API_URL=https://${{DOMAIN}}/api/v1
export NEXT_PUBLIC_SITE_URL=https://${{DOMAIN}}
export NODE_ENV=production
cd "$REMOTE/frontend"
echo BUILD_START
npm run build
mkdir -p .next/standalone/.next
rm -rf .next/standalone/public .next/standalone/.next/static
cp -a public .next/standalone/public
cp -a .next/static .next/standalone/.next/static
systemctl restart nomadro-web
ok=0
for i in $(seq 1 30); do
web=$(curl -s -o /dev/null -w '%{{http_code}}' http://127.0.0.1:3055/ || true)
api=$(curl -s http://127.0.0.1:8055/api/v1/health || true)
echo "try=$i web=$web api=$api"
if echo "$api" | grep -q '"status":"ok"' && [ "$web" = "200" ]; then
ok=1
break
fi
sleep 1
done
curl -skI "https://${{DOMAIN}}/plan" | head -8
curl -sk "https://${{DOMAIN}}/api/v1/health"
echo DIRECT_SYNC=1
[ "$ok" = 1 ]
"""
def ensure_dir(sftp: paramiko.SFTPClient, path: str) -> None:
parts = path.strip("/").split("/")
cur = ""
for p in parts:
cur += "/" + p
try:
sftp.stat(cur)
except FileNotFoundError:
try:
sftp.mkdir(cur)
except OSError:
pass
def main() -> int:
root = Path(__file__).resolve().parents[1]
t0 = time.time()
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print("Connecting...")
client.connect(HOST, username=USER, password=PASS, timeout=30)
sftp = client.open_sftp()
for rel in FILES:
local = root / rel
remote = f"{REMOTE}/{rel}"
print(f"UPLOAD {rel}")
ensure_dir(sftp, str(Path(remote).parent).replace("\\", "/"))
sftp.put(str(local), remote)
sftp.close()
print("Uploaded. Building frontend...")
_, stdout, stderr = client.exec_command(
f"cat > /tmp/nomadro-direct.sh <<'EOF'\n{BUILD}\nEOF\nbash /tmp/nomadro-direct.sh",
timeout=900,
)
while True:
line = stdout.readline()
if not line:
break
print(line, end="")
err = stderr.read().decode("utf-8", errors="replace")
code = stdout.channel.recv_exit_status()
if err.strip():
print("LOG:", err[-4000:])
print("exit:", code)
client.close()
print(f"Done in {int(time.time() - t0)}s")
return code
if __name__ == "__main__":
raise SystemExit(main())