198 lines
6.1 KiB
Python
198 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Commit & push dirty repos (local + server) with eric credentials, deploy douyinWEB ops."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import paramiko
|
|
|
|
HOST = "107.173.30.245"
|
|
PASS = "Xiao4669805"
|
|
GIT_USER = "eric"
|
|
GIT_PASS = "Xiao4669805"
|
|
AUTH = f"{GIT_USER}:{GIT_PASS}@"
|
|
|
|
DOUYIN = Path(r"C:\Users\admin\Desktop\douyinWEB")
|
|
NOMADRO = Path(r"C:\project\gitlab-instance-0a899031_nomadro")
|
|
|
|
|
|
def local(cmd: list[str] | str, cwd: Path) -> str:
|
|
print(f"\n[{cwd.name}] $", cmd if isinstance(cmd, str) else " ".join(cmd))
|
|
r = subprocess.run(
|
|
cmd,
|
|
cwd=str(cwd),
|
|
shell=isinstance(cmd, str),
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
)
|
|
out = (r.stdout or "") + (r.stderr or "")
|
|
print(out[-3000:] if len(out) > 3000 else out)
|
|
if r.returncode != 0:
|
|
raise RuntimeError(f"cmd failed in {cwd}: {cmd}\n{out}")
|
|
return out
|
|
|
|
|
|
def ensure_auth_remote(cwd: Path) -> None:
|
|
url = local(["git", "remote", "get-url", "origin"], cwd).strip()
|
|
if "gitea.dsx2020.com" in url and AUTH not in url:
|
|
if url.startswith("https://"):
|
|
new = url.replace("https://", f"https://{AUTH}", 1)
|
|
# strip existing userinfo
|
|
if "@gitea" in url:
|
|
new = "https://" + AUTH + url.split("@", 1)[1]
|
|
local(["git", "remote", "set-url", "origin", new], cwd)
|
|
|
|
|
|
def commit_push_local(cwd: Path, paths: list[str], message: str) -> None:
|
|
ensure_auth_remote(cwd)
|
|
local(["git", "add", *paths], cwd)
|
|
st = local(["git", "status", "--porcelain"], cwd)
|
|
if not st.strip():
|
|
print(f"[{cwd.name}] nothing to commit")
|
|
# still try push in case ahead
|
|
local(["git", "push", "-u", "origin", "HEAD"], cwd)
|
|
return
|
|
local(["git", "commit", "-m", message], cwd)
|
|
local(["git", "push", "-u", "origin", "HEAD"], cwd)
|
|
|
|
|
|
def main() -> None:
|
|
# 1) Local douyinWEB — ops page
|
|
commit_push_local(
|
|
DOUYIN,
|
|
[
|
|
"frontend/src/app/ops/page.tsx",
|
|
"frontend/src/components/Shell.tsx",
|
|
"frontend/src/app/layout.tsx",
|
|
"backend/app/ops_panel.py",
|
|
"backend/app/notify.py",
|
|
"backend/app/main.py",
|
|
],
|
|
"feat(ops): Umami/ntfy/DNS 运维控制台页面与 API",
|
|
)
|
|
|
|
# 2) Local nomadro — observability stack
|
|
commit_push_local(
|
|
NOMADRO,
|
|
[
|
|
"app/layout.tsx",
|
|
"components/umami-analytics.tsx",
|
|
"deploy/Caddyfile",
|
|
"deploy/README.md",
|
|
"deploy/caddy-sites",
|
|
"deploy/umami",
|
|
"deploy/ntfy",
|
|
"deploy/observability",
|
|
"deploy/router",
|
|
"deploy/wordpress",
|
|
"deploy/deploy.sh",
|
|
"deploy/setup-server.sh",
|
|
],
|
|
"feat: Umami/ntfy 可观测性栈与 WordPress AdSense 部署",
|
|
)
|
|
|
|
# 3) Server repos: commit layout umami injections + sync + deploy douyin
|
|
c = paramiko.SSHClient()
|
|
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
c.connect(HOST, username="root", password=PASS, timeout=30, allow_agent=False, look_for_keys=False)
|
|
|
|
def run(cmd: str, timeout: int = 600) -> str:
|
|
print("\n$ ", cmd[:160])
|
|
_i, o, e = c.exec_command(cmd, timeout=timeout)
|
|
out = (o.read() + e.read()).decode("utf-8", errors="replace")
|
|
print(out[-3500:] if len(out) > 3500 else out)
|
|
return out
|
|
|
|
# Ensure remotes have credentials
|
|
for repo in ("/opt/douyinWEB", "/opt/tiktokWEB", "/opt/wauto", "/var/www/nomadro.com"):
|
|
run(
|
|
f"""
|
|
cd {repo} || exit 0
|
|
url=$(git remote get-url origin 2>/dev/null || true)
|
|
[ -z "$url" ] && exit 0
|
|
case "$url" in
|
|
*{AUTH}*) ;;
|
|
https://*@gitea.dsx2020.com/*)
|
|
rest=$(echo "$url" | sed 's|https://[^@]*@|https://|')
|
|
git remote set-url origin "https://{AUTH}${{rest#https://}}"
|
|
;;
|
|
https://gitea.dsx2020.com/*)
|
|
git remote set-url origin "https://{AUTH}${{url#https://}}"
|
|
;;
|
|
esac
|
|
git remote -v | head -2
|
|
"""
|
|
)
|
|
|
|
# Pull douyin first then apply — better: push already done, pull on server
|
|
run(
|
|
"""
|
|
cd /opt/douyinWEB
|
|
git fetch origin
|
|
git stash push -u -m 'pre-ops-deploy' -- frontend/src/app/layout.tsx 2>/dev/null || true
|
|
git checkout test1
|
|
git pull --ff-only origin test1
|
|
""",
|
|
timeout=120,
|
|
)
|
|
|
|
# Commit other server-only dirty files (tiktok/wauto/nomadro layouts) if still dirty
|
|
run(
|
|
r"""
|
|
commit_if_dirty() {
|
|
local dir="$1"; local msg="$2"; shift 2
|
|
cd "$dir" || return 0
|
|
git add "$@" 2>/dev/null || true
|
|
if [ -n "$(git status --porcelain -- "$@" 2>/dev/null)" ]; then
|
|
git -c user.email=ops@nomadro.com -c user.name=ops commit -m "$msg" || true
|
|
git push origin HEAD || true
|
|
else
|
|
echo "$dir: clean for selected paths"
|
|
fi
|
|
}
|
|
commit_if_dirty /opt/tiktokWEB 'chore: inject Umami analytics tracker' frontend/src/app/layout.tsx
|
|
commit_if_dirty /opt/wauto 'chore: inject Umami analytics tracker' web/app/layout.tsx
|
|
commit_if_dirty /var/www/nomadro.com 'chore: Umami analytics component' app/layout.tsx components/umami-analytics.tsx
|
|
"""
|
|
)
|
|
|
|
# Deploy douyinWEB frontend + restart API
|
|
run(
|
|
"""
|
|
cd /opt/douyinWEB/frontend
|
|
# ensure umami env
|
|
grep -q '^NEXT_PUBLIC_UMAMI_WEBSITE_ID=' .env.local 2>/dev/null || echo 'NEXT_PUBLIC_UMAMI_WEBSITE_ID=f18d3484-be2e-4344-8882-b80c5b53f9cf' >> .env.local
|
|
npm run build
|
|
systemctl restart douyinweb-web
|
|
systemctl restart douyinweb-api
|
|
sleep 2
|
|
systemctl is-active douyinweb-web douyinweb-api
|
|
curl -fsS -o /dev/null -w 'ops_page:%{http_code}\\n' https://dylive.nomadro.com/ops || true
|
|
curl -fsS -o /dev/null -w 'ops_api:%{http_code}\\n' -H 'Cookie: ' https://dylive.nomadro.com/api/ops || true
|
|
""",
|
|
timeout=600,
|
|
)
|
|
|
|
# Sync nomadro.com from git if needed
|
|
run(
|
|
"""
|
|
cd /var/www/nomadro.com
|
|
git fetch origin
|
|
git pull --ff-only origin test || git pull --ff-only || true
|
|
# copy observability deploy assets already in repo; ensure router files synced if present
|
|
""",
|
|
timeout=120,
|
|
)
|
|
|
|
c.close()
|
|
print("\nALL DONE")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|