Add --publish flag to release.py for automatic Gitea releases
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
117
release.py
117
release.py
@@ -11,10 +11,13 @@ Usage:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
|
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
@@ -253,8 +256,120 @@ def main() -> None:
|
|||||||
print("Next steps:")
|
print("Next steps:")
|
||||||
print(" 1. Review changes: git diff")
|
print(" 1. Review changes: git diff")
|
||||||
print(" 2. Build: python build.py --clean")
|
print(" 2. Build: python build.py --clean")
|
||||||
print(" 3. Commit & push")
|
print(" 3. Commit, tag & push")
|
||||||
|
print(f" 4. python release.py --publish {new_version}")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Gitea release publishing
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _gitea_api(endpoint: str, method: str = "GET", data: dict | None = None,
|
||||||
|
binary_path: str | None = None) -> dict | None:
|
||||||
|
"""Call Gitea API. Reads credentials from git remote 'sensey'."""
|
||||||
|
import base64
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "remote", "get-url", "sensey"],
|
||||||
|
capture_output=True, text=True, cwd=PROJECT_DIR,
|
||||||
|
)
|
||||||
|
remote_url = result.stdout.strip()
|
||||||
|
except Exception:
|
||||||
|
print("ERROR: cannot read 'sensey' remote")
|
||||||
|
return None
|
||||||
|
|
||||||
|
m = re.match(r"https://([^:]+):([^@]+)@([^/]+)/(.+?)(?:\.git)?$", remote_url)
|
||||||
|
if not m:
|
||||||
|
print(f"ERROR: cannot parse remote URL: {remote_url}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
user, password, host, repo_path = m.groups()
|
||||||
|
base = f"https://{host}/api/v1/repos/{repo_path}"
|
||||||
|
url = f"{base}/{endpoint}" if endpoint else base
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": "Basic " + base64.b64encode(
|
||||||
|
f"{user}:{password}".encode()
|
||||||
|
).decode(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if binary_path:
|
||||||
|
headers["Content-Type"] = "application/octet-stream"
|
||||||
|
with open(binary_path, "rb") as f:
|
||||||
|
body = f.read()
|
||||||
|
elif data is not None:
|
||||||
|
headers["Content-Type"] = "application/json"
|
||||||
|
body = json.dumps(data).encode()
|
||||||
|
else:
|
||||||
|
body = None
|
||||||
|
|
||||||
|
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req) as resp:
|
||||||
|
return json.loads(resp.read().decode())
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
print(f"Gitea API error {e.code}: {e.read().decode()}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def publish(version: str):
|
||||||
|
"""Create a Gitea release and upload binaries from releases/ folder."""
|
||||||
|
tag = f"v{version}" if not version.startswith("v") else version
|
||||||
|
ver = tag.lstrip("v")
|
||||||
|
|
||||||
|
print(f"\nPublishing release {tag} to Gitea...")
|
||||||
|
|
||||||
|
# Read changelog section for this version
|
||||||
|
changelog_path = os.path.join(PROJECT_DIR, "CHANGELOG.md")
|
||||||
|
body = ""
|
||||||
|
if os.path.exists(changelog_path):
|
||||||
|
text = read_file(changelog_path)
|
||||||
|
pattern = rf"(## \[{re.escape(ver)}\].*?)(?=\n## \[|\Z)"
|
||||||
|
m = re.search(pattern, text, re.DOTALL)
|
||||||
|
if m:
|
||||||
|
body = m.group(1).strip()
|
||||||
|
|
||||||
|
# Create release
|
||||||
|
release = _gitea_api("releases", "POST", {
|
||||||
|
"tag_name": tag,
|
||||||
|
"name": tag,
|
||||||
|
"body": body or f"Release {tag}",
|
||||||
|
"draft": False,
|
||||||
|
"prerelease": False,
|
||||||
|
})
|
||||||
|
if not release:
|
||||||
|
print("ERROR: failed to create release")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
release_id = release["id"]
|
||||||
|
print(f" Release created: {release['html_url']}")
|
||||||
|
|
||||||
|
# Upload matching binaries
|
||||||
|
releases_dir = os.path.join(PROJECT_DIR, "releases")
|
||||||
|
if os.path.isdir(releases_dir):
|
||||||
|
for fname in sorted(os.listdir(releases_dir)):
|
||||||
|
if f"-v{ver}-" in fname:
|
||||||
|
fpath = os.path.join(releases_dir, fname)
|
||||||
|
print(f" Uploading {fname}...", end=" ")
|
||||||
|
asset = _gitea_api(
|
||||||
|
f"releases/{release_id}/assets?name={fname}",
|
||||||
|
"POST", binary_path=fpath,
|
||||||
|
)
|
||||||
|
if asset:
|
||||||
|
size_mb = asset.get("size", 0) / (1024 * 1024)
|
||||||
|
print(f"OK ({size_mb:.1f} MB)")
|
||||||
|
else:
|
||||||
|
print("FAILED")
|
||||||
|
|
||||||
|
print(f"\nRelease {tag} published!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
if "--publish" in sys.argv:
|
||||||
|
idx = sys.argv.index("--publish")
|
||||||
|
if idx + 1 < len(sys.argv):
|
||||||
|
publish(sys.argv[idx + 1])
|
||||||
|
else:
|
||||||
|
publish(get_current_version())
|
||||||
|
sys.exit(0)
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user