From 2425f8694ecafdab89def764523123e7ba23d1db Mon Sep 17 00:00:00 2001 From: xmz7891 Date: Wed, 7 Oct 2026 12:36:35 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=8F=91=E8=A1=8C=E7=89=88=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E9=99=84=E4=BB=B6=EF=BC=9A=E9=87=8D=E5=8F=91=E5=89=8D?= =?UTF-8?q?=E5=85=88=E5=88=A0=E5=90=8C=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea 的发行版复用逻辑有 bug:发现标签已有发行版就复用, 但**没有先删同名附件**,于是每跑一次就多传一份。 调试期间同一个文件在 EzHub 上堆了 3 份(共 8 个附件,实际只要 3 个)。 修法:复用已有发行版时,先列出它的附件, 把和本次要传的同名的删掉,再传。 配套加了: * clean_ezhub_assets.py 清理已堆积的重复(同名只留最新,支持 --dry) * verify_releases.py 对账两个平台的发行版(标题/说明/附件/ 管理页字眼/两边说明是否一致) 已清理:EzHub 从 8 个附件降到 3 个。 --- _build/clean_ezhub_assets.py | 108 +++++++++++++++++++++++++++++++++++ _build/release_all.py | 24 ++++++++ _build/verify_releases.py | 97 +++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 _build/clean_ezhub_assets.py create mode 100644 _build/verify_releases.py diff --git a/_build/clean_ezhub_assets.py b/_build/clean_ezhub_assets.py new file mode 100644 index 0000000..9f7f382 --- /dev/null +++ b/_build/clean_ezhub_assets.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +r'''清理 EzHub 发行版里重复的附件。 + +为什么会重复: + release_all.py 发现标签已有发行版时会"复用它", + 但**没有先删同名附件**,于是每跑一次就多传一遍。 + 我调试时跑了几次,同一个文件就堆了 2~3 份。 + +这个脚本:同名附件只保留最新的一份,其余删掉。 +(Gitea 的附件用 id 区分,创建时间看 created_at。) +''' +from __future__ import annotations + +import json +import sys +import urllib.error +import urllib.request +from collections import defaultdict +from pathlib import Path + +TOKEN = (Path.home() / ".ykxmz" / "ezhub_token").read_text( + encoding="utf-8").strip() +API = "https://git.261826.xyz/api/v1/repos/xmz/ykxmz" +TAG = "v1.8.0" +HEAD = {"Authorization": "token " + TOKEN, "User-Agent": "ykxmz-clean"} + + +def call(url: str, method: str = "GET"): + req = urllib.request.Request(url, method=method, headers=HEAD) + try: + with urllib.request.urlopen(req, timeout=60) as resp: + text = resp.read().decode("utf-8", "replace") + return resp.status, (json.loads(text) if text.strip() else {}) + except urllib.error.HTTPError as exc: + body = exc.read().decode("utf-8", "replace")[:200] + return exc.code, body + + +def main() -> int: + dry = "--dry" in sys.argv + + status, release = call("%s/releases/tags/%s" % (API, TAG)) + if status != 200: + print(" [失败] 取发行版 HTTP %s: %s" % (status, release)) + return 1 + rid = release["id"] + assets = release.get("assets") or [] + print(" 发行版 id=%s,附件 %d 个" % (rid, len(assets))) + + # 按文件名分组 + groups = defaultdict(list) + for a in assets: + groups[a.get("name") or "?"].append(a) + + print() + print(" 分组情况:") + to_delete = [] + for name, items in sorted(groups.items()): + # 新的排在前面(created_at 降序) + items.sort(key=lambda x: x.get("created_at") or "", reverse=True) + keep = items[0] + print(" %-32s 共 %d 份,保留 id=%s(%s)" + % (name, len(items), keep.get("id"), + (keep.get("created_at") or "")[:19])) + for extra in items[1:]: + print(" 删 id=%s(%s)" % (extra.get("id"), + (extra.get("created_at") + or "")[:19])) + to_delete.append(extra) + + if not to_delete: + print() + print(" ✓ 没有重复,不用清理") + return 0 + + print() + if dry: + print(" 演练:会删 %d 个(去掉 --dry 真删)" % len(to_delete)) + return 0 + + print(" 开始删除 %d 个重复附件…" % len(to_delete)) + ok = 0 + for asset in to_delete: + status, body = call("%s/releases/%s/assets/%s" + % (API, rid, asset.get("id")), method="DELETE") + if status in (200, 204): + print(" ✓ 删了 id=%s %s" % (asset.get("id"), + asset.get("name"))) + ok += 1 + else: + print(" ✗ id=%s HTTP %s %s" % (asset.get("id"), status, body)) + + print() + print(" 删除 %d/%d" % (ok, len(to_delete))) + + # 复查 + status, release = call("%s/releases/tags/%s" % (API, TAG)) + assets = release.get("assets") or [] + print() + print(" 现在剩 %d 个附件:" % len(assets)) + for a in assets: + print(" %-32s %8.2f MB" + % (a.get("name"), (a.get("size") or 0) / 1024 / 1024)) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/_build/release_all.py b/_build/release_all.py index 0b9fb87..4e97e83 100644 --- a/_build/release_all.py +++ b/_build/release_all.py @@ -196,6 +196,30 @@ def gitea_release(token: str, version: str, body: str, print(" ✗ 建发行版失败 HTTP %s: %s" % (exc.code, detail)) return False + # 先把同名旧附件删掉。 + # 不删的话,复用已有发行版时每跑一次就多传一份 —— + # 实测调试期间同一个文件堆了 3 份。 + try: + req = urllib.request.Request( + "%s/releases/%s" % (api, rid), headers=auth) + with urllib.request.urlopen(req, timeout=60) as resp: + existing = json.loads(resp.read().decode("utf-8", "replace")) + wanted = {p.name for p in files} + for asset in (existing.get("assets") or []): + if asset.get("name") in wanted: + del_url = "%s/releases/%s/assets/%s" % (api, rid, + asset.get("id")) + dreq = urllib.request.Request(del_url, method="DELETE", + headers=auth) + try: + urllib.request.urlopen(dreq, timeout=60) + print(" • 删掉同名旧附件 %s" % asset.get("name")) + except urllib.error.HTTPError as exc: + print(" • 删 %s 失败 HTTP %s(继续)" + % (asset.get("name"), exc.code)) + except Exception as exc: # noqa: BLE001 + print(" • 清理旧附件时出错(继续): %s" % exc) + ok = 0 for path in files: boundary = "----yk" + uuid.uuid4().hex diff --git a/_build/verify_releases.py b/_build/verify_releases.py new file mode 100644 index 0000000..381882d --- /dev/null +++ b/_build/verify_releases.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +r'''验证两个平台的发行版:标签、说明、附件。''' +from __future__ import annotations + +import json +import urllib.request +from pathlib import Path + +GITEE_TOKEN = (Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote" + r"\_build\.gitee_token").read_text(encoding="utf-8").strip()) +EZHUB_TOKEN = (Path.home() / ".ykxmz" / "ezhub_token").read_text( + encoding="utf-8").strip() + +TAG = "v1.8.0" +EXPECT = ["ykxmz-setup-1.8.0.exe", "ykxmz-server-1.8.0.zip", + "ykxmz-watch-1.8.0.apk"] + + +def fetch(url: str, token: str, kind: str): + headers = {"User-Agent": "ykxmz-verify"} + if kind == "gitee": + url += ("&" if "?" in url else "?") + "access_token=" + token + else: + headers["Authorization"] = "token " + token + with urllib.request.urlopen( + urllib.request.Request(url, headers=headers), timeout=60) as resp: + return json.loads(resp.read().decode("utf-8", "replace")) + + +def check(name: str, release: dict, web: str) -> None: + print(" ═══ %s ═══" % name) + print(" 标题: %s" % release.get("name")) + print(" 标签: %s" % release.get("tag_name")) + print(" 页面: %s" % (release.get("html_url") or web)) + + body = release.get("body") or "" + print(" 说明: %d 字符 / %d 行" % (len(body), body.count("\n") + 1)) + first = (body.strip().splitlines() or [""])[0] + print(" 首行: %s" % first[:70]) + + # 对外口径:不许出现管理页字眼 + bad = [w for w in ("zhongzhuanzhan", "/admin", "管理后台", "管理页") + if w in body] + print(" 管理页字眼: %s" % ("**有 " + str(bad) + "**" if bad else "无 ✓")) + + assets = (release.get("assets") or release.get("attach_files") or []) + print(" 附件 %d 个:" % len(assets)) + names = [] + for a in assets: + aname = a.get("name") or a.get("filename") or "?" + size = a.get("size") or 0 + names.append(aname) + print(" %-32s %8.2f MB" % (aname, size / 1024 / 1024)) + missing = [e for e in EXPECT if e not in names] + print(" 三个产物齐全: %s" % ("**缺 " + str(missing) + "**" + if missing else "✓")) + print() + + +def main() -> int: + print(" 一、Gitee") + try: + r = fetch("https://gitee.com/api/v5/repos/xmz7891/ykxmz" + "/releases/tags/" + TAG, GITEE_TOKEN, "gitee") + check("Gitee", r, "https://gitee.com/xmz7891/ykxmz/releases/tag/" + TAG) + except Exception as exc: # noqa: BLE001 + print(" 查询失败: %s" % exc) + print() + + print(" 二、EzHub") + try: + r = fetch("https://git.261826.xyz/api/v1/repos/xmz/ykxmz" + "/releases/tags/" + TAG, EZHUB_TOKEN, "gitea") + check("EzHub", r, + "https://git.261826.xyz/xmz/ykxmz/releases/tag/" + TAG) + except Exception as exc: # noqa: BLE001 + print(" 查询失败: %s" % exc) + print() + + print(" 三、两边的说明是否一致") + try: + g = fetch("https://gitee.com/api/v5/repos/xmz7891/ykxmz" + "/releases/tags/" + TAG, GITEE_TOKEN, "gitee").get("body") + e = fetch("https://git.261826.xyz/api/v1/repos/xmz/ykxmz" + "/releases/tags/" + TAG, EZHUB_TOKEN, "gitea").get("body") + same = (g or "").strip() == (e or "").strip() + print(" %s" % ("✓ 完全一致" if same else "✗ 不一致")) + if not same: + print(" Gitee %d 字符 / EzHub %d 字符" + % (len(g or ""), len(e or ""))) + except Exception as exc: # noqa: BLE001 + print(" 对比失败: %s" % exc) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())