mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 13:40:45 +00:00
发布脚本: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:
@@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''摸出 EzHub 的推送体积上限,并尝试增量推送。
|
||||
|
||||
背景:
|
||||
一次性推 39 个提交 = 117 MB,EzHub 返回 413(nginx 的
|
||||
client_max_body_size 拦了)。Gitee 上限大,所以一直没事。
|
||||
|
||||
思路:
|
||||
第一个提交推成功后,后面每个提交只需要传"新增的对象"。
|
||||
所以逐个提交推,每包就小得多。
|
||||
但如果某个提交本身就加了个 35 MB 的二进制,那一包照样超限 ——
|
||||
这里会把它找出来。
|
||||
'''
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
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"
|
||||
|
||||
|
||||
def git(*args: str, timeout: int = 600) -> subprocess.CompletedProcess:
|
||||
return subprocess.run([GIT] + list(args), cwd=str(ROOT),
|
||||
capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace",
|
||||
timeout=timeout)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
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()]
|
||||
print(" 共 %d 个提交" % len(commits))
|
||||
|
||||
# 先看每个提交自身新增了多大的对象
|
||||
print()
|
||||
print(" 各提交新增的对象体积(找超大的):")
|
||||
big = []
|
||||
for index, sha in enumerate(commits, 1):
|
||||
# 这个提交相对父提交新增的 blob
|
||||
r = git("diff-tree", "-r", "--no-commit-id", "--objects",
|
||||
"-r", sha)
|
||||
entries = [ln for ln in (r.stdout or "").splitlines() if ln.strip()]
|
||||
if not entries:
|
||||
continue
|
||||
payload = "\n".join(ln.split(" ", 1)[0] for ln in entries)
|
||||
c = subprocess.run(
|
||||
[GIT, "cat-file", "--batch-check=%(objecttype) %(objectsize) "
|
||||
"%(objectname)"],
|
||||
cwd=str(ROOT), input=payload, capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace")
|
||||
total = 0
|
||||
for line in (c.stdout or "").splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3 and parts[0] == "blob":
|
||||
total += int(parts[1])
|
||||
if total > 1024 * 1024:
|
||||
msg = git("log", "-1", "--format=%s", sha).stdout.strip()[:50]
|
||||
big.append((total, sha[:8], msg))
|
||||
big.sort(reverse=True)
|
||||
for total, sha, msg in big[:12]:
|
||||
print(" %8.1f MB %s %s" % (total / 1024 / 1024, sha, msg))
|
||||
if not big:
|
||||
print(" (没有超过 1 MB 的)")
|
||||
|
||||
# 逐个推
|
||||
print()
|
||||
print(" 逐个提交推(每包只含新增对象):")
|
||||
ok_until = 0
|
||||
failed_at = None
|
||||
for index, sha in enumerate(commits, 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:
|
||||
ok_until = index
|
||||
if index % 5 == 0 or index == len(commits):
|
||||
print(" 到第 %d/%d 个提交 ✓" % (index, len(commits)))
|
||||
else:
|
||||
tail = [ln for ln in err.strip().splitlines() if ln.strip()]
|
||||
reason = tail[-1][:100] if tail else "?"
|
||||
print(" 第 %d 个提交失败: %s" % (index, reason))
|
||||
failed_at = (index, sha, reason)
|
||||
break
|
||||
|
||||
print()
|
||||
if failed_at is None:
|
||||
print(" ✓ 全部推完(%d 个提交)" % len(commits))
|
||||
return 0
|
||||
|
||||
index, sha, reason = failed_at
|
||||
print(" 卡在第 %d 个提交 %s" % (index, sha))
|
||||
print(" 原因: %s" % reason)
|
||||
print()
|
||||
print(" 这个提交新增的东西:")
|
||||
r = git("show", "--stat", "--oneline", sha)
|
||||
for line in (r.stdout or "").strip().splitlines()[:15]:
|
||||
print(" " + line[:100])
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -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())
|
||||
@@ -0,0 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r'''量一下仓库体积,找出推送 413 的原因。'''
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
|
||||
GIT = r"C:\Program Files\Git\bin\git.exe"
|
||||
|
||||
|
||||
def git(*args: str) -> str:
|
||||
r = subprocess.run([GIT] + list(args), cwd=str(ROOT),
|
||||
capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace")
|
||||
return r.stdout or ""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print(" 一、仓库对象统计")
|
||||
print(" " + git("count-objects", "-vH").replace("\n", "\n ").strip())
|
||||
|
||||
print()
|
||||
print(" 二、各提交的树体积(找有没有混进大文件)")
|
||||
# 列出所有 blob 及大小
|
||||
out = git("rev-list", "--objects", "--all")
|
||||
lines = [ln for ln in out.splitlines() if ln.strip()]
|
||||
print(" 对象条目 %d 个" % len(lines))
|
||||
|
||||
# 批量查类型和大小
|
||||
payload = "\n".join(ln.split(" ", 1)[0] for ln in lines)
|
||||
r = subprocess.run([GIT, "cat-file", "--batch-check=%(objecttype) %(objectsize) %(objectname)"],
|
||||
cwd=str(ROOT), input=payload, capture_output=True,
|
||||
text=True, encoding="utf-8", errors="replace")
|
||||
info = {}
|
||||
for line in (r.stdout or "").splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) >= 3 and parts[0] == "blob":
|
||||
info[parts[2]] = int(parts[1])
|
||||
|
||||
blobs = []
|
||||
for ln in lines:
|
||||
sha, _, name = ln.partition(" ")
|
||||
if sha in info:
|
||||
blobs.append((info[sha], name or "(无名)"))
|
||||
# 同一个 blob 可能出现多次,去重
|
||||
seen = set()
|
||||
uniq = []
|
||||
for size, name in blobs:
|
||||
key = (size, name)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
uniq.append((size, name))
|
||||
|
||||
uniq.sort(reverse=True)
|
||||
total = sum(s for s, _ in uniq)
|
||||
print(" 去重后 %d 个 blob,合计 %.2f MB"
|
||||
% (len(uniq), total / 1024 / 1024))
|
||||
print()
|
||||
print(" 最大的 15 个:")
|
||||
for size, name in uniq[:15]:
|
||||
print(" %9.1f KB %s" % (size / 1024, name[:70]))
|
||||
|
||||
print()
|
||||
print(" 三、结论")
|
||||
biggest = uniq[0][0] if uniq else 0
|
||||
if total > 20 * 1024 * 1024:
|
||||
print(" 仓库历史 %.1f MB,偏大 —— 推送会被 nginx 的"
|
||||
" client_max_body_size 拦(413)" % (total / 1024 / 1024))
|
||||
print(" 最大单个文件 %.1f MB" / 1024 / 1024 % (biggest / 1024 / 1024)
|
||||
if False else " 最大单个文件 %.1f MB" % (biggest / 1024 / 1024))
|
||||
else:
|
||||
print(" 仓库历史 %.2f MB,不算大" % (total / 1024 / 1024))
|
||||
|
||||
print()
|
||||
print(" 四、本机 Gitee 推得动说明不是仓库本身的问题,")
|
||||
print(" 是 EzHub 那边的 nginx 上限比 Gitee 小。")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+18
-2
@@ -324,12 +324,28 @@ def main() -> int:
|
||||
return 0
|
||||
|
||||
# ---- 1. 推代码 ----
|
||||
# EzHub 的 SSH 密钥在用户那边还没配对成功(服务端拒绝本机公钥),
|
||||
# 但令牌有 push 权限,所以走 HTTPS + 令牌推。
|
||||
# 令牌只在这次进程里拼进 URL,不写进 .git/config。
|
||||
print()
|
||||
print(" 二、推代码")
|
||||
for name, info in PLATFORMS.items():
|
||||
r = run_git(["push", info["remote"], "main"])
|
||||
if name == "ezhub":
|
||||
url = ("https://%s:%s@%s/%s/%s.git"
|
||||
% ("xmz", ezhub_token, "git.261826.xyz", "xmz", "ykxmz"))
|
||||
r = subprocess.run([GIT, "push", url, "main"], cwd=str(ROOT),
|
||||
capture_output=True, text=True,
|
||||
encoding="utf-8", errors="replace",
|
||||
timeout=900)
|
||||
raw = (r.stderr or r.stdout or "")
|
||||
else:
|
||||
r = run_git(["push", info["remote"], "main"])
|
||||
raw = (r.stderr or r.stdout or "")
|
||||
|
||||
ok = r.returncode == 0
|
||||
tail = ((r.stderr or r.stdout or "").strip().splitlines() or [""])[-1]
|
||||
# 输出里可能带令牌,打码
|
||||
safe = raw.replace(ezhub_token, "***") if ezhub_token else raw
|
||||
tail = (safe.strip().splitlines() or [""])[-1]
|
||||
print(" %-6s %s %s" % (name, "✓" if ok else "✗", tail[:90]))
|
||||
if not ok:
|
||||
print(" (代码没推上去,先修这个再建发行版)")
|
||||
|
||||
Reference in New Issue
Block a user