From d15b11db6a7281da727dfa61bd8527eab0d37477 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 9 May 2026 20:35:21 +0000 Subject: [PATCH 1/2] Initial commit with task details Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: https://github.com/link-foundation/python-ai-driven-development-pipeline-template/issues/6 --- .gitkeep | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..03eef28 --- /dev/null +++ b/.gitkeep @@ -0,0 +1 @@ +# .gitkeep file auto-generated at 2026-05-09T20:35:21.453Z for PR creation at branch issue-6-79b9bc4dc763 for issue https://github.com/link-foundation/python-ai-driven-development-pipeline-template/issues/6 \ No newline at end of file From 5987d51437c6dc148569a5ef225580f507ffc9ff Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 9 May 2026 20:41:00 +0000 Subject: [PATCH 2/2] fix(ci): improve GitHub release metadata --- .gitkeep | 1 - ...0260509_204000_issue_6_release_metadata.md | 3 + scripts/create_github_release.py | 52 +++++++++++--- tests/test_create_github_release.py | 69 +++++++++++++++++++ 4 files changed, 115 insertions(+), 10 deletions(-) delete mode 100644 .gitkeep create mode 100644 changelog.d/20260509_204000_issue_6_release_metadata.md create mode 100644 tests/test_create_github_release.py diff --git a/.gitkeep b/.gitkeep deleted file mode 100644 index 03eef28..0000000 --- a/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# .gitkeep file auto-generated at 2026-05-09T20:35:21.453Z for PR creation at branch issue-6-79b9bc4dc763 for issue https://github.com/link-foundation/python-ai-driven-development-pipeline-template/issues/6 \ No newline at end of file diff --git a/changelog.d/20260509_204000_issue_6_release_metadata.md b/changelog.d/20260509_204000_issue_6_release_metadata.md new file mode 100644 index 0000000..22529c0 --- /dev/null +++ b/changelog.d/20260509_204000_issue_6_release_metadata.md @@ -0,0 +1,3 @@ +### Fixed + +- GitHub release creation now supports configurable tag prefixes, language-labeled release titles, and automatic PyPI badge insertion when release notes do not already contain a shields.io badge. diff --git a/scripts/create_github_release.py b/scripts/create_github_release.py index 6d62d3a..1208eb8 100755 --- a/scripts/create_github_release.py +++ b/scripts/create_github_release.py @@ -3,10 +3,12 @@ Create a GitHub release from CHANGELOG.md content. Usage: - python scripts/create_github_release.py --version VERSION --repository REPO + python scripts/create_github_release.py --version VERSION --repository REPO \ + [--tag-prefix PREFIX] [--language LANGUAGE] Example: - python scripts/create_github_release.py --version 1.2.3 --repository owner/repo + python scripts/create_github_release.py --version 1.2.3 --repository owner/repo \ + --tag-prefix python_v --language Python Environment variables: GH_TOKEN or GITHUB_TOKEN: GitHub token for authentication @@ -71,14 +73,30 @@ def extract_changelog_entry(changelog_path: Path, version: str) -> str: return entry if entry else f"Release {version}" +def append_pypi_badge_if_missing(release_notes: str, version: str) -> str: + """Append a PyPI version badge unless a shields.io badge is already present.""" + if "img.shields.io" in release_notes.lower(): + return release_notes + + badge = f"![PyPI](https://img.shields.io/badge/pypi-{version}-blue.svg)" + return f"{release_notes.rstrip()}\n\n{badge}" + + def create_release( - version: str, repository: str, release_notes: str, prerelease: bool = False + version: str, + repository: str, + release_notes: str, + prerelease: bool = False, + tag_prefix: str = "v", + language: str = "Python", ) -> None: """Create a GitHub release using gh CLI.""" - tag = f"v{version}" + tag = f"{tag_prefix}{version}" + title = f"[{language}] {version}" print(f"\nCreating GitHub release for {tag}...") print(f"Repository: {repository}") + print(f"Title: {title}") print(f"Prerelease: {prerelease}") print(f"\nRelease notes:\n{release_notes}\n") @@ -90,7 +108,7 @@ def create_release( "--repo", repository, "--title", - tag, + title, "--notes", release_notes, ] @@ -124,6 +142,16 @@ def main() -> int: action="store_true", help="Mark as prerelease", ) + parser.add_argument( + "--tag-prefix", + default="v", + help='Tag prefix for the release (default "v")', + ) + parser.add_argument( + "--language", + default="Python", + help='Language label for the release title (default "Python")', + ) args = parser.parse_args() @@ -150,11 +178,17 @@ def main() -> int: changelog_path = project_root / "CHANGELOG.md" try: - # Extract changelog entry release_notes = extract_changelog_entry(changelog_path, args.version) - - # Create release - create_release(args.version, args.repository, release_notes, args.prerelease) + release_notes = append_pypi_badge_if_missing(release_notes, args.version) + + create_release( + args.version, + args.repository, + release_notes, + args.prerelease, + args.tag_prefix, + args.language, + ) return 0 diff --git a/tests/test_create_github_release.py b/tests/test_create_github_release.py new file mode 100644 index 0000000..dea3648 --- /dev/null +++ b/tests/test_create_github_release.py @@ -0,0 +1,69 @@ +"""Tests for scripts/create_github_release.py.""" + +from __future__ import annotations + +import importlib.util +import subprocess +import sys +from pathlib import Path + + +SCRIPT_PATH = ( + Path(__file__).resolve().parent.parent / "scripts" / "create_github_release.py" +) +spec = importlib.util.spec_from_file_location("create_github_release", SCRIPT_PATH) +module = importlib.util.module_from_spec(spec) +sys.modules[spec.name] = module +spec.loader.exec_module(module) # type: ignore[union-attr] + + +def test_create_release_uses_tag_prefix_and_language_title(monkeypatch) -> None: + """Release creation should separate tag format from display title.""" + commands = [] + + def fake_run_command(cmd, check=True): + commands.append(cmd) + return subprocess.CompletedProcess(cmd, 0, "", "") + + monkeypatch.setattr(module, "run_command", fake_run_command) + + module.create_release( + version="1.2.3", + repository="owner/repo", + release_notes="Release notes", + prerelease=False, + tag_prefix="python_v", + language="Python", + ) + + assert commands == [ + [ + "gh", + "release", + "create", + "python_v1.2.3", + "--repo", + "owner/repo", + "--title", + "[Python] 1.2.3", + "--notes", + "Release notes", + ], + ] + + +def test_append_pypi_badge_if_missing_adds_static_version_badge() -> None: + """Release notes should get a PyPI badge before gh creates the release.""" + body = module.append_pypi_badge_if_missing("Release notes", "1.2.3") + + assert "Release notes" in body + assert "https://img.shields.io/badge/pypi-1.2.3-blue.svg" in body + + +def test_append_pypi_badge_if_missing_does_not_duplicate_badge() -> None: + """Existing shields.io badges should be preserved without duplication.""" + existing = ( + "Release notes\n\n![PyPI](https://img.shields.io/badge/pypi-1.2.3-blue.svg)" + ) + + assert module.append_pypi_badge_if_missing(existing, "1.2.3") == existing