Files
ykxmz/_build/gitee_release.py
T
xmz7891 ce87ec1650 1.7.0:常规发布
- 版本号 1.7.0 -> 1.7.0
- 重建三端并打包
- 关键测试全过
2026-10-06 23:08:07 +08:00

180 lines
6.3 KiB
Python

# -*- coding: utf-8 -*-
r"""在 Gitee 上创建发行版(Release)并上传三个文件。
用法:
python _build/gitee_release.py <令牌> [版本号]
令牌只从命令行参数读,**不写进任何文件、不进仓库**。
Gitee 的两个接口:
建发行版 POST /api/v5/repos/{owner}/{repo}/releases
传附件 POST /api/v5/repos/{owner}/{repo}/releases/{id}/attach_files
SSH 密钥只能推代码,建发行版必须用这个令牌。
"""
from __future__ import annotations
import json
import mimetypes
import sys
import urllib.error
import urllib.request
import uuid
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
OWNER = "xmz7891"
REPO = "ykxmz"
API = f"https://gitee.com/api/v5/repos/{OWNER}/{REPO}"
def api(url: str, payload: dict | None = None, method: str = "GET",
timeout: int = 60):
data = None
headers = {"User-Agent": "ykxmz-release"}
if payload is not None:
data = json.dumps(payload).encode("utf-8")
headers["Content-Type"] = "application/json;charset=UTF-8"
request = urllib.request.Request(url, data=data, headers=headers,
method=method)
with urllib.request.urlopen(request, timeout=timeout) as response:
body = response.read().decode("utf-8", "replace")
return response.status, (json.loads(body) if body.strip() else {})
def upload_file(url: str, path: Path, token: str, timeout: int = 600):
"""multipart 上传单个文件。"""
boundary = "----ykxmz" + uuid.uuid4().hex
content_type = (mimetypes.guess_type(path.name)[0]
or "application/octet-stream")
head = (
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="access_token"\r\n\r\n'
f"{token}\r\n"
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="file"; '
f'filename="{path.name}"\r\n'
f"Content-Type: {content_type}\r\n\r\n"
).encode("utf-8")
tail = f"\r\n--{boundary}--\r\n".encode("utf-8")
body = head + path.read_bytes() + tail
request = urllib.request.Request(
url, data=body, method="POST",
headers={
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Content-Length": str(len(body)),
"User-Agent": "ykxmz-release",
})
with urllib.request.urlopen(request, timeout=timeout) as response:
text = response.read().decode("utf-8", "replace")
return response.status, (json.loads(text) if text.strip() else {})
def main() -> int:
if len(sys.argv) < 2:
print(" 用法: python _build/gitee_release.py <令牌> [版本号]")
return 1
token = sys.argv[1]
version = sys.argv[2] if len(sys.argv) > 2 else "1.6.0"
tag = f"v{version}"
# 更新内容从哪来:
# 1) 命令行第三个参数(直接给一段话)
# 2) _build/CHANGELOG.md 里最新一节
changelog = sys.argv[3] if len(sys.argv) > 3 else ''
if not changelog:
note = ROOT / "_build" / "CHANGELOG.md"
if note.is_file():
text = note.read_text(encoding="utf-8")
blocks = [b.strip() for b in text.split(chr(10) + "## ") if b.strip()]
for b in blocks:
if b.startswith(version) or b.startswith("v" + version):
changelog = b
break
if not changelog and blocks:
changelog = blocks[0]
if not changelog:
changelog = f"版本 {version}"
# 电脑端给**安装包**(不是 zip 了,用户要求);
# 服务器端给 zip;手表端直接给 APK。
files = [
ROOT / "dist" / f"ykxmz-setup-{version}.exe",
ROOT / "dist" / f"ykxmz-server-{version}.zip",
ROOT / "dist" / f"ykxmz-watch-{version}.apk",
]
for path in files:
if not path.is_file():
print(f" [失败] 找不到 {path}")
return 1
print(f" 仓库: {OWNER}/{REPO}")
print(f" 标签: {tag}")
print()
# 发行版说明**只放更新内容**(用户要求)。
# 不放下载表格、不放用法说明 —— 那些在 README 里,
# 这里堆一堆反而看不清这版改了什么。
body = f"""## 更新内容
{changelog}
"""
print(" 建发行版…")
try:
status, reply = api(
f"{API}/releases",
{"access_token": token, "tag_name": tag, "name": f"ykxmz {version}",
"body": body, "target_commitish": "main", "prerelease": False},
method="POST")
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")[:400]
print(f" [失败] HTTP {exc.code}: {detail}")
return 1
if status not in (200, 201):
print(f" [失败] HTTP {status}: {reply}")
return 1
release_id = reply.get("id")
print(f" ✓ 发行版已建,id={release_id}")
print(f" {reply.get('html_url', '')}")
# 如果标签已经存在(重复发布),走"找已有发行版再传附件"的路
if not release_id:
print(" [提示] 没拿到 id,尝试按标签查已有的发行版…")
try:
_s, existing = api(f"{API}/releases/tags/{tag}?access_token={token}")
release_id = existing.get("id")
print(f" 已有发行版 id={release_id}")
except Exception as exc: # noqa: BLE001
print(f" [失败] 查不到: {exc}")
return 1
# ---- 2. 传附件 ----
print()
print(" 上传文件:")
attach = f"{API}/releases/{release_id}/attach_files"
for path in files:
size_mb = path.stat().st_size / 1024 / 1024
try:
status, reply = upload_file(attach, path, token)
if status in (200, 201):
print(f" ✓ {path.name} ({size_mb:.2f} MB)")
else:
print(f" ✗ {path.name}: HTTP {status} {reply}")
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", "replace")[:200]
print(f" ✗ {path.name}: HTTP {exc.code} {detail}")
except Exception as exc: # noqa: BLE001
print(f" ✗ {path.name}: {exc}")
print()
print(f" 完成:https://gitee.com/{OWNER}/{REPO}/releases/tag/{tag}")
return 0
if __name__ == "__main__":
sys.exit(main())