Add Gitea CI and release pipeline
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import mimetypes
|
||||
import pathlib
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
|
||||
def request_json(method: str, url: str, token: str, payload: dict | None = None) -> dict:
|
||||
data = None
|
||||
headers = {"Authorization": f"token {token}", "Accept": "application/json"}
|
||||
if payload is not None:
|
||||
data = json.dumps(payload).encode()
|
||||
headers["Content-Type"] = "application/json"
|
||||
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode())
|
||||
|
||||
|
||||
def request_no_content(method: str, url: str, token: str) -> None:
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
headers={"Authorization": f"token {token}", "Accept": "application/json"},
|
||||
method=method,
|
||||
)
|
||||
with urllib.request.urlopen(req):
|
||||
return
|
||||
|
||||
|
||||
def get_or_create_release(server: str, repo: str, token: str, tag: str) -> dict:
|
||||
base = f"{server.rstrip('/')}/api/v1/repos/{repo}"
|
||||
tag_url = f"{base}/releases/tags/{urllib.parse.quote(tag, safe='')}"
|
||||
try:
|
||||
return request_json("GET", tag_url, token)
|
||||
except urllib.error.HTTPError as err:
|
||||
if err.code != 404:
|
||||
raise
|
||||
payload = {
|
||||
"tag_name": tag,
|
||||
"name": tag,
|
||||
"draft": False,
|
||||
"prerelease": False,
|
||||
}
|
||||
return request_json("POST", f"{base}/releases", token, payload)
|
||||
|
||||
|
||||
def upload_asset(server: str, repo: str, token: str, release: dict, path: pathlib.Path) -> None:
|
||||
base = f"{server.rstrip('/')}/api/v1/repos/{repo}"
|
||||
assets = release.get("assets", [])
|
||||
for asset in assets:
|
||||
if asset.get("name") == path.name:
|
||||
request_no_content("DELETE", f"{base}/releases/{release['id']}/assets/{asset['id']}", token)
|
||||
query = urllib.parse.urlencode({"name": path.name})
|
||||
url = f"{base}/releases/{release['id']}/assets?{query}"
|
||||
content_type = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=path.read_bytes(),
|
||||
headers={
|
||||
"Authorization": f"token {token}",
|
||||
"Accept": "application/json",
|
||||
"Content-Type": content_type,
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req):
|
||||
return
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--server", required=True)
|
||||
parser.add_argument("--repo", required=True)
|
||||
parser.add_argument("--token", required=True)
|
||||
parser.add_argument("--tag", required=True)
|
||||
parser.add_argument("artifacts", nargs="+")
|
||||
args = parser.parse_args()
|
||||
|
||||
paths = [pathlib.Path(p) for p in args.artifacts]
|
||||
missing = [str(p) for p in paths if not p.is_file()]
|
||||
if missing:
|
||||
print(f"missing artifacts: {', '.join(missing)}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
release = get_or_create_release(args.server, args.repo, args.token, args.tag)
|
||||
for path in paths:
|
||||
upload_asset(args.server, args.repo, args.token, release, path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user