160 lines
4.6 KiB
Python
160 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import urllib.parse
|
|
import urllib.request
|
|
|
|
|
|
def _api_base() -> str:
|
|
api = os.environ.get("GITHUB_API_URL")
|
|
if not api:
|
|
raise RuntimeError("GITHUB_API_URL is not set")
|
|
return api.rstrip("/")
|
|
|
|
|
|
def _repo() -> str:
|
|
repo = os.environ.get("GITHUB_REPOSITORY")
|
|
if not repo:
|
|
raise RuntimeError("GITHUB_REPOSITORY is not set")
|
|
return repo
|
|
|
|
|
|
def _token() -> str:
|
|
token = os.environ.get("GITEA_TOKEN")
|
|
if not token:
|
|
raise RuntimeError("GITEA_TOKEN is not set")
|
|
return token
|
|
|
|
|
|
def _request(path: str, method: str = "GET", data=None, headers=None):
|
|
url = f"{_api_base()}{path}"
|
|
req_headers = {
|
|
"Accept": "application/json",
|
|
"Authorization": f"token {_token()}",
|
|
}
|
|
if headers:
|
|
req_headers.update(headers)
|
|
|
|
body = None
|
|
if data is not None:
|
|
if isinstance(data, (dict, list)):
|
|
body = json.dumps(data).encode("utf-8")
|
|
req_headers["Content-Type"] = "application/json"
|
|
elif isinstance(data, bytes):
|
|
body = data
|
|
else:
|
|
body = str(data).encode("utf-8")
|
|
|
|
req = urllib.request.Request(url, data=body, headers=req_headers, method=method)
|
|
try:
|
|
with urllib.request.urlopen(req) as response:
|
|
raw = response.read()
|
|
if not raw:
|
|
return None
|
|
return json.loads(raw.decode("utf-8"))
|
|
except urllib.error.HTTPError as exc:
|
|
payload = exc.read().decode("utf-8", errors="replace")
|
|
raise RuntimeError(f"{method} {url} failed: {exc.code} {payload}") from exc
|
|
|
|
|
|
def _release_path(tag: str) -> str:
|
|
return f"/repos/{_repo()}/releases/tags/{urllib.parse.quote(tag, safe='')}"
|
|
|
|
|
|
def ensure_release(args) -> int:
|
|
try:
|
|
release = _request(_release_path(args.tag))
|
|
except RuntimeError as exc:
|
|
if " 404 " not in str(exc):
|
|
raise
|
|
release = None
|
|
|
|
if release is None:
|
|
payload = {
|
|
"tag_name": args.tag,
|
|
"target_commitish": args.target,
|
|
"name": args.name or args.tag,
|
|
"body": args.notes or "",
|
|
"draft": args.draft,
|
|
"prerelease": args.prerelease,
|
|
}
|
|
release = _request(f"/repos/{_repo()}/releases", method="POST", data=payload)
|
|
|
|
output = os.environ.get("GITHUB_OUTPUT")
|
|
if output:
|
|
with open(output, "a", encoding="utf-8") as fh:
|
|
fh.write(f"release_id={release['id']}\n")
|
|
fh.write(f"release_tag={release['tag_name']}\n")
|
|
|
|
print(json.dumps({"id": release["id"], "tag": release["tag_name"]}))
|
|
return 0
|
|
|
|
|
|
def _list_assets(release_id: int):
|
|
release = _request(f"/repos/{_repo()}/releases/{release_id}")
|
|
return release.get("assets") or release.get("attachments") or []
|
|
|
|
|
|
def upload_asset(args) -> int:
|
|
filename = args.name or os.path.basename(args.file)
|
|
for asset in _list_assets(args.release_id):
|
|
if asset.get("name") == filename and asset.get("id") is not None:
|
|
_request(
|
|
f"/repos/{_repo()}/releases/{args.release_id}/assets/{asset['id']}",
|
|
method="DELETE",
|
|
)
|
|
|
|
with open(args.file, "rb") as fh:
|
|
payload = fh.read()
|
|
|
|
path = (
|
|
f"/repos/{_repo()}/releases/{args.release_id}/assets"
|
|
f"?name={urllib.parse.quote(filename, safe='')}"
|
|
)
|
|
response = _request(
|
|
path,
|
|
method="POST",
|
|
data=payload,
|
|
headers={"Content-Type": "application/octet-stream"},
|
|
)
|
|
print(json.dumps({"id": response.get("id"), "name": response.get("name", filename)}))
|
|
return 0
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
|
|
ensure = sub.add_parser("ensure-release")
|
|
ensure.add_argument("--tag", required=True)
|
|
ensure.add_argument("--target", required=True)
|
|
ensure.add_argument("--name", default="")
|
|
ensure.add_argument("--notes", default="")
|
|
ensure.add_argument("--draft", action="store_true")
|
|
ensure.add_argument("--prerelease", action="store_true")
|
|
|
|
upload = sub.add_parser("upload-asset")
|
|
upload.add_argument("--release-id", required=True, type=int)
|
|
upload.add_argument("--file", required=True)
|
|
upload.add_argument("--name", default="")
|
|
|
|
args = parser.parse_args()
|
|
if args.cmd == "ensure-release":
|
|
return ensure_release(args)
|
|
if args.cmd == "upload-asset":
|
|
return upload_asset(args)
|
|
|
|
parser.print_help()
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
raise SystemExit(main())
|
|
except Exception as exc:
|
|
print(f"gitea_release.py failed: {exc}", file=sys.stderr)
|
|
raise
|