mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-10-29 03:36:15 +08:00
143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Classify a GitHub Actions trigger without deriving versions from PR data."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
STABLE_TAG = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+$")
|
|
PRERELEASE_TAG = re.compile(r"^[0-9]+\.[0-9]+\.[0-9]+-(?:alpha|beta)\.[0-9]+$")
|
|
|
|
|
|
class ReleaseClassificationError(ValueError):
|
|
"""Raised when a release-shaped trigger is unsafe or unverifiable."""
|
|
|
|
|
|
def classify_release(
|
|
*,
|
|
event_name: str,
|
|
ref_type: str,
|
|
ref_name: str,
|
|
pr_head: str = "",
|
|
pr_title: str = "",
|
|
stable_tag_on_main: bool | None = None,
|
|
) -> dict[str, str]:
|
|
"""Return the four outputs consumed by the release workflow.
|
|
|
|
``pr_title`` is accepted so callers can pass complete event metadata, but
|
|
is deliberately never used as a version source.
|
|
"""
|
|
del pr_title
|
|
result = {
|
|
"release": "0",
|
|
"dev": "0",
|
|
"build_test": "0",
|
|
"version": "Test",
|
|
}
|
|
|
|
if ref_type == "tag" and STABLE_TAG.fullmatch(ref_name):
|
|
if stable_tag_on_main is None:
|
|
raise ReleaseClassificationError(
|
|
f"Stable tag {ref_name} ancestry was not verified"
|
|
)
|
|
if not stable_tag_on_main:
|
|
raise ReleaseClassificationError(
|
|
f"Stable tag {ref_name} does not point to a commit on main; "
|
|
"refusing to release"
|
|
)
|
|
return {
|
|
**result,
|
|
"release": "1",
|
|
"version": ref_name,
|
|
}
|
|
|
|
if ref_type == "tag" and PRERELEASE_TAG.fullmatch(ref_name):
|
|
return {
|
|
**result,
|
|
"release": "1",
|
|
"dev": "1",
|
|
"version": ref_name,
|
|
}
|
|
|
|
if event_name == "pull_request" and "dev" in pr_head:
|
|
result["build_test"] = "1"
|
|
|
|
return result
|
|
|
|
|
|
def _is_main_ancestor(sha: str, main_ref: str) -> bool:
|
|
if not sha:
|
|
raise ReleaseClassificationError(
|
|
"GITHUB_SHA is required to verify a stable release tag"
|
|
)
|
|
completed = subprocess.run(
|
|
["git", "merge-base", "--is-ancestor", sha, main_ref],
|
|
check=False,
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if completed.returncode == 0:
|
|
return True
|
|
if completed.returncode == 1:
|
|
return False
|
|
detail = completed.stderr.strip() or "git merge-base failed"
|
|
raise ReleaseClassificationError(
|
|
f"Unable to verify stable tag ancestry against {main_ref}: {detail}"
|
|
)
|
|
|
|
|
|
def _write_outputs(outputs: dict[str, str], destination: str) -> None:
|
|
payload = "".join(f"{key}={value}\n" for key, value in outputs.items())
|
|
if destination:
|
|
with Path(destination).open("a", encoding="utf-8") as output_file:
|
|
output_file.write(payload)
|
|
else:
|
|
sys.stdout.write(payload)
|
|
|
|
|
|
def _parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("--event-name", default=os.environ.get("EVENT_NAME", ""))
|
|
parser.add_argument("--ref-type", default=os.environ.get("REF_TYPE", ""))
|
|
parser.add_argument("--ref-name", default=os.environ.get("REF_NAME", ""))
|
|
parser.add_argument("--pr-head", default=os.environ.get("HEAD_REF", ""))
|
|
parser.add_argument("--pr-title", default=os.environ.get("PR_TITLE", ""))
|
|
parser.add_argument("--sha", default=os.environ.get("GITHUB_SHA", ""))
|
|
parser.add_argument("--main-ref", default="origin/main")
|
|
parser.add_argument(
|
|
"--output",
|
|
default=os.environ.get("GITHUB_OUTPUT", ""),
|
|
help="GitHub Actions output file; stdout when omitted",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def main() -> int:
|
|
args = _parse_args()
|
|
try:
|
|
stable_tag_on_main = None
|
|
if args.ref_type == "tag" and STABLE_TAG.fullmatch(args.ref_name):
|
|
stable_tag_on_main = _is_main_ancestor(args.sha, args.main_ref)
|
|
outputs = classify_release(
|
|
event_name=args.event_name,
|
|
ref_type=args.ref_type,
|
|
ref_name=args.ref_name,
|
|
pr_head=args.pr_head,
|
|
pr_title=args.pr_title,
|
|
stable_tag_on_main=stable_tag_on_main,
|
|
)
|
|
_write_outputs(outputs, args.output)
|
|
except ReleaseClassificationError as exc:
|
|
print(f"::error::{exc}", file=sys.stderr)
|
|
return 1
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|