Add release asset upload helper
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
# Gitea リリース更新の API 部分(release-update.sh から呼ばれる)。
|
||||||
|
# 1) タグ名からリリースを特定
|
||||||
|
# 2) 同名の旧添付資産を削除し、最新の zip をアップロード
|
||||||
|
# 3) 本文の「ビルド元コミット: `xxxx`」を現在の短縮ハッシュへ置換
|
||||||
|
# 認証は環境変数 GITEA_USER/GITEA_PASS(Basic)。Gitea が自己署名証明書のため TLS 検証は無効化。
|
||||||
|
# 引数: <host> <owner> <repo> <tag> <asset_path> <commit_short>
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import ssl
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
from urllib.request import Request, urlopen
|
||||||
|
from urllib.error import HTTPError
|
||||||
|
|
||||||
|
host, owner, repo, tag, asset, commit = sys.argv[1:7]
|
||||||
|
user = os.environ["GITEA_USER"]
|
||||||
|
pw = os.environ["GITEA_PASS"]
|
||||||
|
|
||||||
|
base = f"https://{host}/api/v1/repos/{owner}/{repo}"
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
auth = "Basic " + base64.b64encode(f"{user}:{pw}".encode()).decode()
|
||||||
|
|
||||||
|
|
||||||
|
def api(path, method="GET", data=None, headers=None):
|
||||||
|
h = {"Authorization": auth}
|
||||||
|
if headers:
|
||||||
|
h.update(headers)
|
||||||
|
req = Request(base + path, data=data, method=method, headers=h)
|
||||||
|
try:
|
||||||
|
r = urlopen(req, context=ctx)
|
||||||
|
except HTTPError as e:
|
||||||
|
msg = e.read().decode("utf-8", "replace")
|
||||||
|
sys.exit(f"ERROR: {method} {path} -> HTTP {e.code}: {msg}")
|
||||||
|
body = r.read()
|
||||||
|
return json.loads(body) if body else None
|
||||||
|
|
||||||
|
|
||||||
|
# 1) タグ名でリリースを特定(無ければ 404 で停止)
|
||||||
|
rel = api(f"/releases/tags/{tag}")
|
||||||
|
rid = rel["id"]
|
||||||
|
print(f"release id={rid} (tag {tag})")
|
||||||
|
|
||||||
|
# 2) 同名の旧資産を削除
|
||||||
|
name = os.path.basename(asset)
|
||||||
|
for a in api(f"/releases/{rid}/assets") or []:
|
||||||
|
if a["name"] == name:
|
||||||
|
api(f"/releases/{rid}/assets/{a['id']}", method="DELETE")
|
||||||
|
print(f"deleted old asset id={a['id']} ({a['size']} bytes)")
|
||||||
|
|
||||||
|
# 3) 最新の zip をアップロード(multipart/form-data, field=attachment)
|
||||||
|
with open(asset, "rb") as f:
|
||||||
|
filedata = f.read()
|
||||||
|
boundary = "----photochkReleaseBoundary7e1f"
|
||||||
|
mp = b"".join([
|
||||||
|
f"--{boundary}\r\n".encode(),
|
||||||
|
f'Content-Disposition: form-data; name="attachment"; filename="{name}"\r\n'.encode(),
|
||||||
|
b"Content-Type: application/octet-stream\r\n\r\n",
|
||||||
|
filedata,
|
||||||
|
b"\r\n",
|
||||||
|
f"--{boundary}--\r\n".encode(),
|
||||||
|
])
|
||||||
|
new = api(
|
||||||
|
f"/releases/{rid}/assets?name={name}",
|
||||||
|
method="POST",
|
||||||
|
data=mp,
|
||||||
|
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
|
||||||
|
)
|
||||||
|
print(f"uploaded asset id={new['id']} ({new['size']} bytes)")
|
||||||
|
|
||||||
|
# 4) 本文の「ビルド元コミット: `xxxx`」を現在の短縮ハッシュへ置換
|
||||||
|
body = rel.get("body") or ""
|
||||||
|
new_body, n = re.subn(
|
||||||
|
r"(ビルド元コミット: `)[0-9a-fA-F]{7,40}(`)",
|
||||||
|
r"\g<1>" + commit + r"\g<2>",
|
||||||
|
body,
|
||||||
|
)
|
||||||
|
if n == 0:
|
||||||
|
# まだ行が無いリリースには追記する。次回以降は置換で更新される。
|
||||||
|
new_body = body.rstrip() + f"\n\nビルド元コミット: `{commit}`\n"
|
||||||
|
api(
|
||||||
|
f"/releases/{rid}",
|
||||||
|
method="PATCH",
|
||||||
|
data=json.dumps({"body": new_body}).encode("utf-8"),
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
)
|
||||||
|
print(f"body {'updated' if n else 'appended'}: ビルド元コミット -> {commit}")
|
||||||
Reference in New Issue
Block a user