summaryrefslogtreecommitdiff
path: root/.github/scripts/generate_changelog.py
diff options
context:
space:
mode:
authorTranch <tranch.xiao@gmail.com>2026-10-16 01:32:11 +0800
committerGitHub <noreply@github.com>2026-10-16 01:32:11 +0800
commit6c001a951ae122b34c86eefcd9abc8f3a1e32a26 (patch)
tree219c0840868e7494fc50d24ea952fdaaebefedd2 /.github/scripts/generate_changelog.py
parent3f7496d580f185c0974c5edc107570c471d0cdde (diff)
parent3fbfb2bd4bb5e27509e8697018cddd105fca8dad (diff)
downloadAeroCore-OS-6c001a951ae122b34c86eefcd9abc8f3a1e32a26.tar.gz
AeroCore-OS-6c001a951ae122b34c86eefcd9abc8f3a1e32a26.zip
Merge pull request #33 from AeroCore-IO/testing44.20261015
Promote tested Bazzite 44.20261012 updates to stable
Diffstat (limited to '.github/scripts/generate_changelog.py')
-rw-r--r--.github/scripts/generate_changelog.py194
1 files changed, 167 insertions, 27 deletions
diff --git a/.github/scripts/generate_changelog.py b/.github/scripts/generate_changelog.py
index c4ea8a2..4c5d7c1 100644
--- a/.github/scripts/generate_changelog.py
+++ b/.github/scripts/generate_changelog.py
@@ -7,6 +7,8 @@ import argparse
import json
import os
import re
+import urllib.error
+import urllib.request
import subprocess
import tempfile
from pathlib import Path
@@ -73,6 +75,111 @@ def markdown_version(value: str | None) -> str:
return value or "—"
+MAJOR_PACKAGES = {
+ "kernel": "Kernel",
+ "kernel-core": "Kernel",
+ "kernel-modules": "Kernel",
+ "kernel-modules-core": "Kernel",
+ "kernel-modules-extra": "Kernel",
+ "kernel-tools": "Kernel",
+ "kernel-devel": "Kernel",
+ "kernel-headers": "Kernel",
+ "kernel-lts": "Kernel (Nvidia LTS)",
+ "linux-firmware": "Firmware",
+ "mesa-dri-drivers": "Mesa",
+ "mesa-filesystem": "Mesa",
+ "mesa-libEGL": "Mesa",
+ "mesa-libGL": "Mesa",
+ "mesa-libgbm": "Mesa",
+ "mesa-va-drivers": "Mesa",
+ "mesa-vulkan-drivers": "Mesa",
+ "gamescope": "Gamescope",
+ "gamescope-session": "Gamescope Session",
+ "mangohud": "MangoHUD",
+ "inputplumber": "InputPlumber",
+ "opengamepadui": "OpenGamepadUI",
+ "powerstation": "PowerStation",
+ "steamos-manager": "SteamOS-Manager",
+ "umu-launcher": "UMU Launcher",
+ "bazaar": "Bazaar",
+ "distrobox": "Distrobox",
+ "gnome-shell": "Gnome",
+ "plasma-desktop": "KDE",
+ "waydroid": "Waydroid",
+}
+
+
+def major_package_rows(
+ current_packages: dict[str, str],
+ previous_packages: dict[str, str] | None,
+) -> list[str]:
+ selected: dict[str, tuple[str | None, str | None]] = {}
+ for name, current in current_packages.items():
+ display_name = MAJOR_PACKAGES.get(name)
+ if not display_name:
+ continue
+ previous = (previous_packages or {}).get(name)
+ selected[display_name] = (previous, current)
+
+ rows = []
+ for display_name, (old, new) in selected.items():
+ version = markdown_version(new)
+ if old and new and old != new:
+ version = f"{old} ➡️ {new}"
+ rows.append(f"| **{display_name}** | {version} |")
+ return rows
+
+
+def all_image_rows(changes: list[tuple[str, str | None, str | None]]) -> list[str]:
+ rows = []
+ for name, old, new in changes:
+ icon = "➕" if old is None else "➖" if new is None else "🔄"
+ rows.append(
+ f"| {icon} | `{name}` | {markdown_version(old)} | {markdown_version(new)} |"
+ )
+ return rows
+
+
+def upstream_release_commit(tag: str) -> str | None:
+ try:
+ request = urllib.request.Request(
+ f"https://api.github.com/repos/ublue-os/bazzite/git/ref/tags/{tag}",
+ headers={"Accept": "application/vnd.github+json", "User-Agent": "AeroCore-OS-release-notes"},
+ )
+ with urllib.request.urlopen(request, timeout=30) as response:
+ data = json.load(response)
+ obj = data["object"]
+ if obj.get("type") == "tag":
+ request = urllib.request.Request(
+ f"https://api.github.com/repos/ublue-os/bazzite/git/tags/{obj['sha']}",
+ headers={"Accept": "application/vnd.github+json", "User-Agent": "AeroCore-OS-release-notes"},
+ )
+ with urllib.request.urlopen(request, timeout=30) as response:
+ obj = json.load(response)["object"]
+ return obj.get("sha")
+ except (urllib.error.URLError, urllib.error.HTTPError, KeyError, json.JSONDecodeError):
+ return None
+
+
+def upstream_compare_commits(start_revision: str | None, end_revision: str | None) -> list[tuple[str, str, str]]:
+ if not start_revision or not end_revision or start_revision == end_revision:
+ return []
+ request = urllib.request.Request(
+ f"https://api.github.com/repos/ublue-os/bazzite/compare/{start_revision}...{end_revision}",
+ headers={"Accept": "application/vnd.github+json", "User-Agent": "AeroCore-OS-release-notes"},
+ )
+ with urllib.request.urlopen(request, timeout=60) as response:
+ compare = json.load(response)
+ return [
+ (
+ item["sha"],
+ item["commit"]["message"].splitlines()[0],
+ item["commit"].get("author", {}).get("name", "Unknown"),
+ )
+ for item in compare.get("commits", [])
+ ]
+
+
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--image", required=True, help="registry/repository without tag")
@@ -89,6 +196,7 @@ def main() -> None:
current_info = image_info(args.image, args.current)
current_packages = package_map(fetch_sbom(args.image, current_info["Digest"]))
+ base_image = current_info.get("Labels", {}).get("org.opencontainers.image.base.name")
previous_packages: dict[str, str] | None = None
if previous:
try:
@@ -107,47 +215,79 @@ def main() -> None:
changes.append((name, old, new))
previous_info = image_info(args.image, previous) if previous else {}
+ previous_base = previous_info.get("Labels", {}).get("org.opencontainers.image.base.name", "")
+ previous_base_tag = previous_base.rpartition(":")[2]
+ current_base_tag = base_image.rpartition(":")[2] if base_image else ""
+ start_revision = upstream_release_commit(previous_base_tag) if previous_base_tag else None
+ end_revision = upstream_release_commit(current_base_tag) if current_base_tag else None
+ upstream_commits = upstream_compare_commits(start_revision, end_revision)
+ upstream_range_available = bool(start_revision and end_revision)
+
start_revision = previous_info.get("Labels", {}).get("org.opencontainers.image.revision")
end_revision = current_info.get("Labels", {}).get("org.opencontainers.image.revision")
- commits: list[str] = []
- if start_revision and end_revision:
+ aerocore_commits: list[tuple[str, str, str]] = []
+ if start_revision and end_revision and start_revision != end_revision:
try:
- commits = run(
- "git",
- "log",
- "--format=%H%x09%s%x09%an",
- f"{start_revision}..{end_revision}",
- ).splitlines()
+ aerocore_commits = [
+ (item.split("\t", 2)[0], item.split("\t", 2)[1], item.split("\t", 2)[2])
+ for item in run(
+ "git", "log", "--format=%H%x09%s%x09%an", f"{start_revision}..{end_revision}"
+ ).splitlines()
+ ]
except subprocess.CalledProcessError as error:
- # A release may be built after histories are intentionally joined
- # or rewritten. In that case the previous image revision can be
- # valid metadata but not a reachable Git revision in this checkout.
- # Package changes and the release itself remain valid, so omit the
- # commit section instead of failing the whole release job.
+ # Release metadata can point to revisions outside this checkout's
+ # history after histories are joined or rewritten. Keep the package
+ # and upstream changelog sections and omit only AeroCore commits.
print(
- "Warning: unable to determine commit range "
+ "Warning: unable to determine AeroCore commit range "
f"{start_revision}..{end_revision}: {error}"
)
lines = [args.handwritten.strip() or f"This is an automatically generated changelog for release `{args.current}`.", ""]
+ if base_image:
+ base_name, _, base_tag = base_image.rpartition(":")
+ if base_name and base_tag:
+ base_repo = base_name.removeprefix("docker://")
+ upstream_version = re.sub(r"^(?:stable|testing|testing-candidate)-", "", base_tag)
+ lines.append(
+ f"Based on upstream [{upstream_version}](https://github.com/ublue-os/bazzite/releases/tag/{base_tag}) "
+ f"(`{base_repo}`)."
+ )
+ else:
+ lines.append(f"Based on upstream image `{base_image}`.")
+ lines.append("")
if previous:
- lines.append(f"From previous `{args.channel}` version `{previous}` there have been the following package changes.")
- else:
- lines.append("No previous release was found for this channel; package changes are shown against an empty baseline.")
- lines += ["", "### Package changes", "", "| Name | Previous | New |", "| --- | --- | --- |"]
- if previous and previous_packages is None:
- lines.append("| — | Previous release has no attached SBOM | Diff starts with the next release |")
- elif not changes:
- lines.append("| — | — | — |")
+ lines.append(f"From previous `{args.channel}` version `{previous}` there have been the following changes. One package per new version shown.")
else:
- lines += [f"| `{name}` | {markdown_version(old)} | {markdown_version(new)} |" for name, old, new in changes]
+ lines.append("No previous release was found for this channel; changes are shown against an empty baseline.")
+
+ lines += ["", "### Major packages", "", "| Name | Version |", "| --- | --- |"]
+ major_rows = major_package_rows(current_packages, previous_packages)
+ lines.extend(major_rows or ["| — | No major package changes |"])
lines += ["", "### Commits", "", "| Hash | Subject | Author |", "| --- | --- | --- |"]
- for commit in commits:
- commit_hash, subject, author = commit.split("\t", 2)
+ if upstream_commits:
+ lines.append("| **Upstream Bazzite** | | |")
+ for commit_hash, subject, author in upstream_commits:
+ lines.append(f"| [{commit_hash[:7]}](https://github.com/ublue-os/bazzite/commit/{commit_hash}) | {subject} | {author} |")
+ elif upstream_range_available:
+ lines.append("| **Upstream Bazzite** | No commits between the selected base versions | — |")
+ else:
+ lines.append("| **Upstream Bazzite** | Commit range unavailable (previous image may predate base image metadata) | — |")
+ if aerocore_commits:
+ lines.append("| **AeroCore OS** | | |")
+ for commit_hash, subject, author in aerocore_commits:
lines.append(f"| [{commit_hash[:7]}](https://github.com/{args.repository}/commit/{commit_hash}) | {subject} | {author} |")
- if not commits:
- lines.append("| — | No commit range available | — |")
+ if not aerocore_commits:
+ lines.append("| **AeroCore OS** | No commits between the selected image versions | — |")
+
+ lines += ["", "### All Images", "", "| | Name | Previous | New |", "| --- | --- | --- | --- |"]
+ if previous and previous_packages is None:
+ lines.append("| — | Previous release has no attached SBOM | — | Diff starts with the next release |")
+ elif not changes:
+ lines.append("| — | No package changes | — | — |")
+ else:
+ lines.extend(all_image_rows(changes))
lines += ["", "### How to switch", "", "For current users, switch to the channel image with:", "", "```bash", f"sudo bootc switch {args.image}:{args.channel}", "", "# Or switch to this exact image:", f"sudo bootc switch {args.image}:{args.current}", "```", ""]
args.output.write_text("\n".join(lines))