diff options
| -rw-r--r-- | .github/scripts/generate_changelog.py | 194 | ||||
| -rw-r--r-- | .github/workflows/build.yml | 6 | ||||
| -rw-r--r-- | Containerfile | 6 | ||||
| -rw-r--r-- | Justfile | 7 | ||||
| -rwxr-xr-x | build_files/install-instruments.sh | 32 | ||||
| -rw-r--r-- | image-template.env | 2 |
6 files changed, 204 insertions, 43 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)) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b6b8df..5ec0da4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,7 +24,7 @@ env: IMAGE_LOGO_URL: ${{ vars.IMAGE_LOGO_URL || 'https://avatars.githubusercontent.com/u/120078124?s=200&v=4' }} DEFAULT_TAG: ${{ vars.DEFAULT_TAG || 'stable' }} BIB_IMAGE: ${{ vars.BIB_IMAGE || 'quay.io/centos-bootc/bootc-image-builder:latest' }} - BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20260908' }} + BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20261012' }} FLATPAK_REMOTE_URL: ${{ vars.FLATPAK_REMOTE_URL }} HOMEBREW_BOTTLE_DOMAIN: ${{ vars.HOMEBREW_BOTTLE_DOMAIN }} HOMEBREW_API_DOMAIN: ${{ vars.HOMEBREW_API_DOMAIN }} @@ -95,7 +95,7 @@ jobs: - name: Generate Version and Tags id: gen-version env: - BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20260908' }} + BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20261012' }} run: | set -euo pipefail @@ -162,7 +162,7 @@ jobs: id: build-image env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20260908' }} + BASE_IMAGE: ${{ vars.BASE_IMAGE || 'ghcr.io/ublue-os/bazzite-deck:stable-44.20261012' }} FLATPAK_REMOTE_URL: ${{ vars.FLATPAK_REMOTE_URL }} HOMEBREW_BOTTLE_DOMAIN: ${{ vars.HOMEBREW_BOTTLE_DOMAIN }} HOMEBREW_API_DOMAIN: ${{ vars.HOMEBREW_API_DOMAIN }} diff --git a/Containerfile b/Containerfile index 11d6ebf..794b46d 100644 --- a/Containerfile +++ b/Containerfile @@ -1,5 +1,5 @@ # Allow build scripts to be referenced without being copied into the final image -ARG BASE_IMAGE="ghcr.io/ublue-os/bazzite-deck:stable-44.20260908" +ARG BASE_IMAGE="ghcr.io/ublue-os/bazzite-deck:stable-44.20261012" FROM scratch AS ctx COPY build_files / @@ -15,8 +15,8 @@ ARG VERSION_TAG="latest" ARG VERSION_PRETTY="latest" ARG OSTREE_IMAGE_REF="" ## Other possible base images include: -# ghcr.io/ublue-os/bazzite-deck:stable-44.20260908 -# ghcr.io/ublue-os/bazzite-deck:testing-44.20260908.3 +# ghcr.io/ublue-os/bazzite-deck:stable-44.20261012 +# ghcr.io/ublue-os/bazzite-deck:testing-44.202609019 ### [IM]MUTABLE /opt ## Some bootable images, like Fedora, have /opt symlinked to /var/opt, in order to @@ -9,9 +9,9 @@ export image_logo_url := env_var("IMAGE_LOGO_URL") export default_tag := env_var("DEFAULT_TAG") export bib_image := env_var("BIB_IMAGE") export IMAGE_BRANCH := env_var_or_default("IMAGE_BRANCH", "testing-candidate") -export VERSION_TAG := env_var_or_default("VERSION_TAG", default_tag) -export VERSION_PRETTY := env_var_or_default("VERSION_PRETTY", VERSION_TAG) -export BASE_IMAGE := env_var_or_default("BASE_IMAGE", "ghcr.io/ublue-os/bazzite-deck:stable-44.20260908") +export VERSION_TAG := env_var_or_default("VERSION_TAG", "") +export VERSION_PRETTY := env_var_or_default("VERSION_PRETTY", "") +export BASE_IMAGE := env_var_or_default("BASE_IMAGE", "ghcr.io/ublue-os/bazzite-deck:stable-44.20261012") export OSTREE_IMAGE_REF := env_var_or_default("OSTREE_IMAGE_REF", "ostree-image-signed:docker://ghcr.io/" + repo_organization + "/" + image_name) export LIVE_BASE_IMAGE := env_var_or_default("LIVE_BASE_IMAGE", "quay.io/fedora/fedora-kinoite:44") export TITANOBOA_REPOSITORY := env_var_or_default("TITANOBOA_REPOSITORY", "https://github.com/Zeglius/titanoboa.git") @@ -137,6 +137,7 @@ build $target_image=image_name $tag=default_tag: "--build-arg" "IMAGE_BRANCH={{ tag }}" "--build-arg" "VERSION_TAG=${VERSION_TAG:-{{ tag }}}" "--build-arg" "VERSION_PRETTY=${VERSION_PRETTY:-${VERSION_TAG:-{{ tag }}}}" + "--label" "org.opencontainers.image.base.name=${BASE_IMAGE:-ghcr.io/ublue-os/bazzite-deck:stable-44.20261012}" ) for arg in BASE_IMAGE FLATPAK_REMOTE_URL HOMEBREW_BOTTLE_DOMAIN HOMEBREW_API_DOMAIN OSTREE_IMAGE_REF INSTRUMENTS_ENABLED INSTRUMENTS_RELEASE_REPOSITORY INSTRUMENTS_VERSION INSTRUMENTS_RELEASE_API_BASE; do diff --git a/build_files/install-instruments.sh b/build_files/install-instruments.sh index f85bd3b..0cf5fe6 100755 --- a/build_files/install-instruments.sh +++ b/build_files/install-instruments.sh @@ -48,7 +48,29 @@ github_curl() { args+=(--header "Authorization: Bearer $(</run/secrets/GITHUB_TOKEN)") fi - curl "${args[@]}" "$@" + if curl "${args[@]}" "$@"; then + return 0 + fi + + # The default release repository is public. A workflow token from another + # repository can be rejected by GitHub (403), even though anonymous access + # to the same release is allowed. Retry without credentials so a stale or + # cross-repository token cannot break public image builds. + if [[ -s /run/secrets/GITHUB_TOKEN ]]; then + local anonymous_args=( + --fail + --location + --retry 3 + --retry-delay 2 + --retry-max-time 60 + --silent + --show-error + ) + curl "${anonymous_args[@]}" "$@" + return $? + fi + + return 1 } if [[ "${INSTRUMENTS_VERSION}" == "latest" ]]; then @@ -129,13 +151,12 @@ required_paths=( install.sh appimage/AppRun appimage/AppRun.wrapped - sidecar/hhd-input-helper.cjs - sidecar/vendor/hhd-input-helper/instruments_hhd_input/__init__.py + sidecar/sidecar-main.cjs + sidecar/aerocore-bridge.cjs runtime/avionics runtime/cabin runtime/wing runtime/node/bin/node - python-wheelhouse pkg/systemd/ac-avionics.service pkg/systemd/ac-cabin.service pkg/systemd/ac-wing.service @@ -151,8 +172,7 @@ for path in "${required_paths[@]}"; do fi done -if [[ ! -f "$payload/pkg/aerocore-booster.sh" && - ! -f "$payload/pkg/instruments-ng.sh" ]]; then +if [[ ! -f "$payload/pkg/aerocore-booster.sh" ]]; then echo "Booster launcher is missing from the release payload" >&2 exit 1 fi diff --git a/image-template.env b/image-template.env index 3a5a03f..ea5dead 100644 --- a/image-template.env +++ b/image-template.env @@ -16,7 +16,7 @@ BIB_IMAGE="quay.io/centos-bootc/bootc-image-builder:latest" # Keep this on bazzite-deck for Steam Deck, game mode, and handheld customizations. # Use a concrete tag during transition windows if stable/testing moves underneath you. -BASE_IMAGE="ghcr.io/ublue-os/bazzite-deck:stable-44.20260908" +BASE_IMAGE="ghcr.io/ublue-os/bazzite-deck:stable-44.20261012" # Optional mirror settings. FLATPAK_REMOTE_URL="" |
