#!/usr/bin/env python3 from __future__ import annotations import argparse import json import os import urllib.error import urllib.parse import urllib.request import zipfile from pathlib import Path def zip_resourcepack(resourcepack_root: Path, archive_path: Path) -> None: archive_path.parent.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(archive_path, mode="w", compression=zipfile.ZIP_DEFLATED) as archive: for source_file in sorted(resourcepack_root.rglob("*")): if source_file.is_dir(): continue relative_path = source_file.relative_to(resourcepack_root).as_posix() archive.write(source_file, relative_path) def request_json(request: urllib.request.Request) -> dict[str, object]: with urllib.request.urlopen(request) as response: return json.load(response) def create_release(api_base: str, repository: str, token: str, tag: str, name: str, body: str) -> dict[str, object]: payload = json.dumps( { "tag_name": tag, "name": name, "body": body, "draft": False, "prerelease": False, } ).encode("utf-8") request = urllib.request.Request( f"{api_base}/repos/{repository}/releases", data=payload, method="POST", headers={ "Authorization": f"token {token}", "Content-Type": "application/json", "Accept": "application/json", }, ) return request_json(request) def upload_asset(api_base: str, repository: str, token: str, release_id: int, archive_path: Path, asset_name: str) -> dict[str, object]: with archive_path.open("rb") as handle: payload = handle.read() query = urllib.parse.urlencode({"name": asset_name}) request = urllib.request.Request( f"{api_base}/repos/{repository}/releases/{release_id}/assets?{query}", data=payload, method="POST", headers={ "Authorization": f"token {token}", "Content-Type": "application/zip", "Accept": "application/json", }, ) return request_json(request) def main() -> int: parser = argparse.ArgumentParser(description="Package the resource pack and publish it as a Gitea release asset.") parser.add_argument("--resourcepack-root", default="resourcepack", help="Path to the resource pack root") parser.add_argument("--archive-path", default="build/resourcepack.zip", help="Path to the generated zip archive") parser.add_argument("--tag", required=True, help="Release tag to publish") parser.add_argument("--name", default=None, help="Release name to publish") parser.add_argument("--body", default=None, help="Release notes/body text") args = parser.parse_args() token = os.environ.get("RELEASE_TOKEN") or os.environ.get("GITEA_TOKEN") or os.environ.get("GITHUB_TOKEN") server_url = os.environ.get("GITEA_SERVER_URL") or os.environ.get("GITHUB_SERVER_URL") repository = os.environ.get("GITEA_REPOSITORY") or os.environ.get("GITHUB_REPOSITORY") if not token: raise SystemExit("RELEASE_TOKEN is required") if not server_url: raise SystemExit("GITEA_SERVER_URL or GITHUB_SERVER_URL is required") if not repository: raise SystemExit("GITEA_REPOSITORY or GITHUB_REPOSITORY is required") api_base = server_url.rstrip("/") + "/api/v1" resourcepack_root = Path(args.resourcepack_root).resolve() archive_path = Path(args.archive_path).resolve() release_name = args.name or args.tag release_body = args.body or f"Automated resource pack release for {args.tag}." asset_name = f"resourcepack-{args.tag}.zip" zip_resourcepack(resourcepack_root, archive_path) release = create_release(api_base, repository, token, args.tag, release_name, release_body) release_id = release.get("id") if not isinstance(release_id, int): raise SystemExit("Release creation did not return a numeric release id") upload_asset(api_base, repository, token, release_id, archive_path, asset_name) return 0 if __name__ == "__main__": raise SystemExit(main())