import os import sys import json import argparse import urllib.request import urllib.parse import mimetypes import package_dist DEFAULT_GITEA_URL = "https://gitea.eibl.tech" DEFAULT_REPO = "me0nline/LOGAR" def upload_file_to_release(base_url, repo, release_id, token, file_path): filename = os.path.basename(file_path) url = f"{base_url}/api/v1/repos/{repo}/releases/{release_id}/assets?name={urllib.parse.quote(filename)}" with open(file_path, "rb") as f: file_bytes = f.read() req = urllib.request.Request(url, data=file_bytes, method="POST") req.add_header("Authorization", f"token {token}") req.add_header("Content-Type", "application/octet-stream") req.add_header("Accept", "application/json") try: with urllib.request.urlopen(req) as resp: data = json.loads(resp.read().decode("utf-8")) print(f"[+] Attached {filename} ({len(file_bytes)} bytes) to release.") return data except urllib.error.HTTPError as e: err = e.read().decode("utf-8", errors="ignore") print(f"[!] Error uploading {filename}: HTTP {e.code} - {err}") return None def create_or_get_release(base_url, repo, tag, token, title=None, notes=None): url = f"{base_url}/api/v1/repos/{repo}/releases" headers = { "Authorization": f"token {token}", "Content-Type": "application/json", "Accept": "application/json" } # Check if release exists check_url = f"{base_url}/api/v1/repos/{repo}/releases/tags/{urllib.parse.quote(tag)}" check_req = urllib.request.Request(check_url, headers={"Authorization": f"token {token}"}) try: with urllib.request.urlopen(check_req) as resp: existing = json.loads(resp.read().decode("utf-8")) print(f"[*] Found existing release for tag {tag} (ID: {existing['id']})") return existing["id"] except urllib.error.HTTPError: pass # Create new release payload = { "tag_name": tag, "name": title or f"LOGAR Release {tag}", "body": notes or f"Automated binary release for {tag}.", "draft": False, "prerelease": False } req = urllib.request.Request(url, data=json.dumps(payload).encode("utf-8"), headers=headers, method="POST") with urllib.request.urlopen(req) as resp: created = json.loads(resp.read().decode("utf-8")) print(f"[+] Created release {tag} (ID: {created['id']})") return created["id"] def main(): parser = argparse.ArgumentParser(description="Upload LOGAR compiled binaries directly to Gitea Release") parser.add_argument("--tag", required=True, help="Release tag name (e.g. v1.0.0)") parser.add_argument("--token", default=os.environ.get("GITEA_TOKEN"), help="Gitea Personal Access Token (or set GITEA_TOKEN env var)") parser.add_argument("--url", default=DEFAULT_GITEA_URL, help="Base Gitea instance URL") parser.add_argument("--repo", default=DEFAULT_REPO, help="Repository owner/name") parser.add_argument("--skip-build", action="store_true", help="Skip running package_dist.py before upload") args = parser.parse_args() token = args.token if not token: token = input("Enter Gitea Personal Access Token: ").strip() if not token: print("[!] Token is required.") sys.exit(1) if not args.skip_build: print("[*] Assembling compiled binaries...") package_dist.main() dist_dir = os.path.abspath("dist") if not os.path.exists(dist_dir) or not os.listdir(dist_dir): print("[!] No binaries found in dist/. Run package_dist.py first.") sys.exit(1) print(f"[*] Connecting to Gitea: {args.url} (repo: {args.repo})...") release_id = create_or_get_release(args.url, args.repo, args.tag, token) print(f"[*] Uploading binary assets from '{dist_dir}'...") for f in sorted(os.listdir(dist_dir)): fpath = os.path.join(dist_dir, f) if os.path.isfile(fpath): upload_file_to_release(args.url, args.repo, release_id, token, fpath) print(f"\n[+] Release successfully published with binary assets: {args.url}/{args.repo}/releases/tag/{args.tag}") if __name__ == "__main__": main()