guanghulab/scripts/sync-to-sg.py

157 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
光湖直连同步器 · 本地→新加坡
绕过代理,直连新加坡 Gatekeeper分 chunk 传输文件到代码仓库。
用法: python3 sync-to-sg.py --file brain/xxx.hdlp --target brain/xxx.hdlp --msg "feat: xxx"
"""
import argparse
import base64
import json
import os
import subprocess
import sys
import time
GK_URL = "http://43.156.237.110:3911/exec"
GK_KEY = "zy_gtw_c38be341d65043eee0ba51a214a267832a9ae46dd73e423c"
REPO_PATH = "/opt/zhuyuan/guanghulab"
CHUNK_SIZE = 6000 # bytes新加坡 gatekeeper 安全上限
CMD_TIMEOUT = 30 # 每条命令超时秒数
def gk_exec(cmd: str) -> dict:
"""通过 Gatekeeper 执行命令,绕过代理直连"""
env = os.environ.copy()
env.pop("http_proxy", None)
env.pop("https_proxy", None)
env.pop("HTTP_PROXY", None)
env.pop("HTTPS_PROXY", None)
result = subprocess.run(
["curl", "-s", "--connect-timeout", str(CMD_TIMEOUT),
"-X", "POST", GK_URL,
"-H", "Content-Type: application/json",
"-H", f"Authorization: Bearer {GK_KEY}",
"-d", json.dumps({"cmd": cmd})],
capture_output=True, text=True, timeout=CMD_TIMEOUT + 5, env=env
)
try:
return json.loads(result.stdout)
except json.JSONDecodeError:
return {"ok": False, "error": f"gatekeeper 返回非 JSON: {result.stdout[:200]}", "stderr": result.stderr}
def sync_file(local_path: str, target_path: str, commit_msg: str) -> bool:
"""同步单个文件到新加坡仓库"""
# 读文件
with open(local_path, "rb") as f:
content = f.read()
expected_bytes = len(content)
b64 = base64.b64encode(content).decode("ascii")
print(f"[sync] {local_path}{target_path} ({expected_bytes} bytes, {len(b64)} b64 chars)")
# 清空临时目录
r = gk_exec(f"rm -f /tmp/sync_chunk_* /tmp/sync_b64.txt")
if not r.get("ok"):
print(f"[error] 清空临时目录失败: {r}")
return False
# 分 chunk 上传
chunks = [b64[i:i+CHUNK_SIZE] for i in range(0, len(b64), CHUNK_SIZE)]
print(f"[sync] 分 {len(chunks)} chunks 上传...")
for i, chunk in enumerate(chunks):
# base64 编码 chunk 内容避免 shell 转义问题
chunk_b64 = base64.b64encode(chunk.encode()).decode()
cmd = f'echo "{chunk_b64}" | base64 -d >> /tmp/sync_b64.txt'
r = gk_exec(cmd)
if not r.get("ok"):
print(f"[error] chunk {i+1}/{len(chunks)} 上传失败: {r}")
return False
print(f" chunk {i+1}/{len(chunks)}")
# 解码并写入
full_target = f"{REPO_PATH}/{target_path}"
target_dir = os.path.dirname(full_target)
write_cmd = (
f"mkdir -p {target_dir} && "
f"base64 -d /tmp/sync_b64.txt > {full_target} && "
f"echo BYTES=$(wc -c < {full_target})"
)
r = gk_exec(write_cmd)
if not r.get("ok"):
print(f"[error] 写入失败: {r}")
return False
# 校验字节数
stdout = r.get("stdout", "")
if f"BYTES={expected_bytes}" not in stdout:
actual = stdout.replace("BYTES=", "").strip()
print(f"[error] 字节校验失败: 期望={expected_bytes}, 实际={actual}")
return False
print(f"[sync] 写入+校验通过 ({expected_bytes} bytes)")
# Git 提交pull→commit→push
escaped_msg = commit_msg.replace('"', '\\"').replace("'", "\\'")
git_cmd = (
f"cd {REPO_PATH} && "
f"git config user.email zhuyuan@guanghulab.com && "
f"git config user.name '铸渊 ICE-GL-ZY001' && "
f"git pull --rebase origin main 2>&1; "
f"git add {target_path} && "
f"GIT_OUT=$(git commit -m \"{escaped_msg}\" 2>&1); echo \"COMMIT: $GIT_OUT\"; "
f"if echo \"$GIT_OUT\" | grep -q 'nothing to commit'; then echo 'NO_CHANGE'; else git push origin main 2>&1; fi"
)
r = gk_exec(git_cmd)
stdout = r.get("stdout", "")
stderr = r.get("stderr", "")
if "NO_CHANGE" in stdout:
print(f"[sync] 文件无变更,跳过 commit+push")
elif r.get("ok"):
print(f"[sync] git pull + commit + push 成功")
if stdout:
print(f" 输出: {stdout[:300]}")
else:
# git 可能返回非零但实际成功了committer 警告等)
if "COMMIT:" in stdout and "nothing to commit" not in stdout:
print(f"[sync] git 操作完成(有警告但 commit 已执行)")
else:
print(f"[error] git 操作失败: stdout={stdout[:300]}, stderr={stderr[:200]}")
return False
# 清理
gk_exec("rm -f /tmp/sync_chunk_* /tmp/sync_b64.txt")
return True
def main():
parser = argparse.ArgumentParser(description="光湖直连同步器 · 本地→新加坡")
parser.add_argument("--file", required=True, help="本地文件路径")
parser.add_argument("--target", required=True, help="目标路径(相对于仓库根)")
parser.add_argument("--msg", required=True, help="commit message")
args = parser.parse_args()
if not os.path.exists(args.file):
print(f"[error] 文件不存在: {args.file}")
sys.exit(1)
print(f"" * 50)
print(f"光湖直连同步器 · 本地→新加坡 BS-SG-001")
print(f"" * 50)
start = time.time()
ok = sync_file(args.file, args.target, args.msg)
elapsed = time.time() - start
if ok:
print(f"✅ 同步完成 ({elapsed:.1f}s)")
else:
print(f"❌ 同步失败 ({elapsed:.1f}s)")
sys.exit(1)
if __name__ == "__main__":
main()