refactor release notes: reference-style links, --since flag, --dry-run improvements

This commit is contained in:
Richard Macias
2026-02-27 20:11:35 -06:00
parent aeb633e51e
commit 41a037a7f6

View File

@@ -1,6 +1,6 @@
"""Create a GitHub release with tagged JSON artifacts. """Create a GitHub release with tagged JSON artifacts.
Expects `make build` to have already been run. This script only handles Expects `just build` to have already been run. This script only handles
the publish side: tagging, pushing, and creating the GitHub release. the publish side: tagging, pushing, and creating the GitHub release.
Workflow: Workflow:
@@ -12,8 +12,8 @@ Workflow:
6. Create git tag, push, and create GitHub release 6. Create git tag, push, and create GitHub release
Usage: Usage:
make build # build artifacts first just build # build artifacts first
make publish # then publish just release # then publish
Requires: gh (GitHub CLI), git, python3 Requires: gh (GitHub CLI), git, python3
""" """
@@ -41,6 +41,8 @@ STANDARD_JSON = REPO_ROOT / "obtainium-emulation-pack-latest.json"
DUAL_SCREEN_JSON = REPO_ROOT / "obtainium-emulation-pack-dual-screen-latest.json" DUAL_SCREEN_JSON = REPO_ROOT / "obtainium-emulation-pack-dual-screen-latest.json"
APPLICATIONS_JSON = REPO_ROOT / "src" / "applications.json" APPLICATIONS_JSON = REPO_ROOT / "src" / "applications.json"
RELEASES_URL = "https://github.com/RJNY/Obtainium-Emulation-Pack/releases/tag"
SEMVER_PATTERN = re.compile(r"^v?(\d+)\.(\d+)\.(\d+)$") SEMVER_PATTERN = re.compile(r"^v?(\d+)\.(\d+)\.(\d+)$")
@@ -204,13 +206,27 @@ def diff_apps(
# Table rendering for release notes # Table rendering for release notes
def _make_ref_key(app: dict[str, Any]) -> str:
return get_display_name(app).lower().replace(" ", "-").replace("!", "").replace("(", "").replace(")", "")
def make_app_table_row(app: dict[str, Any], change: str) -> str: def make_app_table_row(app: dict[str, Any], change: str) -> str:
display_name = f'<a href="{get_application_url(app)}">{get_display_name(app)}</a>' name = get_display_name(app)
obtainium_link = make_obtainium_link(app) ref_key = _make_ref_key(app)
badge = f'<a href="{obtainium_link}">Add to Obtainium!</a>' if change != "Removed" else "-" app_link = f"[{name}]({get_application_url(app)})"
if change == "Removed":
install = "-"
else:
install = f"[Add to Obtainium!][{ref_key}]"
std = "" if should_include_app(app, "standard") else "" std = "" if should_include_app(app, "standard") else ""
ds = "" if should_include_app(app, "dual-screen") else "" ds = "" if should_include_app(app, "dual-screen") else ""
return f"| {display_name} | {badge} | {change} | {std} | {ds} |" return f"| {app_link} | {install} | {change} | {std} | {ds} |"
def make_app_reference_link(app: dict[str, Any]) -> str:
ref_key = _make_ref_key(app)
obtainium_link = make_obtainium_link(app)
return f"[{ref_key}]: {obtainium_link}"
CHANGES_TABLE_HEADER = ( CHANGES_TABLE_HEADER = (
@@ -223,6 +239,7 @@ def generate_changes_table(
added: list[dict[str, Any]], added: list[dict[str, Any]],
changed: list[dict[str, Any]], changed: list[dict[str, Any]],
removed: list[dict[str, Any]], removed: list[dict[str, Any]],
version: str,
) -> str: ) -> str:
rows: list[tuple[str, dict[str, Any]]] = [] rows: list[tuple[str, dict[str, Any]]] = []
for app in added: for app in added:
@@ -240,6 +257,15 @@ def generate_changes_table(
lines = [CHANGES_TABLE_HEADER] lines = [CHANGES_TABLE_HEADER]
for change, app in rows: for change, app in rows:
lines.append(make_app_table_row(app, change)) lines.append(make_app_table_row(app, change))
lines.append("")
lines.append("Links appear broken? [click here][release]")
lines.append("")
lines.append(f"[release]: {RELEASES_URL}/{version}")
for change, app in rows:
if change != "Removed":
lines.append(make_app_reference_link(app))
return "\n".join(lines) return "\n".join(lines)
@@ -303,6 +329,7 @@ def generate_release_notes(
added: list[dict[str, Any]], added: list[dict[str, Any]],
changed: list[dict[str, Any]], changed: list[dict[str, Any]],
removed: list[dict[str, Any]], removed: list[dict[str, Any]],
version: str,
) -> str: ) -> str:
lines: list[str] = [] lines: list[str] = []
@@ -327,7 +354,7 @@ def generate_release_notes(
if added or changed or removed: if added or changed or removed:
lines.append("## App Changes\n") lines.append("## App Changes\n")
lines.append(generate_changes_table(added, changed, removed)) lines.append(generate_changes_table(added, changed, removed, version))
lines.append("") lines.append("")
return "\n".join(lines) return "\n".join(lines)
@@ -436,6 +463,10 @@ def main() -> None:
"--notes-file", "-f", "--notes-file", "-f",
help="Path to a file containing release notes. Skips generation and editor.", help="Path to a file containing release notes. Skips generation and editor.",
) )
parser.add_argument(
"--since", "-s",
help="Override base tag for diff (e.g. v7.5.0). Defaults to latest tag.",
)
parser.add_argument( parser.add_argument(
"--dry-run", "--dryrun", "--dry-run", "--dryrun",
action="store_true", action="store_true",
@@ -449,7 +480,7 @@ def main() -> None:
print("Fetching tags from remote...") print("Fetching tags from remote...")
run(["git", "fetch", "--tags"]) run(["git", "fetch", "--tags"])
latest = get_latest_tag() latest = args.since or get_latest_tag()
# Determine version # Determine version
if args.version: if args.version:
@@ -463,10 +494,11 @@ def main() -> None:
version = prompt_version(latest) version = prompt_version(latest)
# Check if tag already exists # Check if tag already exists
result = run(["git", "tag", "-l", version], capture=True) if not args.dry_run:
if version in result.stdout.strip().splitlines(): result = run(["git", "tag", "-l", version], capture=True)
print(f"Error: Tag {version} already exists.") if version in result.stdout.strip().splitlines():
sys.exit(1) print(f"Error: Tag {version} already exists.")
sys.exit(1)
# Detect changed apps # Detect changed apps
print("\nDetecting app changes...") print("\nDetecting app changes...")
@@ -485,7 +517,7 @@ def main() -> None:
notes = args.notes notes = args.notes
else: else:
# Auto-generate and open in editor # Auto-generate and open in editor
notes = generate_release_notes(latest, added, changed, removed) notes = generate_release_notes(latest, added, changed, removed, version)
if args.dry_run: if args.dry_run:
tmp_dir = REPO_ROOT / "tmp" tmp_dir = REPO_ROOT / "tmp"
@@ -523,7 +555,7 @@ def main() -> None:
for f in (STANDARD_JSON, DUAL_SCREEN_JSON): for f in (STANDARD_JSON, DUAL_SCREEN_JSON):
if not f.exists(): if not f.exists():
print(f"Error: Expected artifact not found: {f}") print(f"Error: Expected artifact not found: {f}")
print("Did you run `make build` first?") print("Did you run `just build` first?")
sys.exit(1) sys.exit(1)
# Show summary before proceeding # Show summary before proceeding
@@ -546,7 +578,7 @@ def main() -> None:
print("Aborted.") print("Aborted.")
sys.exit(0) sys.exit(0)
# Commit any uncommitted changes (e.g. from `make build`) # Commit any uncommitted changes (e.g. from `just build`)
if not check_working_tree_clean(): if not check_working_tree_clean():
print() print()
print("Working tree has changes. Committing...") print("Working tree has changes. Committing...")
@@ -563,7 +595,7 @@ def main() -> None:
print() print()
print(f"Release {version} created successfully!") print(f"Release {version} created successfully!")
print(f"https://github.com/RJNY/Obtainium-Emulation-Pack/releases/tag/{version}") print(f"{RELEASES_URL}/{version}")
finally: finally:
cleanup(versioned_copies) cleanup(versioned_copies)