发布脚本:EzHub 走 HTTPS+令牌推送(SSH 还没配好)

EzHub 的 SSH 公钥在服务端一直被拒(Permission denied),
但令牌有 push 权限。所以发布脚本里 EzHub 改用
HTTPS + 令牌推,令牌只在进程内拼进 URL,不写进 .git/config。

顺带加了增量推送脚本 ezhub_push_all.py:
一次性推 44 个提交 = 117 MB,被 EzHub 的 nginx 413 拦了。
Git 推送是增量的,逐个提交推每包就小得多 —— 实测 44 个全部推成功。
脚本会先探测远端进度,只推缺的,并且对网络抖动自动重试。
This commit is contained in:
xmz7891
2026-10-07 12:32:11 +08:00
parent 9f9db53a06
commit f200240730
4 changed files with 360 additions and 2 deletions
+145
View File
@@ -0,0 +1,145 @@
# -*- coding: utf-8 -*-
r'''带重试的增量推送 —— 把 44 个提交逐个推到 EzHub。
为什么逐个推:
一次性推 44 个提交 = 117 MB,EzHub 的 nginx 返回 413。
但 Git 推送是"增量"的:第一个提交推成功后,
后面每个提交只需要传自己新增的对象。
逐个推,每包只有几十 KB ~ 几 MB,就不会撞上限。
为什么带重试:
这台网络(4G + 家宽)会偶发连不上 git.261826.xyz。
实测推第 5 个提交时报 "Failed to connect" —— 纯网络抖动,
重试就好。不加重试会白费前面推成功的部分(虽然它们已经进仓库了,
但脚本会误以为整体失败)。
已经推上去的提交会自动跳过(push 会说 Everything up-to-date)。
'''
from __future__ import annotations
import subprocess
import sys
import time
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
GIT = r"C:\Program Files\Git\bin\git.exe"
TOKEN_FILE = Path.home() / ".ykxmz" / "ezhub_token"
OWNER, REPO, HOST = "xmz", "ykxmz", "git.261826.xyz"
MAX_TRIES = 4
RETRY_WAIT = 6 # 秒,每次重试前等这么久
def git(*args: str) -> subprocess.CompletedProcess:
return subprocess.run([GIT] + list(args), cwd=str(ROOT),
capture_output=True, text=True,
encoding="utf-8", errors="replace")
def remote_main(url: str) -> str:
"""取远端 main 指向哪个提交(空仓库返回空串)。"""
r = subprocess.run([GIT, "ls-remote", url, "refs/heads/main"],
cwd=str(ROOT), capture_output=True, text=True,
encoding="utf-8", errors="replace", timeout=120)
line = (r.stdout or "").strip()
return line.split()[0] if line else ""
def push_one(url: str, sha: str, token: str) -> tuple:
"""推一个提交。返回 (成功?, 说明)。
⚠ 如果远端已经超过这个提交了,再推它就是"回退",
Git 会拒绝(non-fast-forward)。这种情况视为成功(跳过),
因为它的内容已经在远端的历史里了。
"""
last = ""
for attempt in range(1, MAX_TRIES + 1):
r = subprocess.run(
[GIT, "push", url, "%s:refs/heads/main" % sha],
cwd=str(ROOT), capture_output=True, text=True,
encoding="utf-8", errors="replace", timeout=900)
err = (r.stderr or r.stdout or "").replace(token, "***")
if r.returncode == 0:
if "Everything up-to-date" in err:
return True, "已是最新"
return True, "ok"
last = [ln for ln in err.strip().splitlines() if ln.strip()]
last = last[-1][:110] if last else "?"
# 远端已经往前走了 -> 这个提交已经在历史里,跳过
if "non-fast-forward" in err or "fast-forwards" in err:
return True, "远端已包含(跳过)"
# 只对"能重试"的错误重试:网络抖动 / 5xx
retryable = any(k in last for k in (
"Failed to connect", "Connection reset", "timed out",
"Could not resolve", "RPC failed", "unexpected disconnect",
" 500", " 502", " 503", " 504"))
if not retryable or attempt == MAX_TRIES:
return False, "第 %d 次: %s" % (attempt, last)
print(" 重试 %d/%d(%s)" % (attempt, MAX_TRIES, last[:50]))
time.sleep(RETRY_WAIT)
return False, last
def main() -> int:
if not TOKEN_FILE.is_file():
print(" [失败] 没有令牌,先跑 ezHub_push.py <令牌>")
return 1
token = TOKEN_FILE.read_text(encoding="utf-8").strip()
url = "https://%s:%s@%s/%s/%s.git" % (OWNER, token, HOST, OWNER, REPO)
out = git("rev-list", "--reverse", "HEAD").stdout or ""
commits = [c for c in out.splitlines() if c.strip()]
total = len(commits)
head = remote_main(url)
if head:
# 远端已经有的那些直接跳过
ahead = git("rev-list", "--reverse", "%s..HEAD" % head).stdout or ""
todo = [c for c in ahead.splitlines() if c.strip()]
print(" 远端 main 已在 %s" % head[:12])
print(" 共 %d 个提交,其中 %d 个已经在远端,需要推 %d 个"
% (total, total - len(todo), len(todo)))
else:
todo = commits
print(" 远端还是空的")
print(" 共 %d 个提交,需要全部推" % total)
print()
if not todo:
print(" ✓ 远端已经是最新,不用推")
done = 0
for index, sha in enumerate(todo, 1):
ok, why = push_one(url, sha, token)
if ok:
done = index
if index % 5 == 0 or index == len(todo):
print(" %3d/%d ✓" % (index, len(todo)))
else:
print(" %3d/%d ✗ %s" % (index, len(todo), why))
r = git("show", "--stat", "--oneline", sha)
print(" 这个提交改了什么:")
for line in (r.stdout or "").strip().splitlines()[:8]:
print(" " + line[:95])
print()
print(" 这一轮推到第 %d 个,还剩 %d 个"
% (done, len(todo) - done))
print(" 再跑一次本脚本会从断的地方继续。")
return 1
print()
print(" ✓ 这一轮全部推完" % ())
# 确认
remote_sha = remote_main(url)
local_sha = git("rev-parse", "HEAD").stdout.strip()
print(" 远端 main: %s" % remote_sha[:12])
print(" 本地 HEAD: %s" % local_sha[:12])
print(" %s" % ("✓ 一致" if remote_sha == local_sha else "✗ 不一致"))
return 0 if remote_sha == local_sha else 1
if __name__ == "__main__":
sys.exit(main())