Cut the release from the changelog heading on a green main instead of a hand-pushed tag
This commit is contained in:
@@ -1,38 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create the Gitea release for a tag, its body the tag's CHANGELOG.md section.
|
||||
"""Publish the Gitea release that CHANGELOG.md's top heading names, tagging SHA.
|
||||
|
||||
Env: GITEA_API_URL, GITEA_REPOSITORY (owner/repo), GITEA_TOKEN, TAG (vX.Y.Z).
|
||||
A top heading of `[Unreleased]`, or a version already released, publishes nothing.
|
||||
Env: GITEA_API_URL, GITEA_REPOSITORY (owner/repo), GITEA_TOKEN, SHA.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
|
||||
def section(changelog: str, version: str) -> str | None:
|
||||
m = re.search(rf"^## \[{re.escape(version)}\].*?$\n(.*?)(?=^## \[|\Z)", changelog, re.M | re.S)
|
||||
return m.group(1).strip() if m else None
|
||||
HEADING = re.compile(r"^## \[([^\]]+)\].*?$\n(.*?)(?=^## \[|\Z)", re.M | re.S)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
tag = os.environ["TAG"]
|
||||
if not re.fullmatch(r"v\d+\.\d+\.\d+", tag):
|
||||
print(f"{tag}: not a release tag; a release is vX.Y.Z", file=sys.stderr)
|
||||
return 1
|
||||
with open("CHANGELOG.md", encoding="utf-8") as f:
|
||||
body = section(f.read(), tag[1:])
|
||||
if body is None:
|
||||
print(f"CHANGELOG.md has no `## [{tag[1:]}]` heading; add the section, then tag", file=sys.stderr)
|
||||
return 1
|
||||
def request(path: str, data: dict | None = None):
|
||||
req = urllib.request.Request(
|
||||
f"{os.environ['GITEA_API_URL']}/repos/{os.environ['GITEA_REPOSITORY']}/releases",
|
||||
data=json.dumps({"body": body, "name": tag, "tag_name": tag}).encode(),
|
||||
f"{os.environ['GITEA_API_URL']}/repos/{os.environ['GITEA_REPOSITORY']}/{path}",
|
||||
data=json.dumps(data).encode() if data else None,
|
||||
headers={"Authorization": f"token {os.environ['GITEA_TOKEN']}", "Content-Type": "application/json"},
|
||||
)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
print(json.load(resp)["html_url"])
|
||||
return json.load(resp)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
with open("CHANGELOG.md", encoding="utf-8") as f:
|
||||
top = HEADING.search(f.read())
|
||||
if top is None:
|
||||
print("CHANGELOG.md has no `## [...]` heading", file=sys.stderr)
|
||||
return 1
|
||||
version, body = top.group(1), top.group(2).strip()
|
||||
if version == "Unreleased":
|
||||
print("top heading is Unreleased; nothing to publish")
|
||||
return 0
|
||||
if not re.fullmatch(r"\d+\.\d+\.\d+", version):
|
||||
print(f"top heading `[{version}]` is neither Unreleased nor X.Y.Z", file=sys.stderr)
|
||||
return 1
|
||||
tag = f"v{version}"
|
||||
try:
|
||||
print(f"{tag} already published: {request(f'releases/tags/{tag}')['html_url']}")
|
||||
return 0
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code != 404:
|
||||
raise
|
||||
release = request("releases", {"body": body, "name": tag, "tag_name": tag, "target_commitish": os.environ["SHA"]})
|
||||
print(release["html_url"])
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user