diff --git a/experimentations/fetch-serials.py b/experimentations/fetch-serials.py deleted file mode 100644 index 4b8dfb5..0000000 --- a/experimentations/fetch-serials.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/python3 -# -# This script was generated using Gemini (model "Thinking with 3 Pro", whatever that means 🤷) -# Here is the prompt I used: -# BEGIN PROMPT -# * There is the https://cdimage.ubuntu.com/ server with the following directory tree: -# // -# * `` can be any value in the following: daily, daily-live, daily-preinstalled, daily-minimal -# * `` is usually a date in the form 20251201, but sometimes is 20251201.1 -# * Please write a Python script that browse this server and gets the latest serial for each product/type combination -# END PROMPT -# -# See git history to read the problems this script had. -# The most pressing issues have been fixed and the script can be used to populate the YAML. -# This script doesn't do anything else than printing information, so it's fairly safe to use. - -import requests -from bs4 import BeautifulSoup -import re - -# Configuration -BASE_URL = "https://cdimage.ubuntu.com" -ALLOWED_TYPES = {"daily", "daily-live", "daily-preinstalled", "daily-minimal"} - - -def get_links(url): - """ - Fetches the HTML content of a directory listing and returns a list of - subdirectory names (ending with '/'). - """ - try: - response = requests.get(url, timeout=10) - response.raise_for_status() - soup = BeautifulSoup(response.text, "html.parser") - - links = [] - for link in soup.find_all("a"): - href = link.get("href") - # Apache directory listings provide relative links. - # We look for directories (ending in /) and ignore parent links. - if ( - href - and href.endswith("/") - and not href.startswith("?") - and href not in ["/", "../"] - ): - links.append(href.rstrip("/")) - return links - except requests.exceptions.RequestException: - # Silently fail for 404s or permission errors on specific folders - # print(f"Skipping {url}: {e}", file=sys.stderr) - return [] - - -def parse_serial(serial_str): - """ - Parses a serial string into a tuple (date, version) for accurate sorting. - Example: '20251201' -> (20251201, 0) - Example: '20251201.1' -> (20251201, 1) - """ - # Regex to match YYYYMMDD and optional .Version - match = re.match(r"^(\d{8})(?:\.(\d+))?$", serial_str) - if match: - date_part = int(match.group(1)) - # If no suffix version is present, assume 0 - version_part = int(match.group(2)) if match.group(2) else 0 - return (date_part, version_part) - return None - - -def main(): - print(f"Browsing {BASE_URL} for latest serials...\n") - print(f"{'Product':<25} | {'Type':<20} | {'Latest Serial':<12} | {'URL'}") - print("-" * 100) - - # Step 1: Browse root to find all products - products = get_links(BASE_URL + "/") - - for product in products: - # Skip common non-product directories to save time/errors - if product in ["releases", "experimental", "noble", "streams"]: - continue - - product_url = f"{BASE_URL}/{product}/" - - # Step 2: Browse the product directory to find types - types = get_links(product_url) - - for type_name in types: - if type_name in ALLOWED_TYPES: - type_url = f"{product_url}{type_name}/" - - # Step 3: Browse the type directory to find serials - build_dirs = get_links(type_url) - - valid_serials = [] - for build_dir in build_dirs: - # 'current' and 'pending' are often symlinks, but we want the actual serial date - serial = parse_serial(build_dir) - if serial: - valid_serials.append( - { - "original": build_dir, - "serial": serial, - "url": f"{type_url}{build_dir}", - } - ) - - if valid_serials: - # Sort by the serial tuple (Date, Version) in descending order - valid_serials.sort(key=lambda x: x["serial"], reverse=True) - latest_build = valid_serials[0] - - print( - f"{product:<25} | {type_name:<20} | {latest_build['original']:<13} | {latest_build['url']}" - ) - print("-" * 100) - print( - "Please go check each link and verify all images while populating the milestone YAML file with the candidate serials" - ) - - -if __name__ == "__main__": - main() diff --git a/experimentations/publish-image-set b/experimentations/publish-image-set index 65b5e10..2e630e0 100755 --- a/experimentations/publish-image-set +++ b/experimentations/publish-image-set @@ -1,61 +1,186 @@ #!/usr/bin/python3 - -from pathlib import Path import argparse -import yaml - - -def parse_args(): - args = argparse.ArgumentParser() - args.add_argument( - "--data", - "-d", - type=Path, - required=True, - help="Path to the data.yaml file describing the release", - ) - - return args.parse_args() - +import requests + + +def slugify(string): + return string.lower().replace(" ", "-") + + +def str_image(image): + return f"{image['os']: <25} {image['arch']: <14} {image['name']: <50} {image['version']: <12}" + + +def is_official(os): + if os in ["ubuntu-desktop", "ubuntu-server", "ubuntu-wsl"]: + return True + return False + + +def get_artifacts(series, pre_publish): + """ + Yields all 'Approved' artifacts for a given release using the + Ubuntu Test Observer API. + + :param release_name: The Ubuntu release codename (e.g., 'noble', 'jammy') + """ + base_url = "https://tests-api.ubuntu.com" + params = { + "family": "image", + } + + response = requests.get(base_url + "/v1/artefacts", params=params) + response.raise_for_status() + + artifacts = [a for a in response.json() if a["release"] == series] + if pre_publish: + artifacts = [a for a in artifacts if is_official(a["os"])] + print(f"Found {len(artifacts)} artifacts") + artifacts = [a for a in artifacts if a["status"] == "APPROVED"] + print(f"Found {len(artifacts)} approved artifacts") + for artifact in artifacts: + response = requests.get(base_url + f"/v1/artefacts/{artifact['id']}/builds") + response.raise_for_status() + build = response.json()[0] + image = { + "name": artifact["name"], + "os": artifact["os"], + "version": artifact["version"], + "arch": build["architecture"].split(".")[0], # fixup old cdimage bug + } + yield image + + +def get_type(image): + os = image["os"] + dir = image["dir"] + + if os == "ubuntu-base": + return "base" + if os == "ubuntu-mini-iso": + return "mini-iso" + if os == "ubuntu-wsl": + return "wsl" + if os == "ubuntu-server": + if "daily-preinstalled" in dir: + return "preinstalled-server" + if "daily-live" in dir: + return "live-server" + if os in ["ubuntu-desktop", "edubuntu"]: + if dir == "daily-preinstalled": + return "preinstalled-desktop" + if os == "xubuntu": + if dir == "daily-minimal": + return "minimal" + + return "desktop" + + +def get_dir(image): + os = image["os"] + name = image["name"] + + dir = "daily-live" + if os == "ubuntu-base": + dir = "daily" + if name.startswith("dangerous"): + dir = "daily-dangerous" + if "minimal" in name: + dir = "daily-minimal" + if "preinstalled" in name: + dir = "daily-preinstalled" + if os in ["ubuntu-server", "ubuntu-wsl"]: + dir = f"{os}/{dir}" + return dir + + +def get_project(image): + os = image["os"] + if os in ["ubuntu-desktop", "ubuntu-server", "ubuntu-wsl"]: + return "ubuntu" + return os + + +def get_official(image, milestone, pre_publish): + os = image["os"] + + if "snapshot" in milestone.lower(): + return "named" + if is_official(os): + if pre_publish: + return "poolonly" + else: + return "yes" + return "no" -def parse_milestone(milestone: Path): - return yaml.safe_load(milestone.read_text()) +def render_publishing_script(milestone, images): + milestone_slug = slugify(milestone) -def render_publishing_script( - milestone: dict[str, str], - images: dict[str, dict[str, str]], -): print(""" ## make backup: cd ~/cdimage/; rm -rf www.prev; cp -al www www.prev; cd www """) - for image_name, image_data in images.items(): - print(f"\n# {image_name}") - if image_data["serial"]: - print( - image_data["publishing_command"] - .replace("SERIAL", image_data["serial"]) - .replace("MILESTONE_SLUG", milestone["slug"]) - ) - else: - print("# No serial set for publication, skipping") + for image in images: + print(f"\n# {str_image(image)}") + print( + f"ARCHES='{image['arch']}' for-project {image['project']} " + f"publish-release {image['dir']} {image['version']} {image['type']} {image['official']} {milestone_slug}" + ) print(f""" ## fix name in headers: -find full -path '*/{milestone["slug"]}*HEADER.html' | xargs sed -i 's/Daily Build/{milestone["name"]}/' +find full -path '*/{milestone_slug}*HEADER.html' | xargs sed -i 's/Daily Build/{milestone}/' ## check changes against www.prev: diff -u <(cd ../www.prev/full && find | sort) <(cd full && find | sort) | less """) +def parse_args(): + args = argparse.ArgumentParser() + args.add_argument( + "--series", + "-s", + type=str, + required=True, + help="The series to release (e.g. 'noble', 'resolute'...)", + ) + args.add_argument( + "--milestone", + "-m", + type=str, + required=True, + help="The milestone to release (e.g. 'Final', 'Snapshot 1'...)", + ) + args.add_argument( + "--pre-publish", + action="store_true", + help="Whether to pre-publish or do a full release", + ) + + return args.parse_args() + + def main(): args = parse_args() - input_data = parse_milestone(args.data) - render_publishing_script(input_data["milestone"], input_data["images"]) + print(f"Fetching approved artifacts for {args.series}...") + + images = list(get_artifacts(args.series, args.pre_publish)) + images.sort(key=lambda a: a["os"]) + print(" Summary of images to be released ".center(120, "=")) + for image in images: + image["dir"] = get_dir(image) + image["type"] = get_type(image) + image["project"] = get_project(image) + image["official"] = get_official(image, args.milestone, args.pre_publish) + print(str_image(image)) + print("=" * 120) + print(" Publishing script ".center(120, "=")) + render_publishing_script(args.milestone, images) + print(" End of script ".center(120, "=")) if __name__ == "__main__": diff --git a/experimentations/resolute-snapshot-1.yaml b/experimentations/resolute-snapshot-1.yaml deleted file mode 100644 index 3d5d794..0000000 --- a/experimentations/resolute-snapshot-1.yaml +++ /dev/null @@ -1,79 +0,0 @@ -images: - edubuntu-amd64: - serial: "20251119" - publishing_command: ARCHES='amd64' for-project edubuntu publish-release daily-live SERIAL desktop named - edubuntu-arm64+raspi: - serial: "20251124" - publishing_command: ARCHES='arm64+raspi' for-project edubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named - kubuntu-amd64: - serial: "20251125" - publishing_command: ARCHES='amd64' for-project kubuntu publish-release daily-live SERIAL desktop named - lubuntu-amd64: - serial: "20251124" - publishing_command: ARCHES='amd64' for-project lubuntu publish-release daily-live SERIAL desktop named - ubuntu-desktop-amd64: - serial: "20251101" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release daily-live SERIAL desktop yes - ubuntu-desktop-arm64: - serial: "20251101" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release daily-live SERIAL desktop named - ubuntu-desktop-arm64+raspi: - serial: "20251119" - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named - # As soon as the serial is the same for all architectures/subproducts, we can - # take the shortcut of grouping them together in the same 'publishing_command' - # to shorten a bit the YAML. - ubuntu-base: - serial: "20251126" - publishing_command: ARCHES='amd64 arm64 armhf ppc64el riscv64 s390x' for-project ubuntu-base publish-release daily SERIAL base named - ubuntu-budgie: - serial: "20251124" - publishing_command: ARCHES='amd64' for-project ubuntu-budgie publish-release daily-live SERIAL desktop named - ubuntu-cinnamon: - serial: "20251118" - publishing_command: ARCHES='amd64' for-project ubuntucinnamon publish-release daily-live SERIAL desktop named - ubuntu-kylin: - serial: "20251118" - publishing_command: ARCHES='amd64' for-project ubuntukylin publish-release daily-live SERIAL desktop named - ubuntu-mate: - serial: "20251119" - publishing_command: ARCHES='amd64' for-project ubuntu-mate publish-release daily-live SERIAL desktop named - ubuntu-server-preinstalled-arm64+raspi: - serial: "20251126" - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named - ubuntu-server-preinstalled-riscv64: - serial: "20251125" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named - ubuntu-server-live-amd64: - serial: "20251124" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server yes - ubuntu-server-live-arm64: - serial: "20251125" - publishing_command: ARCHES='arm64 arm64+largemem' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named - ubuntu-server-live-ppc64el: - serial: "20251124" - publishing_command: ARCHES='ppc64el' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named - ubuntu-server-live-riscv64: - serial: "20251125" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named - ubuntu-server-live-s390x: - serial: "20251125" - publishing_command: ARCHES='s390x' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named - ubuntu-studio: - serial: "20251126" - publishing_command: ARCHES='amd64' for-project ubuntustudio publish-release daily-live SERIAL desktop named - ubuntu-unity: - serial: "20251118" - publishing_command: ARCHES='amd64' for-project ubuntu-unity publish-release daily-live SERIAL desktop named - ubuntu-wsl-amd64: - serial: "20251126" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl yes - ubuntu-wsl-arm64: - serial: "20251126" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl named - xubuntu: - serial: "20251119" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-live SERIAL desktop named - xubuntu-minimal: - serial: "20251125" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-minimal SERIAL minimal named diff --git a/experimentations/resolute-snapshot-2.yaml b/experimentations/resolute-snapshot-2.yaml deleted file mode 100644 index 17624ed..0000000 --- a/experimentations/resolute-snapshot-2.yaml +++ /dev/null @@ -1,82 +0,0 @@ -milestone: - name: Snapshot 2 - slug: snapshot-2 -images: - edubuntu-amd64: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project edubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - edubuntu-arm64+raspi: - serial: "20251209" - publishing_command: ARCHES='arm64+raspi' for-project edubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named MILESTONE_SLUG - kubuntu-amd64: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project kubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - lubuntu-amd64: - serial: "20251208" - publishing_command: ARCHES='amd64' for-project lubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-amd64: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-arm64: - serial: "20251208" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-arm64+raspi: - serial: - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named MILESTONE_SLUG - # As soon as the serial is the same for all architectures/subproducts, we can - # take the shortcut of grouping them together in the same 'publishing_command' - # to shorten a bit the YAML. - ubuntu-base: - serial: "20251209" - publishing_command: ARCHES='amd64 arm64 armhf ppc64el riscv64 s390x' for-project ubuntu-base publish-release daily SERIAL base named MILESTONE_SLUG - ubuntu-budgie: - serial: "20251208" - publishing_command: ARCHES='amd64' for-project ubuntu-budgie publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-cinnamon: - serial: "20251208" - publishing_command: ARCHES='amd64' for-project ubuntucinnamon publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-kylin: - serial: "20251208" - publishing_command: ARCHES='amd64' for-project ubuntukylin publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-mate: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project ubuntu-mate publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-server-preinstalled-arm64+raspi: - serial: "20251209" - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named MILESTONE_SLUG - ubuntu-server-preinstalled-riscv64: - serial: "20251209" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named MILESTONE_SLUG - ubuntu-server-live-amd64: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-arm64: - serial: "20251209" - publishing_command: ARCHES='arm64 arm64+largemem' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-ppc64el: - serial: "20251209" - publishing_command: ARCHES='ppc64el' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-riscv64: - serial: "20251209" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-s390x: - serial: "20251209" - publishing_command: ARCHES='s390x' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-studio: - serial: "20251207" - publishing_command: ARCHES='amd64' for-project ubuntustudio publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-unity: - serial: "20251208" - publishing_command: ARCHES='amd64' for-project ubuntu-unity publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-wsl-amd64: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl named MILESTONE_SLUG - ubuntu-wsl-arm64: - serial: "20251209" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl named MILESTONE_SLUG - xubuntu: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - xubuntu-minimal: - serial: "20251209" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-minimal SERIAL minimal named MILESTONE_SLUG diff --git a/experimentations/resolute-snapshot-3.yaml b/experimentations/resolute-snapshot-3.yaml deleted file mode 100644 index 3505fe4..0000000 --- a/experimentations/resolute-snapshot-3.yaml +++ /dev/null @@ -1,82 +0,0 @@ -milestone: - name: Snapshot 3 - slug: snapshot-3 -images: - edubuntu-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project edubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - edubuntu-arm64+raspi: - serial: "20260129" - publishing_command: ARCHES='arm64+raspi' for-project edubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named MILESTONE_SLUG - kubuntu-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project kubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - lubuntu-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project lubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-arm64: - serial: "20260129" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-desktop-arm64+raspi: - serial: "20260129" - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release daily-preinstalled SERIAL preinstalled-desktop named MILESTONE_SLUG - # As soon as the serial is the same for all architectures/subproducts, we can - # take the shortcut of grouping them together in the same 'publishing_command' - # to shorten a bit the YAML. - ubuntu-base: - serial: "20260129" - publishing_command: ARCHES='amd64 arm64 armhf ppc64el riscv64 s390x' for-project ubuntu-base publish-release daily SERIAL base named MILESTONE_SLUG - ubuntu-budgie: - serial: "20260128" - publishing_command: ARCHES='amd64' for-project ubuntu-budgie publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-cinnamon: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntucinnamon publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-kylin: - serial: "20260128" - publishing_command: ARCHES='amd64' for-project ubuntukylin publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-mate: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntu-mate publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-server-preinstalled-arm64+raspi: - serial: "20260129" - publishing_command: ARCHES='arm64+raspi' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named MILESTONE_SLUG - ubuntu-server-preinstalled-riscv64: - serial: "20260129" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-preinstalled SERIAL preinstalled-server named MILESTONE_SLUG - ubuntu-server-live-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-arm64: - serial: "20260129" - publishing_command: ARCHES='arm64 arm64+largemem' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-ppc64el: - serial: "20260129" - publishing_command: ARCHES='ppc64el' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-riscv64: - serial: "20260129" - publishing_command: ARCHES='riscv64' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-server-live-s390x: - serial: "20260129" - publishing_command: ARCHES='s390x' for-project ubuntu publish-release ubuntu-server/daily-live SERIAL live-server named MILESTONE_SLUG - ubuntu-studio: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntustudio publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-unity: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntu-unity publish-release daily-live SERIAL desktop named MILESTONE_SLUG - ubuntu-wsl-amd64: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl named MILESTONE_SLUG - ubuntu-wsl-arm64: - serial: "20260129" - publishing_command: ARCHES='arm64' for-project ubuntu publish-release ubuntu-wsl/daily-live SERIAL wsl named MILESTONE_SLUG - xubuntu: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-live SERIAL desktop named MILESTONE_SLUG - xubuntu-minimal: - serial: "20260129" - publishing_command: ARCHES='amd64' for-project xubuntu publish-release daily-minimal SERIAL minimal named MILESTONE_SLUG