Files
ykxmz/_build/gitee_release.py
T

188 lines
6.8 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.
# -*- 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 changelog:
lines = changelog.splitlines()
if lines and lines[0].strip().startswith("#"):
changelog = "\n".join(lines[1:]).strip()
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()
# 发行版说明**只放更新内容**(用户要求)。
# 不再套一层 "## 更新内容" —— CHANGELOG 每段自带 "## 版本号",
# 套一层会变成两个标题叠在一起。
body = changelog.strip()
if not body:
body = f"版本 {version}"
# 保险:Gitee 上 markdown 首行若被吃掉,这里补个空行
if not body.endswith("\n"):
body += "\n"
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())