From 4dc31cdeab070cff440d83aa272769aeb367af95 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 25 Apr 2026 18:26:24 +0200 Subject: [PATCH 1/6] release-targets: per-scope DEBIAN/UBUNTU codename substitution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the literal Debian / Ubuntu codenames in release-targets/*.manual and the hardcoded sections of scripts/generate_targets.py with two symbolic tokens — DEBIAN and UBUNTU — that get substituted at generation time. Each output file (standard-support, nightly, community-maintained, apps) gets its own (debian, ubuntu) flag pair plumbed through to resolve_release_tokens(), so promoting any release line is a single CLI flag flip in the workflow rather than a multi-place rename. Mechanics: * SCOPE_DEFAULTS at module top centralises the per-scope pair. Defaults (trixie/noble for standard + community + apps, forky/resolute for nightly) reproduce the pre-refactor literal pins exactly — running with no flags is bit-identical to pre-refactor output, modulo the symbolic-token round-trip. * resolve_release_tokens() rewrites `RELEASE: UBUNTU` / `RELEASE: DEBIAN` to the supplied codenames. Anchored to start-of-line so a sibling key like `KERNEL_RELEASE: UBUNTU` can't have its substring overwritten and end up with a corrupted value. Indentation is captured and restored; \b word-boundary on the token side prevents false-matching `UBUNTU_FOO`-style identifiers; re.escape on the token guards against regex metacharacters. * generate_exposed_map() also accepts the per-scope codename pairs and bakes the right pair into each board's regex (stable boards → standard codenames, community boards → community codenames). Without this, YAML output would track promotions but exposed.map would keep matching old codenames, dropping "recommended images" off the website. * .github/workflows/generate-build-lists.yaml exposes all 8 flags as workflow_dispatch.inputs with matching defaults; push and repository_dispatch triggers (which carry no inputs) get the SCOPE_DEFAULTS via `${{ inputs.X || 'default' }}`. * release-targets/README.md documents the per-scope flags and the promotion workflow. * Descriptive comments in the manual fragments and emit functions no longer mention specific codenames since the codename is now dynamic ("Debian trixie minimal" → "Debian minimal"). Promoting Ubuntu standard to a new release becomes: - workflow_dispatch with `ubuntu_standard=` - done No code change, no PR, no rename across N files. --- .github/workflows/generate-build-lists.yaml | 56 +++- release-targets/README.md | 74 ++++- ...argets-release-community-maintained.manual | 12 +- .../targets-release-nightly.manual | 23 +- .../targets-release-standard-support.manual | 16 +- scripts/generate_targets.py | 298 +++++++++++++----- 6 files changed, 365 insertions(+), 114 deletions(-) diff --git a/.github/workflows/generate-build-lists.yaml b/.github/workflows/generate-build-lists.yaml index 336777c9e..ba2cb0f92 100644 --- a/.github/workflows/generate-build-lists.yaml +++ b/.github/workflows/generate-build-lists.yaml @@ -1,7 +1,45 @@ name: "Infrastructure: Generate image build lists" on: - workflow_dispatch: # Manually triggered via GitHub Actions UI + workflow_dispatch: + # Per-output-file release codename overrides. Defaults match + # scripts/generate_targets.py SCOPE_DEFAULTS, so dispatching with + # all defaults reproduces the previous literal-pin behaviour. + # Set any of these to a different codename to promote that + # release line without touching the script or release-targets/*. + inputs: + debian_standard: + description: "Debian codename for standard-support builds" + required: false + default: "trixie" + ubuntu_standard: + description: "Ubuntu codename for standard-support builds" + required: false + default: "noble" + debian_nightly: + description: "Debian codename for nightly builds" + required: false + default: "forky" + ubuntu_nightly: + description: "Ubuntu codename for nightly builds" + required: false + default: "resolute" + debian_community: + description: "Debian codename for community-maintained builds" + required: false + default: "trixie" + ubuntu_community: + description: "Ubuntu codename for community-maintained builds" + required: false + default: "noble" + debian_apps: + description: "Debian codename for apps builds (HA / OMV / OpenHAB; not Kali, which stays sid)" + required: false + default: "trixie" + ubuntu_apps: + description: "Ubuntu codename for apps builds" + required: false + default: "noble" repository_dispatch: types: ["Generate lists"] push: @@ -54,8 +92,22 @@ jobs: python3 -m pip install --upgrade pip - name: Run generate_targets.py + # `inputs.*` is only populated on workflow_dispatch; push / + # repository_dispatch fire with empty inputs, so each `||` + # falls back to the same default the script's argparse uses. + # Net effect: codename promotion is a workflow_dispatch knob; + # automatic re-runs use the script's pinned defaults. run: | - python3 scripts/generate_targets.py image-info.json release-targets 2>&1 | tee -a generation.log + python3 scripts/generate_targets.py image-info.json release-targets \ + --debian-standard "${{ inputs.debian_standard || 'trixie' }}" \ + --ubuntu-standard "${{ inputs.ubuntu_standard || 'noble' }}" \ + --debian-nightly "${{ inputs.debian_nightly || 'forky' }}" \ + --ubuntu-nightly "${{ inputs.ubuntu_nightly || 'resolute' }}" \ + --debian-community "${{ inputs.debian_community || 'trixie' }}" \ + --ubuntu-community "${{ inputs.ubuntu_community || 'noble' }}" \ + --debian-apps "${{ inputs.debian_apps || 'trixie' }}" \ + --ubuntu-apps "${{ inputs.ubuntu_apps || 'noble' }}" \ + 2>&1 | tee -a generation.log - name: Generate kernel descriptions run: | diff --git a/release-targets/README.md b/release-targets/README.md index 96d00af03..7dbead95e 100644 --- a/release-targets/README.md +++ b/release-targets/README.md @@ -144,7 +144,7 @@ bananapim5 Additional YAML that gets appended to the auto-generated targets section: ```yaml -# Ubuntu stable minimal cloud +# Ubuntu minimal cloud minimal-cli-stable-ubuntu-cloud: enabled: yes configs: [ armbian-cloud ] @@ -152,7 +152,12 @@ minimal-cli-stable-ubuntu-cloud: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + # Symbolic codename token — substituted with the actual codename + # by scripts/generate_targets.py (defaults to whatever + # --ubuntu- flag the workflow was dispatched with). + # Use literal codenames only when a block must pin to a specific + # codename regardless of the per-scope flag. + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -170,13 +175,68 @@ All fast HDMI boards (64-bit boards with video output) automatically get: - Extensions in `targets-extensions.map.blacklist` are REMOVED from both automatic and manual extensions. - The blacklist takes precedence over all other extension sources. +## Release codename substitution + +Both this generator's hardcoded YAML stanzas and every `*.manual` +override file use **two symbolic release tokens** instead of literal +Debian/Ubuntu codenames: + +| Token | Substituted with | +|----------|-------------------------------------| +| `DEBIAN` | the codename passed via `--debian-` | +| `UBUNTU` | the codename passed via `--ubuntu-` | + +Each output file (`apps`, `standard`, `nightly`, `community`) gets its +own (debian, ubuntu) flag pair, so promoting one release line is +independent of the others. + +**Promoting a release** is now a flag flip on the workflow dispatch +inputs — no script edit, no manual-file edit: + +```bash +# Standard support stays on noble; nightly moves Ubuntu to oracular +python3 scripts/generate_targets.py image-info.json release-targets/ \ + --ubuntu-nightly oracular +``` + +`apps-kali` keeps `RELEASE: sid` literal — Kali tracks Debian unstable +forever, that's not a "current Debian stable" pin. + ## Usage ```bash -python3 scripts/generate_targets.py +python3 scripts/generate_targets.py [output_directory] \ + [--debian-standard CODENAME] [--ubuntu-standard CODENAME] \ + [--debian-nightly CODENAME] [--ubuntu-nightly CODENAME] \ + [--debian-community CODENAME] [--ubuntu-community CODENAME] \ + [--debian-apps CODENAME] [--ubuntu-apps CODENAME] ``` -Both arguments are required. The output directory should contain `targets-extensions.map` and any `.blacklist`/`.manual` files. +`image-info.json` is required. `output_directory` defaults to the +current directory and should contain `targets-extensions.map` plus any +`.blacklist` / `.manual` files. + +### Per-scope codename flags + +| Flag | Default | Substitutes `RELEASE: …` in | +|-----------------------|------------|-------------------------------| +| `--debian-standard` | `trixie` | `targets-release-standard-support.yaml` | +| `--ubuntu-standard` | `noble` | `targets-release-standard-support.yaml` | +| `--debian-nightly` | `forky` | `targets-release-nightly.yaml` | +| `--ubuntu-nightly` | `resolute` | `targets-release-nightly.yaml` | +| `--debian-community` | `trixie` | `targets-release-community-maintained.yaml` | +| `--ubuntu-community` | `noble` | `targets-release-community-maintained.yaml` | +| `--debian-apps` | `trixie` | `targets-release-apps.yaml` | +| `--ubuntu-apps` | `noble` | `targets-release-apps.yaml` | + +The same per-scope codenames also drive the regex patterns in +`exposed.map`, so the build matrix and the "recommended images" filter +on the website stay in lockstep — bumping `--ubuntu-standard` updates +both atomically. + +Defaults reproduce the previous literal pins exactly; running with no +flags is byte-identical to the pre-substitution behaviour (modulo the +symbolic-token round-trip). ## Example @@ -184,8 +244,12 @@ Both arguments are required. The output directory should contain `targets-extens # Download latest image-info.json curl -L -o image-info.json https://github.armbian.com/image-info.json -# Generate target YAML files to https://github.armbian.com/release-targets/ directory +# Generate target YAML files using all default codenames python3 scripts/generate_targets.py image-info.json release-targets/ + +# Same, but build standard-support against the next Ubuntu LTS instead +python3 scripts/generate_targets.py image-info.json release-targets/ \ + --ubuntu-standard resolute ``` ## Requirements diff --git a/release-targets/targets-release-community-maintained.manual b/release-targets/targets-release-community-maintained.manual index 0cda1280a..e907d84d2 100644 --- a/release-targets/targets-release-community-maintained.manual +++ b/release-targets/targets-release-community-maintained.manual @@ -1,7 +1,7 @@ # Manual target additions for community maintained builds # This content will be appended to the auto-generated targets-release-community-maintained.yaml -# Debian trixie cli +# Debian cli cli-stable-debian: enabled: yes configs: [ armbian-community ] @@ -9,14 +9,14 @@ cli-stable-debian: gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "no" BUILD_DESKTOP: "no" items: - { BOARD: aml-s9xx-box, BRANCH: current } - { BOARD: aml-s9xx-box, BRANCH: edge } -# Ubuntu noble cli +# Ubuntu cli cli-stable-ubuntu: enabled: yes configs: [ armbian-community ] @@ -24,14 +24,14 @@ cli-stable-ubuntu: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "no" items: - { BOARD: aml-s9xx-box, BRANCH: current } - { BOARD: aml-s9xx-box, BRANCH: edge } -# Debian stable XFCE desktop +# Debian XFCE desktop desktop-stable-debian-xfce: enabled: yes configs: [ armbian-community ] @@ -39,7 +39,7 @@ desktop-stable-debian-xfce: gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" diff --git a/release-targets/targets-release-nightly.manual b/release-targets/targets-release-nightly.manual index 2e6c52556..dd3db3f90 100644 --- a/release-targets/targets-release-nightly.manual +++ b/release-targets/targets-release-nightly.manual @@ -1,7 +1,7 @@ # Manual target additions for nightly builds # This content will be appended to the auto-generated targets-release-nightly.yaml -# Ubuntu stable minimal cloud +# Ubuntu minimal cloud minimal-cli-stable-ubuntu-cloud: enabled: yes configs: [ armbian-cloud ] @@ -9,7 +9,7 @@ minimal-cli-stable-ubuntu-cloud: gha: *armbian-gha build-image: "yes" vars: - RELEASE: resolute + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -18,7 +18,7 @@ minimal-cli-stable-ubuntu-cloud: - { BOARD: uefi-arm64, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } - { BOARD: uefi-x86, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } -# Debian unstable minimal cloud +# Debian minimal cloud minimal-cli-unstable-debian-cloud: enabled: yes configs: [ armbian-cloud ] @@ -26,7 +26,7 @@ minimal-cli-unstable-debian-cloud: gha: *armbian-gha build-image: "yes" vars: - RELEASE: forky + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -35,7 +35,11 @@ minimal-cli-unstable-debian-cloud: - { BOARD: uefi-arm64, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } - { BOARD: uefi-x86, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } -# Debian stable minimal cloud (trixie) +# Debian minimal cloud (legacy duplicate of minimal-cli-unstable-debian-cloud +# above — both blocks resolve to RELEASE: DEBIAN under the substitution +# model, so they build the same image. Kept for now until a follow-up +# decides whether to drop, or to introduce a second per-scope token to +# carry an explicit "stable vs testing" Debian split inside nightly.) minimal-cli-stable-debian-cloud-trixie: enabled: yes configs: [ armbian-cloud ] @@ -43,7 +47,7 @@ minimal-cli-stable-debian-cloud-trixie: gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -52,7 +56,10 @@ minimal-cli-stable-debian-cloud-trixie: - { BOARD: uefi-arm64, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } - { BOARD: uefi-x86, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-vhdx" } -# Ubuntu LTS minimal cloud (noble) +# Ubuntu minimal cloud (legacy duplicate of minimal-cli-stable-ubuntu-cloud +# above — same caveat as the trixie/forky pair: both Ubuntu blocks now +# resolve to RELEASE: UBUNTU under the substitution model, so they build +# the same image.) minimal-cli-stable-ubuntu-cloud-noble: enabled: yes configs: [ armbian-cloud ] @@ -60,7 +67,7 @@ minimal-cli-stable-ubuntu-cloud-noble: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: diff --git a/release-targets/targets-release-standard-support.manual b/release-targets/targets-release-standard-support.manual index 138cca5a2..fbda78db3 100644 --- a/release-targets/targets-release-standard-support.manual +++ b/release-targets/targets-release-standard-support.manual @@ -1,7 +1,7 @@ # Manual target additions for standard support builds # This content will be appended to the auto-generated targets-release-standard-support.yaml -# Debian trixie minimal - UEFI only +# Debian minimal - UEFI only minimal-stable-debian-uefi: enabled: yes configs: [ armbian-images ] @@ -9,7 +9,7 @@ minimal-stable-debian-uefi: gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -22,7 +22,7 @@ minimal-stable-debian-uefi: - { BOARD: radxa-dragon-q6a, BRANCH: current, ENABLE_EXTENSIONS: "ufs" } - { BOARD: radxa-nio-12l, BRANCH: edge, ENABLE_EXTENSIONS: "ufs" } -# Ubuntu noble minimal with current kernel - UEFI only +# Ubuntu minimal with current kernel - UEFI only minimal-stable-ubuntu-current-uefi: enabled: yes configs: [ armbian-images ] @@ -30,7 +30,7 @@ minimal-stable-ubuntu-current-uefi: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -43,7 +43,7 @@ minimal-stable-ubuntu-current-uefi: - { BOARD: radxa-dragon-q6a, BRANCH: current, ENABLE_EXTENSIONS: "ufs" } - { BOARD: radxa-nio-12l, BRANCH: edge, ENABLE_EXTENSIONS: "ufs" } -# Ubuntu noble GNOME desktop - UEFI only +# Ubuntu GNOME desktop - UEFI only desktop-stable-ubuntu-gnome-uefi: enabled: yes configs: [ armbian-images ] @@ -51,7 +51,7 @@ desktop-stable-ubuntu-gnome-uefi: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "gnome" @@ -64,7 +64,7 @@ desktop-stable-ubuntu-gnome-uefi: - { BOARD: radxa-dragon-q6a, BRANCH: current, ENABLE_EXTENSIONS: "ufs" } - { BOARD: radxa-nio-12l, BRANCH: edge, ENABLE_EXTENSIONS: "ufs" } -# Ubuntu noble Cinnamon desktop - UEFI only +# Ubuntu Cinnamon desktop - UEFI only desktop-stable-ubuntu-cinnamon-uefi: enabled: yes configs: [ armbian-images ] @@ -72,7 +72,7 @@ desktop-stable-ubuntu-cinnamon-uefi: gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "cinnamon" diff --git a/scripts/generate_targets.py b/scripts/generate_targets.py index 45ca02b82..8a70766aa 100755 --- a/scripts/generate_targets.py +++ b/scripts/generate_targets.py @@ -6,12 +6,73 @@ release types based on board support levels and use cases. """ +import argparse import json +import re import sys from collections import defaultdict from pathlib import Path +# Release-codename substitution tokens. Both the manual override files +# (release-targets/*.manual) and this generator's own hardcoded YAML +# stanzas use these symbolic names instead of pinning a specific +# Debian/Ubuntu codename. A pair of `--debian-` / `--ubuntu-` +# CLI flags per output target decides what gets substituted just before +# each YAML file is written. Promoting nightly to a new Debian (e.g. +# moving from forky to whatever the next Debian testing is) becomes a +# single flag flip, not a 30-place codename rename, and the four output +# files (standard / nightly / community / apps) can each be on their +# own (debian, ubuntu) pair — e.g. standard on trixie + noble while +# nightly is on forky + resolute, which is exactly the current state +# the defaults below preserve. +RELEASE_TOKEN_DEBIAN = "DEBIAN" +RELEASE_TOKEN_UBUNTU = "UBUNTU" + +# Per-output-file default codename pairs. Match the literal pins these +# files used before the substitution refactor — running the generator +# with no flags reproduces the previous behaviour exactly. +SCOPE_DEFAULTS = { + "standard": {"debian": "trixie", "ubuntu": "noble"}, + "nightly": {"debian": "forky", "ubuntu": "resolute"}, + "community": {"debian": "trixie", "ubuntu": "noble"}, + "apps": {"debian": "trixie", "ubuntu": "noble"}, +} + + +def resolve_release_tokens(yaml_text: str, debian: str, ubuntu: str) -> str: + """ + Substitute the symbolic RELEASE_TOKEN_* placeholders with real + Debian/Ubuntu codenames. + + Anchored to start-of-line (with optional leading whitespace) so + only the literal `RELEASE:` YAML key is matched — a hypothetical + sibling key like `KERNEL_RELEASE: UBUNTU` would otherwise have + its `RELEASE: UBUNTU` substring matched and overwritten too, + leaving `KERNEL_RELEASE: noble` (corrupted value, `KERNEL_` + preserved). No such key exists today; the anchor forward-proofs + against future additions. + + `\\b` word-boundary stays so a token that happens to be a + substring of an unrelated string isn't touched. Indentation is + captured and restored in the replacement so the YAML's leading + whitespace stays intact. Applied to the *fully-assembled* YAML, + so it covers both this generator's emit functions and any manual + content appended via load_manual_overrides(). + """ + yaml_text = re.sub( + r"(?m)^(\s*)RELEASE:\s*" + re.escape(RELEASE_TOKEN_DEBIAN) + r"\b", + lambda m: f"{m.group(1)}RELEASE: {debian}", + yaml_text, + ) + yaml_text = re.sub( + r"(?m)^(\s*)RELEASE:\s*" + re.escape(RELEASE_TOKEN_UBUNTU) + r"\b", + lambda m: f"{m.group(1)}RELEASE: {ubuntu}", + yaml_text, + ) + return yaml_text + + def load_image_info(json_path): """Load and parse image-info.json file.""" with open(json_path, 'r') as f: @@ -536,7 +597,7 @@ def generate_apps_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "no" BUILD_DESKTOP: "no" ENABLE_EXTENSIONS: "ha" @@ -550,7 +611,7 @@ def generate_apps_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" ENABLE_EXTENSIONS: "omv" @@ -564,7 +625,7 @@ def generate_apps_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "no" BUILD_DESKTOP: "no" ENABLE_EXTENSIONS: "openhab" @@ -786,7 +847,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -840,7 +901,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -896,7 +957,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -922,7 +983,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "gnome" @@ -951,7 +1012,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "kde-neon" @@ -977,7 +1038,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -999,7 +1060,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -1033,7 +1094,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1057,7 +1118,7 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1136,7 +1197,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): yaml += """# automated lists stop targets: - # Debian forky minimal CLI for all boards + # Debian minimal CLI for all boards nightly-forky-all: enabled: yes configs: [ armbian-images ] @@ -1144,7 +1205,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: forky + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1161,7 +1222,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): yaml += ' - *nightly-loongarch\n' yaml += """ - # Ubuntu resolute GNOME desktop for fast HDMI boards + # Ubuntu GNOME desktop for fast HDMI boards nightly-resolute-gnome: enabled: yes configs: [ armbian-images ] @@ -1169,7 +1230,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: resolute + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "gnome" @@ -1180,10 +1241,10 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): - *nightly-fast-hdmi """ - # Ubuntu resolute XFCE desktop for slow HDMI boards + # Ubuntu XFCE desktop for slow HDMI boards if slow_boards: yaml += """ - # Ubuntu resolute XFCE desktop for slow HDMI boards + # Ubuntu XFCE desktop for slow HDMI boards nightly-resolute-xfce: enabled: yes configs: [ armbian-images ] @@ -1191,7 +1252,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: resolute + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -1202,10 +1263,10 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): - *nightly-slow-hdmi """ - # Ubuntu resolute XFCE desktop for RISC-V boards + # Ubuntu XFCE desktop for RISC-V boards if riscv64_boards: yaml += """ - # Ubuntu resolute XFCE desktop for RISC-V boards + # Ubuntu XFCE desktop for RISC-V boards nightly-resolute-riscv64-xfce: enabled: yes configs: [ armbian-images ] @@ -1213,7 +1274,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: resolute + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -1224,10 +1285,10 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): - *nightly-riscv64 """ - # Ubuntu resolute minimal CLI for headless boards only + # Ubuntu minimal CLI for headless boards only if headless_boards: yaml += """ - # Ubuntu resolute minimal CLI for headless boards + # Ubuntu minimal CLI for headless boards nightly-resolute-minimal: enabled: yes configs: [ armbian-images ] @@ -1235,7 +1296,7 @@ def generate_nightly_yaml(conf_wip_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: resolute + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1402,7 +1463,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): yaml += """# automated lists stop targets: - # Debian trixie minimal CLI for all community boards + # Debian minimal CLI for all community boards community-trixie-all: enabled: yes configs: [ armbian-community ] @@ -1410,7 +1471,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: trixie + RELEASE: DEBIAN BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1447,7 +1508,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): yaml += ' - *community-edge-loongarch\n' yaml += """ - # Ubuntu noble GNOME desktop for fast HDMI community boards + # Ubuntu GNOME desktop for fast HDMI community boards community-noble-gnome: enabled: yes configs: [ armbian-community ] @@ -1455,7 +1516,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "gnome" @@ -1470,10 +1531,10 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): if edge_fast: yaml += ' - *community-edge-fast-hdmi\n' - # Ubuntu noble KDE Neon desktop for fast HDMI community boards + # Ubuntu KDE Neon desktop for fast HDMI community boards if current_fast or vendor_fast or edge_fast: yaml += """ - # Ubuntu noble KDE Neon desktop for fast HDMI community boards + # Ubuntu KDE Neon desktop for fast HDMI community boards community-noble-kde-neon: enabled: yes configs: [ armbian-community ] @@ -1481,7 +1542,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "kde-neon" @@ -1496,10 +1557,10 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): if edge_fast: yaml += ' - *community-edge-fast-hdmi\n' - # Ubuntu noble XFCE desktop for slow HDMI community boards + # Ubuntu XFCE desktop for slow HDMI community boards if current_slow or vendor_slow or edge_slow: yaml += """ - # Ubuntu noble XFCE desktop for slow HDMI community boards + # Ubuntu XFCE desktop for slow HDMI community boards community-noble-xfce: enabled: yes configs: [ armbian-community ] @@ -1507,7 +1568,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -1523,10 +1584,10 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): if edge_slow: yaml += ' - *community-edge-slow-hdmi\n' - # Ubuntu noble XFCE desktop for RISC-V community boards + # Ubuntu XFCE desktop for RISC-V community boards if current_riscv64 or vendor_riscv64 or edge_riscv64: yaml += """ - # Ubuntu noble XFCE desktop for RISC-V community boards + # Ubuntu XFCE desktop for RISC-V community boards community-noble-riscv64-xfce: enabled: yes configs: [ armbian-community ] @@ -1534,7 +1595,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "no" BUILD_DESKTOP: "yes" DESKTOP_ENVIRONMENT: "xfce" @@ -1550,10 +1611,10 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): if edge_riscv64: yaml += ' - *community-edge-riscv64\n' - # Ubuntu noble minimal CLI for headless community boards + # Ubuntu minimal CLI for headless community boards if current_headless or vendor_headless or edge_headless: yaml += """ - # Ubuntu noble minimal CLI for headless community boards + # Ubuntu minimal CLI for headless community boards community-noble-minimal: enabled: yes configs: [ armbian-community ] @@ -1561,7 +1622,7 @@ def generate_community_yaml(csc_tvb_boards, manual_content=""): gha: *armbian-gha build-image: "yes" vars: - RELEASE: noble + RELEASE: UBUNTU BUILD_MINIMAL: "yes" BUILD_DESKTOP: "no" items: @@ -1588,17 +1649,33 @@ def capitalize_board_name(board): return board.capitalize() -def generate_exposed_map(conf_wip_boards, csc_tvb_boards=None): +def generate_exposed_map( + conf_wip_boards, + csc_tvb_boards=None, + *, + debian_standard, + ubuntu_standard, + debian_community, + ubuntu_community, +): """ Generate exposed.map with regex patterns for recommended images. For each board, generates 2 patterns: - 1. Minimal: Debian trixie + current branch - 2. For boards with video: Ubuntu noble + desktop (gnome/xfce) - For headless: Ubuntu noble + minimal - For riscv64: Ubuntu noble + xfce desktop + 1. Minimal: Debian + current branch + 2. For boards with video: Ubuntu + desktop (gnome/xfce) + For headless: Ubuntu + minimal + For riscv64: Ubuntu + xfce desktop conf_wip_boards: stable boards (conf/wip support level) - images have no 'community_' prefix - csc_tvb_boards: community boards (csc/tvb support level) - images have 'community_' prefix + csc_tvb_boards: community boards (csc/tvb support level) - images have 'community_' prefix + + The Debian/Ubuntu codenames baked into each generated regex are picked + per board based on its support tier — stable boards use the standard- + support codenames, community boards use the community codenames — so + the exposed.map patterns track whatever codename the YAML files were + last generated with. Without this, `generate_*_yaml` could be promoted + to a new release while exposed.map kept matching the old one and + "recommended images" would silently drop off the website. """ if csc_tvb_boards is None: csc_tvb_boards = [] @@ -1645,49 +1722,57 @@ def generate_exposed_map(conf_wip_boards, csc_tvb_boards=None): # Capitalize board name for pattern board_pattern = capitalize_board_name(board) - # 1. Minimal: Debian trixie + current/vendor branch (all boards) + # Per-board (debian, ubuntu) codename pair — stable boards + # follow the standard-support flags, community boards follow + # the community flags. Keeps exposed.map regex patterns in + # lockstep with whatever codenames the YAML files were just + # generated against. + if board_type == 'community': + debian_codename = debian_community + ubuntu_codename = ubuntu_community + else: + debian_codename = debian_standard + ubuntu_codename = ubuntu_standard + + # 1. Minimal: Debian + current/vendor branch (all boards) # Generate two patterns: one with dir prefix (for dl.armbian.com), one without (for GitHub releases) - minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_trixie_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_trixie_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" + minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{debian_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" + minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{debian_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" lines.append(minimal_pattern) lines.append(minimal_pattern_no_prefix) # 2. Second pattern: depends on board type - # For loongarch: only bookworm minimal (no noble) + # loongarch: only the Debian minimal pattern above (no Ubuntu image) if is_fast == 'loongarch': single_image_boards.append(board) continue - # For riscv64: noble xfce desktop + # For riscv64: Ubuntu xfce desktop if is_fast == 'riscv64': - riscv64_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" - riscv64_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" + riscv64_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" + riscv64_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" lines.append(riscv64_pattern) lines.append(riscv64_pattern_no_prefix) continue - # For boards with video: Ubuntu noble + desktop + # For boards with video: Ubuntu + desktop if board_has_video and is_fast is not None: # Determine desktop type based on hardware speed if is_fast is True: # Fast boards get GNOME desktop pattern only desktop_type = 'gnome_desktop' - desktop_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - desktop_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - lines.append(desktop_pattern) - lines.append(desktop_pattern_no_prefix) else: # is_fast is False (slow hardware) desktop_type = 'xfce_desktop' - desktop_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - desktop_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - lines.append(desktop_pattern) - lines.append(desktop_pattern_no_prefix) + desktop_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" + desktop_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" + lines.append(desktop_pattern) + lines.append(desktop_pattern_no_prefix) else: - # Headless boards: Ubuntu noble minimal - noble_minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - noble_minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_noble_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - lines.append(noble_minimal_pattern) - lines.append(noble_minimal_pattern_no_prefix) + # Headless boards: Ubuntu minimal + ubuntu_minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" + ubuntu_minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" + lines.append(ubuntu_minimal_pattern) + lines.append(ubuntu_minimal_pattern_no_prefix) # Display warning for boards with only one image (loongarch only) if single_image_boards: @@ -1700,23 +1785,59 @@ def generate_exposed_map(conf_wip_boards, csc_tvb_boards=None): def main(): """Main entry point.""" - if len(sys.argv) < 2: - print("Usage: generate_targets.py [output_dir]") - print("If output_dir is not specified, uses current directory") - sys.exit(1) - - json_path = Path(sys.argv[1]) + parser = argparse.ArgumentParser( + description=( + "Generate Armbian target YAML files. The emitted YAML carries " + "symbolic RELEASE tokens (DEBIAN / UBUNTU) that get substituted " + "with codenames passed via per-scope flags " + f"(--debian-<{'|'.join(SCOPE_DEFAULTS)}> / " + f"--ubuntu-<{'|'.join(SCOPE_DEFAULTS)}>) just before each output " + "file is written. Defaults preserve the previous literal pins, " + "so running with no flags reproduces the old behaviour exactly. " + "Promoting a release line is a flag flip, not a multi-place rename." + ) + ) + parser.add_argument( + "json_path", + type=Path, + help="Path to image-info.json", + ) + parser.add_argument( + "output_dir", + type=Path, + nargs="?", + default=Path.cwd(), + help="Where to write the generated YAML files (default: cwd)", + ) + # One pair of (--debian-, --ubuntu-) flags per output + # file, registered in a loop so adding a new scope is a one-line + # change to SCOPE_DEFAULTS. + for scope, defaults in SCOPE_DEFAULTS.items(): + parser.add_argument( + f"--debian-{scope}", + default=defaults["debian"], + metavar="CODENAME", + dest=f"debian_{scope}", + help=f"Debian codename used in the {scope} output file " + f"(default: {defaults['debian']})", + ) + parser.add_argument( + f"--ubuntu-{scope}", + default=defaults["ubuntu"], + metavar="CODENAME", + dest=f"ubuntu_{scope}", + help=f"Ubuntu codename used in the {scope} output file " + f"(default: {defaults['ubuntu']})", + ) + args = parser.parse_args() + + json_path = args.json_path + output_dir = args.output_dir if not json_path.exists(): print(f"Error: {json_path} does not exist") sys.exit(1) - # Determine output directory - if len(sys.argv) >= 3: - output_dir = Path(sys.argv[2]) - else: - output_dir = Path.cwd() - output_dir.mkdir(parents=True, exist_ok=True) # Load extensions map (optional) - look in output directory, then release-targets, then script directory @@ -1760,7 +1881,7 @@ def main(): conf_wip_boards_apps, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_apps) print(f" apps: {len(conf_wip_boards_apps)} boards after blacklist", file=sys.stderr) apps_yaml = generate_apps_yaml(conf_wip_boards_apps, manual_apps) - apps_path.write_text(apps_yaml) + apps_path.write_text(resolve_release_tokens(apps_yaml, args.debian_apps, args.ubuntu_apps)) print(f" Written {apps_path}", file=sys.stderr) # targets-release-standard-support.yaml @@ -1770,7 +1891,7 @@ def main(): conf_wip_boards_stable, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_stable) print(f" stable: {len(conf_wip_boards_stable)} boards after blacklist", file=sys.stderr) stable_yaml = generate_stable_yaml(conf_wip_boards_stable, manual_stable) - stable_path.write_text(stable_yaml) + stable_path.write_text(resolve_release_tokens(stable_yaml, args.debian_standard, args.ubuntu_standard)) print(f" Written {stable_path}", file=sys.stderr) # targets-release-nightly.yaml @@ -1780,7 +1901,7 @@ def main(): conf_wip_boards_nightly, _ = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_nightly) print(f" nightly: {len(conf_wip_boards_nightly)} boards after blacklist", file=sys.stderr) nightly_yaml = generate_nightly_yaml(conf_wip_boards_nightly, manual_nightly) - nightly_path.write_text(nightly_yaml) + nightly_path.write_text(resolve_release_tokens(nightly_yaml, args.debian_nightly, args.ubuntu_nightly)) print(f" Written {nightly_path}", file=sys.stderr) # targets-release-community-maintained.yaml @@ -1790,13 +1911,20 @@ def main(): _, csc_tvb_boards_community = extract_boards_by_support_level(image_info, extensions_map, remove_extensions_map, blacklist_community) print(f" community: {len(csc_tvb_boards_community)} boards after blacklist", file=sys.stderr) community_yaml = generate_community_yaml(csc_tvb_boards_community, manual_community) - community_path.write_text(community_yaml) + community_path.write_text(resolve_release_tokens(community_yaml, args.debian_community, args.ubuntu_community)) print(f" Written {community_path}", file=sys.stderr) # exposed.map # Generate from stable + community boards (exclude nightly targets) exposed_map_path = output_dir / 'exposed.map' - exposed_map = generate_exposed_map(conf_wip_boards_stable, csc_tvb_boards_community) + exposed_map = generate_exposed_map( + conf_wip_boards_stable, + csc_tvb_boards_community, + debian_standard=args.debian_standard, + ubuntu_standard=args.ubuntu_standard, + debian_community=args.debian_community, + ubuntu_community=args.ubuntu_community, + ) exposed_map_path.write_text(exposed_map) print(f" Written {exposed_map_path}", file=sys.stderr) From dca9fb6109fd12dd30a8f73b88b62e6aa29e2ea6 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sun, 26 Apr 2026 13:49:45 +0200 Subject: [PATCH 2/6] release-targets: promote standard + community defaults to resolute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flip --ubuntu-standard and --ubuntu-community defaults from noble to resolute. The substitution machinery from the previous commit makes this a one-line change to SCOPE_DEFAULTS plus the workflow input default. Apps and nightly defaults stay unchanged (apps tracks the last LTS for build-image stability; nightly was already on resolute). Output codenames in standard-support and community-maintained release-targets YAMLs move from noble → resolute on next regeneration. Per-board exposed.map regexes pick up resolute via the threading landed in the previous commit; there's no separate place to update. The workflow inputs can still be overridden per-run for one-off backfills (e.g. workflow_dispatch with ubuntu_standard=noble would emit a noble-targeted set without touching this file). --- .github/workflows/generate-build-lists.yaml | 8 ++++---- release-targets/README.md | 9 +++++---- scripts/generate_targets.py | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/generate-build-lists.yaml b/.github/workflows/generate-build-lists.yaml index ba2cb0f92..c3818d46d 100644 --- a/.github/workflows/generate-build-lists.yaml +++ b/.github/workflows/generate-build-lists.yaml @@ -15,7 +15,7 @@ on: ubuntu_standard: description: "Ubuntu codename for standard-support builds" required: false - default: "noble" + default: "resolute" debian_nightly: description: "Debian codename for nightly builds" required: false @@ -31,7 +31,7 @@ on: ubuntu_community: description: "Ubuntu codename for community-maintained builds" required: false - default: "noble" + default: "resolute" debian_apps: description: "Debian codename for apps builds (HA / OMV / OpenHAB; not Kali, which stays sid)" required: false @@ -100,11 +100,11 @@ jobs: run: | python3 scripts/generate_targets.py image-info.json release-targets \ --debian-standard "${{ inputs.debian_standard || 'trixie' }}" \ - --ubuntu-standard "${{ inputs.ubuntu_standard || 'noble' }}" \ + --ubuntu-standard "${{ inputs.ubuntu_standard || 'resolute' }}" \ --debian-nightly "${{ inputs.debian_nightly || 'forky' }}" \ --ubuntu-nightly "${{ inputs.ubuntu_nightly || 'resolute' }}" \ --debian-community "${{ inputs.debian_community || 'trixie' }}" \ - --ubuntu-community "${{ inputs.ubuntu_community || 'noble' }}" \ + --ubuntu-community "${{ inputs.ubuntu_community || 'resolute' }}" \ --debian-apps "${{ inputs.debian_apps || 'trixie' }}" \ --ubuntu-apps "${{ inputs.ubuntu_apps || 'noble' }}" \ 2>&1 | tee -a generation.log diff --git a/release-targets/README.md b/release-targets/README.md index 7dbead95e..c76123f2a 100644 --- a/release-targets/README.md +++ b/release-targets/README.md @@ -221,11 +221,11 @@ current directory and should contain `targets-extensions.map` plus any | Flag | Default | Substitutes `RELEASE: …` in | |-----------------------|------------|-------------------------------| | `--debian-standard` | `trixie` | `targets-release-standard-support.yaml` | -| `--ubuntu-standard` | `noble` | `targets-release-standard-support.yaml` | +| `--ubuntu-standard` | `resolute` | `targets-release-standard-support.yaml` | | `--debian-nightly` | `forky` | `targets-release-nightly.yaml` | | `--ubuntu-nightly` | `resolute` | `targets-release-nightly.yaml` | | `--debian-community` | `trixie` | `targets-release-community-maintained.yaml` | -| `--ubuntu-community` | `noble` | `targets-release-community-maintained.yaml` | +| `--ubuntu-community` | `resolute` | `targets-release-community-maintained.yaml` | | `--debian-apps` | `trixie` | `targets-release-apps.yaml` | | `--ubuntu-apps` | `noble` | `targets-release-apps.yaml` | @@ -247,9 +247,10 @@ curl -L -o image-info.json https://github.armbian.com/image-info.json # Generate target YAML files using all default codenames python3 scripts/generate_targets.py image-info.json release-targets/ -# Same, but build standard-support against the next Ubuntu LTS instead +# Roll standard-support's Ubuntu line back to the previous LTS +# (default is resolute; this pins to noble for one invocation) python3 scripts/generate_targets.py image-info.json release-targets/ \ - --ubuntu-standard resolute + --ubuntu-standard noble ``` ## Requirements diff --git a/scripts/generate_targets.py b/scripts/generate_targets.py index 8a70766aa..33f4d4517 100755 --- a/scripts/generate_targets.py +++ b/scripts/generate_targets.py @@ -33,9 +33,9 @@ # files used before the substitution refactor — running the generator # with no flags reproduces the previous behaviour exactly. SCOPE_DEFAULTS = { - "standard": {"debian": "trixie", "ubuntu": "noble"}, + "standard": {"debian": "trixie", "ubuntu": "resolute"}, "nightly": {"debian": "forky", "ubuntu": "resolute"}, - "community": {"debian": "trixie", "ubuntu": "noble"}, + "community": {"debian": "trixie", "ubuntu": "resolute"}, "apps": {"debian": "trixie", "ubuntu": "noble"}, } From 052c8c7d8d78ebde35f9e58f2e89c88d482bb99e Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sun, 26 Apr 2026 19:51:24 +0200 Subject: [PATCH 3/6] release-targets: set DESKTOP_TIER on the manual desktop blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator's hardcoded desktop blocks all set DESKTOP_TIER explicitly (mid for normal blocks, minimal only for the riscv64-xfce slow-hardware block) but the three manual desktop override blocks lacked it entirely: standard-support desktop-stable-ubuntu-gnome-uefi (no TIER) standard-support desktop-stable-ubuntu-cinnamon-uefi (no TIER) community desktop-stable-debian-xfce (no TIER) Add DESKTOP_TIER: "mid" to all three, matching the generator's default desktop tier. Same insertion point — right after DESKTOP_APPGROUPS_SELECTED — so the var ordering is consistent with the script-emitted blocks. mid is the right default here: - The two standard-support blocks target UEFI / Qualcomm / MediaTek hardware that comfortably runs the mid bundle (browsers + programming appgroup is already selected). - The community block runs on aml-s9xx-box and only enables XFCE without any appgroups — mid is still appropriate; minimal would drop standard XFCE companion utils. --- release-targets/targets-release-community-maintained.manual | 1 + release-targets/targets-release-standard-support.manual | 2 ++ 2 files changed, 3 insertions(+) diff --git a/release-targets/targets-release-community-maintained.manual b/release-targets/targets-release-community-maintained.manual index e907d84d2..1029f1234 100644 --- a/release-targets/targets-release-community-maintained.manual +++ b/release-targets/targets-release-community-maintained.manual @@ -45,5 +45,6 @@ desktop-stable-debian-xfce: DESKTOP_ENVIRONMENT: "xfce" DESKTOP_ENVIRONMENT_CONFIG_NAME: "config_base" DESKTOP_APPGROUPS_SELECTED: "" + DESKTOP_TIER: "mid" items: - { BOARD: aml-s9xx-box, BRANCH: current } diff --git a/release-targets/targets-release-standard-support.manual b/release-targets/targets-release-standard-support.manual index fbda78db3..77da9a010 100644 --- a/release-targets/targets-release-standard-support.manual +++ b/release-targets/targets-release-standard-support.manual @@ -57,6 +57,7 @@ desktop-stable-ubuntu-gnome-uefi: DESKTOP_ENVIRONMENT: "gnome" DESKTOP_ENVIRONMENT_CONFIG_NAME: "config_base" DESKTOP_APPGROUPS_SELECTED: "browsers,programming" + DESKTOP_TIER: "mid" items: - { BOARD: uefi-arm64, BRANCH: edge, ENABLE_EXTENSIONS: "v4l2loopback-dkms,mesa-vpu" } - { BOARD: uefi-x86, BRANCH: edge, ENABLE_EXTENSIONS: "v4l2loopback-dkms,mesa-vpu,nvidia" } @@ -78,6 +79,7 @@ desktop-stable-ubuntu-cinnamon-uefi: DESKTOP_ENVIRONMENT: "cinnamon" DESKTOP_ENVIRONMENT_CONFIG_NAME: "config_base" DESKTOP_APPGROUPS_SELECTED: "browsers,programming" + DESKTOP_TIER: "mid" items: - { BOARD: uefi-arm64, BRANCH: current, ENABLE_EXTENSIONS: "v4l2loopback-dkms,mesa-vpu" } - { BOARD: uefi-x86, BRANCH: current, ENABLE_EXTENSIONS: "v4l2loopback-dkms,mesa-vpu,nvidia" } From fd6f5c3072351e14e1f8c1a29930db68d584085d Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 2 May 2026 12:34:22 +0200 Subject: [PATCH 4/6] release-targets: add Bianbu desktop for riscv64 (legacy / noble) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emit a `desktop-stable-ubuntu-riscv64-bianbu` target alongside the existing XFCE one, scoped to legacy-branch riscv64 boards only and pinned to the noble release. Why so narrow: - Bianbu's PVR DRI userspace is built against the SpacemiT BSP kernel that lives on the legacy branch — current/edge kernels won't have a matching DRI driver, so a Bianbu image on those branches would boot to llvmpipe at best. - archive.spacemit.com only ships SpacemiT's Mesa fork (the 24.01bbx build with the PVR DRI backend, pinned via configng's bianbu.yaml, see armbian/configng#897) for noble snapshots. The resolute archive has no matching snapshots yet — only resolute-customization and resolute-porting at v4.0betaN exist server-side, with no resolute* base. So `RELEASE: noble` is hardcoded literal here, not the substitutable `UBUNTU` token, otherwise the release-targets-promote-resolute-to-stable flag flip on the parent branch would silently retarget Bianbu at a release that can't build. Tier=mid because bianbu-minimal is intentionally bare-bones (DE meta + K1 hardware enablement only); mid adds the full Bianbu desktop + camera stack, which is what users picking a Bianbu image expect. Items reference *stable-legacy-riscv64 (already defined upstream when legacy_riscv64 is non-empty), so the same gate applies and the block is skipped on builds with no legacy riscv64 boards. --- scripts/generate_targets.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/generate_targets.py b/scripts/generate_targets.py index 33f4d4517..66011ab18 100755 --- a/scripts/generate_targets.py +++ b/scripts/generate_targets.py @@ -1078,6 +1078,34 @@ def generate_stable_yaml(conf_wip_boards, manual_content=""): if edge_riscv64: yaml += ' - *stable-edge-riscv64\n' + # Ubuntu noble Bianbu desktop for RISC-V (SpacemiT K1, legacy branch only). + # Bianbu's PVR-DRI userspace is tied to the SpacemiT BSP kernel that lives + # on the legacy branch, and the SpacemiT archive only ships its Mesa fork + # (24.01bbx, pinned via configng's bianbu.yaml) on noble — `RELEASE: noble` + # is therefore literal here, not the substitutable `UBUNTU` token. + # Tier=mid because bianbu-minimal is intentionally bare; mid is the + # canonical Bianbu desktop experience. + if legacy_riscv64: + yaml += """ + # Ubuntu noble Bianbu desktop for SpacemiT K1 boards (legacy branch) + desktop-stable-ubuntu-riscv64-bianbu: + enabled: yes + configs: [ armbian-images ] + pipeline: + gha: *armbian-gha + build-image: "yes" + vars: + RELEASE: noble + BUILD_MINIMAL: "no" + BUILD_DESKTOP: "yes" + DESKTOP_ENVIRONMENT: "bianbu" + DESKTOP_ENVIRONMENT_CONFIG_NAME: "config_base" + DESKTOP_APPGROUPS_SELECTED: "" + DESKTOP_TIER: "mid" + items: + - *stable-legacy-riscv64 +""" + if manual_content: # Indent manual content by 2 spaces to be under targets: indented_manual = '\n'.join(' ' + line if line.strip() else line for line in manual_content.split('\n')) From 09109b89e74ea5355168d17e807d6f1c30808c66 Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 2 May 2026 12:53:35 +0200 Subject: [PATCH 5/6] release-targets: per-board(family) regex overrides for exposed.map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit generate_exposed_map() picks the (release, branch, suffix) tuple algorithmically — riscv64 → xfce_desktop, fast video → gnome, slow video → xfce, headless → ubuntu minimal, loongarch → no desktop pattern. When a vendor BSP is tied to a combination outside the algorithmic default — e.g. SpacemiT K1 boards on noble/legacy with Bianbu desktop, where archive.spacemit.com only ships the PVR DRI Mesa fork (24.01bbx) for noble and the matching kernel BSP lives on the legacy branch — the algorithm picks the wrong target and "recommended images" point at images that never get built. Add a sidecar release-targets/exposed.map.overrides.yaml whose entries can redirect either of the two regex patterns generate_exposed_map emits per board: overrides: - boardfamily: # OR boards: [b1, b2, ...] minimal: # pattern 1 override release: branch: suffix: # default "minimal" desktop: # pattern 2 override release: branch: suffix: # full literal tail, e.g. bianbu_desktop Either inner block may be omitted to leave that pattern at its algorithmic default. Inside each block, every field is optional and falls through. A per-board entry overlays a boardfamily entry block-by-block, then field-by-field. A per-board entry that sets only `minimal:` keeps the family's `desktop:` block intact; a per-board `desktop: {suffix: x}` keeps the family's `desktop.{release, branch}` while replacing only `suffix`. Use this to carve a partial exception out of a boardfamily rule without repeating its other blocks. First-shipped overrides: * spacemit boardfamily → minimal=noble/legacy, desktop=noble/legacy/bianbu_desktop. Matches the build target `desktop-stable-ubuntu-riscv64-bianbu` (added in a separate commit) and the configng pinning of Bianbu's Mesa fork (armbian/configng#897). * bananapif3 + musepipro per-board overlay → minimal=trixie/current (those two boards do ship a current-branch trixie minimal alongside the noble/legacy desktop), desktop unchanged from the family entry via the overlay merge. Loader is regex-only (no bash sourcing); cycles in source-via-yaml references are guarded by a visited set; malformed entries (no match key, non-mapping inner blocks, unknown top-level keys) are dropped with a warning so a typo can't silently rewrite the recommended-image set for every board in the world. --- release-targets/exposed.map.overrides.yaml | 70 ++++++ scripts/generate_targets.py | 265 ++++++++++++++++++--- 2 files changed, 304 insertions(+), 31 deletions(-) create mode 100644 release-targets/exposed.map.overrides.yaml diff --git a/release-targets/exposed.map.overrides.yaml b/release-targets/exposed.map.overrides.yaml new file mode 100644 index 000000000..8d98c3d1a --- /dev/null +++ b/release-targets/exposed.map.overrides.yaml @@ -0,0 +1,70 @@ +# Per-board / boardfamily overrides for the recommended-image regex +# in exposed.map. +# +# generate_exposed_map emits TWO regex patterns per board: +# +# 1. "minimal" — Debian + board's selected branch + "minimal" suffix +# 2. "desktop" — Ubuntu + board's selected branch + algorithmic suffix +# (xfce_desktop / gnome_desktop for video boards, +# minimal for headless, xfce_desktop for riscv64; +# loongarch boards skip this pattern). +# +# When a vendor BSP doesn't follow these defaults — e.g. its minimal +# image lives on a different release than upstream Debian, or its +# desktop is a custom build like Bianbu — list the override here. +# +# Match keys per entry: +# +# boardfamily: # apply to every board in this family +# boards: [b1, b2, ...] # apply to specific board slugs (must +# # be a YAML list — `boards: a, b` is a +# # string and gets dropped at load time) +# +# Per-pattern override blocks (both optional; both internal fields +# are also optional and fall through to the algorithmic default): +# +# minimal: +# release: # literal codename (NOT a UBUNTU/DEBIAN token) +# branch: # e.g. legacy, vendor +# suffix: # tail of the regex; default "minimal" +# +# desktop: +# release: +# branch: +# suffix: # full literal tail, e.g. "bianbu_desktop" +# +# Merge rules: a per-board entry (`boards: [...]`) overlays the +# boardfamily entry block-by-block, then field-by-field. So a +# per-board entry that sets only `minimal:` keeps the family's +# `desktop:` block intact, and a per-board `desktop: {suffix: x}` +# keeps the family's `desktop.{release, branch}` while replacing +# only `suffix`. Use this to carve a partial exception out of a +# boardfamily rule without repeating its other blocks. + +overrides: + - boardfamily: spacemit + # SpacemiT K1 boards: desktop image lives on Ubuntu noble + legacy. + # Bianbu's PVR DRI Mesa fork (24.01bbx) only ships for noble on + # archive.spacemit.com, and the matching kernel BSP lives on the + # legacy branch. Build target for the desktop image is + # `desktop-stable-ubuntu-riscv64-bianbu`. Default minimal lines + # up with the same noble/legacy combo for boards that only ship + # legacy kernels (e.g. musebook). See armbian/configng#897 for + # archive pinning details. + minimal: + release: noble + branch: legacy + desktop: + release: noble + branch: legacy + suffix: bianbu_desktop + + - boards: [bananapif3, musepipro] + # K1 boards that *do* ship a current-branch trixie minimal CLI + # image alongside the legacy/noble desktop. Override the family's + # minimal pattern (Debian trixie + current) for these two; the + # family's desktop block (noble/legacy/bianbu_desktop) stays in + # effect via the per-board overlay merge. + minimal: + release: trixie + branch: current diff --git a/scripts/generate_targets.py b/scripts/generate_targets.py index 66011ab18..288570343 100755 --- a/scripts/generate_targets.py +++ b/scripts/generate_targets.py @@ -13,6 +13,8 @@ from collections import defaultdict from pathlib import Path +import yaml + # Release-codename substitution tokens. Both the manual override files # (release-targets/*.manual) and this generator's own hardcoded YAML @@ -223,6 +225,167 @@ def load_manual_overrides(base_path): return "" +def load_exposed_overrides(path): + """ + Load per-board(family) overrides for exposed.map regex generation. + + The file (typically `/exposed.map.overrides.yaml`) lets a + vendor BSP redirect either of the two regex patterns + generate_exposed_map emits per board. Useful when, e.g., the SpacemiT + K1 boardfamily has a Bianbu desktop image on noble/legacy that the + generic riscv64 → xfce path doesn't reach, and the matching minimal + image lives on noble/legacy too rather than on the default Debian + codename. + + Schema: + + overrides: + - boardfamily: # match all boards in this family + # AND/OR (in a separate entry) + boards: [b1, b2, ...] # specific board slugs + + # Optional override for pattern 1 (the "minimal" image). + # Defaults to: debian_codename + board's selected branch + "minimal". + minimal: + release: # literal codename (NOT a UBUNTU/DEBIAN token) + branch: + suffix: # tail of the regex; default "minimal" + + # Optional override for pattern 2 (the "desktop" image). + # Defaults to: ubuntu_codename + board's selected branch + + # algorithmic suffix (xfce_desktop / gnome_desktop / minimal). + desktop: + release: + branch: + suffix: # full literal tail, e.g. "bianbu_desktop" + + Either inner block may be omitted to leave that pattern untouched. + Inside each block, every field is optional and falls through to the + algorithmic default the generator would have used. + + A per-board entry (`boards: [...]`) overlays the boardfamily entry, + block-by-block then field-by-field — so a per-board entry that only + sets `minimal:` keeps the family's `desktop:` block intact, and a + per-board `desktop: {suffix: x}` keeps the family's + `desktop.{release, branch}` while replacing only `suffix`. See + match_exposed_override() for the merge rules. + + Missing file → []. Malformed entries (no match key, non-mapping + inner blocks) are dropped with a warning so a typo can't silently + rewrite the recommended-image set for every board in the world. + """ + if not path.exists(): + return [] + try: + with open(path) as f: + data = yaml.safe_load(f) or {} + except (yaml.YAMLError, OSError) as e: + print(f"Warning: could not parse {path}: {e}", file=sys.stderr) + return [] + + raw = data.get('overrides', []) + if not isinstance(raw, list): + print(f"Warning: {path}: 'overrides' must be a list", file=sys.stderr) + return [] + + valid = [] + for i, entry in enumerate(raw): + if not isinstance(entry, dict): + print(f"Warning: {path}: overrides[{i}] is not a mapping; skipped", file=sys.stderr) + continue + # An entry without any match key would silently match everything. + has_boards = isinstance(entry.get('boards'), list) and entry['boards'] + has_family = isinstance(entry.get('boardfamily'), str) and entry['boardfamily'] + if not (has_boards or has_family): + print(f"Warning: {path}: overrides[{i}] needs `boards` or `boardfamily`; skipped", file=sys.stderr) + continue + # Inner blocks must be mappings if present. A flat-schema typo + # (e.g. someone writing `release: noble` at the top level instead + # of nesting it under `desktop:`) silently has no effect, so we + # warn — that's the only way to catch a misplaced override. + ok = True + for block in ('minimal', 'desktop'): + if block in entry and not isinstance(entry[block], dict): + print(f"Warning: {path}: overrides[{i}].{block} must be a mapping; entry skipped", file=sys.stderr) + ok = False + break + if not ok: + continue + for top in entry.keys(): + if top in ('boards', 'boardfamily', 'minimal', 'desktop'): + continue + print(f"Warning: {path}: overrides[{i}] has unknown top-level key {top!r}; ignored", file=sys.stderr) + valid.append(entry) + if valid: + print(f" Loaded exposed.map overrides from {path.name}: {len(valid)} entries", file=sys.stderr) + return valid + + +def match_exposed_override(overrides, *, board, boardfamily): + """ + Pick the effective override for a given board, or None. + + A per-board entry (`boards: [...]`) is overlaid on top of the + boardfamily entry (`boardfamily: ...`) — block by block, then + field by field within each block. Fields the per-board entry + sets win; fields it omits fall through to the family. This lets + a single board carve out a partial exception (e.g. different + minimal release) without having to repeat the rest of the + family's blocks. + + Examples (with a family `{boardfamily: spacemit, minimal: {...}, desktop: {...}}`): + + - boards: [musebook] → returns the family entry verbatim + - boards: [musepipro] + minimal: { release: trixie, branch: current } + → keeps family.desktop, replaces + family.minimal entirely + - boards: [bananapif3] + desktop: { suffix: xfce_desktop } → keeps family.minimal, keeps + family.desktop.{release, branch}, + replaces only desktop.suffix + + With no per-board match the family entry is returned as-is; with + no family match the per-board entry is returned as-is; with neither, + None. + """ + family_entry = None + if boardfamily: + for entry in overrides: + if entry.get('boardfamily') == boardfamily: + family_entry = entry + break + + board_entry = None + for entry in overrides: + if board in (entry.get('boards') or []): + board_entry = entry + break + + if board_entry is None and family_entry is None: + return None + if board_entry is None: + return family_entry + if family_entry is None: + return board_entry + + # Both present — overlay board on family. Block-level: a missing + # block on the per-board entry leaves the family's block intact. + # Field-level: inside a block the per-board fields update the + # family's, leaving unset fields alone. + merged = {} + for block in ('minimal', 'desktop'): + family_block = family_entry.get(block) if isinstance(family_entry.get(block), dict) else None + board_block = board_entry.get(block) if isinstance(board_entry.get(block), dict) else None + if family_block is None and board_block is None: + continue + merged_block = dict(family_block) if family_block else {} + if board_block: + merged_block.update(board_block) + merged[block] = merged_block + return merged + + def load_blacklist(base_path): """ Load blacklist of boards to exclude from automation. @@ -1685,6 +1848,7 @@ def generate_exposed_map( ubuntu_standard, debian_community, ubuntu_community, + overrides=None, ): """ Generate exposed.map with regex patterns for recommended images. @@ -1704,9 +1868,18 @@ def generate_exposed_map( last generated with. Without this, `generate_*_yaml` could be promoted to a new release while exposed.map kept matching the old one and "recommended images" would silently drop off the website. + + overrides: list returned by load_exposed_overrides(). Each entry + may carry a `minimal:` block (overrides pattern 1) and/or a + `desktop:` block (overrides pattern 2). Each block can swap in a + custom (release, branch, suffix) tuple. Used by vendor BSPs whose + recommended images live off the algorithmic default — see + exposed.map.overrides.yaml. """ if csc_tvb_boards is None: csc_tvb_boards = [] + if overrides is None: + overrides = [] lines = [] single_image_boards = [] # Track boards with only minimal image (loongarch only) @@ -1732,6 +1905,14 @@ def generate_exposed_map( # Get inventory data for checking extensions inventory = entry.get('in', {}).get('inventory', {}) board_has_video = inventory.get('BOARD_HAS_VIDEO', False) + # BOARDFAMILY for override matching — same fallback path as + # is_fast_hardware uses, since some boards only set it under + # BOARD_TOP_LEVEL_VARS rather than at the inventory root. + boardfamily = ( + inventory.get('BOARDFAMILY', '') + or inventory.get('BOARD_TOP_LEVEL_VARS', {}).get('BOARDFAMILY', '') + ) + override = match_exposed_override(overrides, board=board, boardfamily=boardfamily) # Determine file extension based on extensions # Check for special output formats @@ -1762,45 +1943,62 @@ def generate_exposed_map( debian_codename = debian_standard ubuntu_codename = ubuntu_standard - # 1. Minimal: Debian + current/vendor branch (all boards) - # Generate two patterns: one with dir prefix (for dl.armbian.com), one without (for GitHub releases) - minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{debian_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{debian_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" + # 1. Minimal: Debian + board's branch + "minimal" by default. + # Apply override.minimal (if any) — vendor BSPs whose minimal + # image lives off the default Debian release/branch combination + # can redirect both the release codename and the regex tail + # (e.g. spacemit's K1 boards have no Debian-trixie-legacy image + # but do have Ubuntu-noble-legacy minimal). + m_release = debian_codename + m_branch = branch + m_suffix = 'minimal' + if override and isinstance(override.get('minimal'), dict): + mb = override['minimal'] + m_release = mb.get('release', m_release) + m_branch = mb.get('branch', m_branch) + m_suffix = mb.get('suffix', m_suffix) + + minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{m_release}_{m_branch}_[0-9]*.[0-9]*.[0-9]*_{m_suffix}{file_ext}" + minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{m_release}_{m_branch}_[0-9]*.[0-9]*.[0-9]*_{m_suffix}{file_ext}" lines.append(minimal_pattern) lines.append(minimal_pattern_no_prefix) - # 2. Second pattern: depends on board type - # loongarch: only the Debian minimal pattern above (no Ubuntu image) + # 2. Second pattern: Ubuntu image, suffix depends on board type. + # loongarch is the exception — only the Debian minimal pattern + # above is emitted, no Ubuntu image exists for it yet. if is_fast == 'loongarch': single_image_boards.append(board) continue - # For riscv64: Ubuntu xfce desktop + # Default suffix (the `..._{file_ext}` tail of the regex) + # mirrors the historical behaviour: + # riscv64 → xfce_desktop + # video + fast → gnome_desktop + # video + slow → xfce_desktop + # headless → minimal if is_fast == 'riscv64': - riscv64_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" - riscv64_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_xfce_desktop{file_ext}" - lines.append(riscv64_pattern) - lines.append(riscv64_pattern_no_prefix) - continue - - # For boards with video: Ubuntu + desktop - if board_has_video and is_fast is not None: - # Determine desktop type based on hardware speed - if is_fast is True: - # Fast boards get GNOME desktop pattern only - desktop_type = 'gnome_desktop' - else: # is_fast is False (slow hardware) - desktop_type = 'xfce_desktop' - desktop_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - desktop_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_{desktop_type}{file_ext}" - lines.append(desktop_pattern) - lines.append(desktop_pattern_no_prefix) + default_suffix = 'xfce_desktop' + elif board_has_video and is_fast is not None: + default_suffix = 'gnome_desktop' if is_fast is True else 'xfce_desktop' else: - # Headless boards: Ubuntu minimal - ubuntu_minimal_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - ubuntu_minimal_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{ubuntu_codename}_{branch}_[0-9]*.[0-9]*.[0-9]*_minimal{file_ext}" - lines.append(ubuntu_minimal_pattern) - lines.append(ubuntu_minimal_pattern_no_prefix) + default_suffix = 'minimal' + + # Apply override.desktop (if any). suffix is the literal regex + # tail (e.g. "bianbu_desktop"), not just the desktop name — + # keeps the schema symmetric with the minimal block above. + d_release = ubuntu_codename + d_branch = branch + d_suffix = default_suffix + if override and isinstance(override.get('desktop'), dict): + db = override['desktop'] + d_release = db.get('release', d_release) + d_branch = db.get('branch', d_branch) + d_suffix = db.get('suffix', d_suffix) + + second_pattern = f"{dir_prefix}Armbian_{community_prefix}[0-9].*{board_pattern}_{d_release}_{d_branch}_[0-9]*.[0-9]*.[0-9]*_{d_suffix}{file_ext}" + second_pattern_no_prefix = f"Armbian_{community_prefix}[0-9].*{board_pattern}_{d_release}_{d_branch}_[0-9]*.[0-9]*.[0-9]*_{d_suffix}{file_ext}" + lines.append(second_pattern) + lines.append(second_pattern_no_prefix) # Display warning for boards with only one image (loongarch only) if single_image_boards: @@ -1943,8 +2141,12 @@ def main(): print(f" Written {community_path}", file=sys.stderr) # exposed.map - # Generate from stable + community boards (exclude nightly targets) + # Generate from stable + community boards (exclude nightly targets). + # Per-board(family) overrides for the recommended-image regex (used + # by vendor BSPs whose desktop image lives on a non-default + # branch/release/desktop combo) live next to the output file. exposed_map_path = output_dir / 'exposed.map' + exposed_overrides = load_exposed_overrides(output_dir / 'exposed.map.overrides.yaml') exposed_map = generate_exposed_map( conf_wip_boards_stable, csc_tvb_boards_community, @@ -1952,6 +2154,7 @@ def main(): ubuntu_standard=args.ubuntu_standard, debian_community=args.debian_community, ubuntu_community=args.ubuntu_community, + overrides=exposed_overrides, ) exposed_map_path.write_text(exposed_map) print(f" Written {exposed_map_path}", file=sys.stderr) From 35dcedd1a86c16fd33e28561e7f3fadaedab406a Mon Sep 17 00:00:00 2001 From: Igor Pecovnik Date: Sat, 2 May 2026 22:08:42 +0200 Subject: [PATCH 6/6] release-targets/README: refresh inputs, outputs, and codename docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six concrete fixes plus a tidy of the section flow: * Replace the literal "Ubuntu Noble" sprinkled through "Generated files" — the standard-support and community defaults flipped from noble to resolute in the previous commits in this branch, so those references were stale and would mislead anyone using the README to figure out what the matrix produces today. Switch to neutral wording that points at the per-scope flag instead of naming a specific codename. * Document `exposed.map.overrides.yaml`. It's a real input (loaded by `load_exposed_overrides` and consumed by `generate_exposed_map`) but was completely missing from the inputs table and the configuration-file-formats section. Adds the schema and the merge semantics ("per-board entries overlay boardfamily entries block-by-block then field-by-field") so the next person editing it has a reference. * Document `exposed.map` itself as a generated output. Previously "Generated Files" only listed the four `targets-release-*.yaml` files, even though `exposed.map` is also written. * Mention `reusable.yml` (the virtual-board mechanism) in the inputs table. Its own header has a thorough schema explanation, so this README just points at it rather than duplicating. * Update the manual-fragment example to include `DESKTOP_TIER: "mid"` on a desktop block. It's required for armbian-config's tiered install to pick the right tier; without it the install falls back to the parser default and silently produces a different package set than intended. * Replace the `--ubuntu-nightly oracular` example with `questing`. oracular is Ubuntu 24.10 and was already past EOL, so as a forward-looking example it aged badly. Section-level cleanup: rename "Configuration Files" → "Inputs" and "Generated Files" → "Outputs" with a one-line preamble each, then collapse the per-target enumeration into a description of what *categories* the generator emits (minimal / desktop / apps and the arch / branch / DE axes they fan out across) rather than a literal list that was already incomplete and would need editing on every new emit. Net: +131 / -167 lines. README is more accurate, more complete, and slightly shorter. --- release-targets/README.md | 298 +++++++++++++++++--------------------- 1 file changed, 131 insertions(+), 167 deletions(-) diff --git a/release-targets/README.md b/release-targets/README.md index c76123f2a..cd2d2c8fc 100644 --- a/release-targets/README.md +++ b/release-targets/README.md @@ -1,263 +1,227 @@ # Armbian Target YAML Generator -This script generates Armbian CI/CD pipeline configuration YAML files from `image-info.json`. +`scripts/generate_targets.py` reads `image-info.json` (the per-board build inventory) plus the configuration files in this directory and emits the YAML files that drive Armbian's CI/CD pipeline matrix. -## Quick Start - -All configuration files should be in this directory (`release-targets/`): +## Quick start ```bash # From repository root python3 scripts/generate_targets.py image-info.json release-targets/ ``` -## Configuration Files - -The script reads the following files from the output directory (same location as generated files): +All inputs and outputs live in `release-targets/`. With no flags, the output reproduces the previous literal codename pins exactly (modulo the symbolic-token round-trip — see [Release codename substitution](#release-codename-substitution)). -- **`targets-extensions.map`** - Manual extensions for specific boards/branches (optional) -- **`targets-extensions.map.blacklist`** - Extensions to remove from specific boards/branches (optional) -- **`targets-release-.blacklist`** - Boards to exclude from each target type (optional) -- **`targets-release-.manual`** - Additional YAML to append to each target (optional) +## Inputs -## Generated Files +Files the script reads from the output directory. Every input is optional unless noted; missing files are treated as empty. -The script generates 4 YAML files with the naming pattern `targets-release-.yaml`: +| File | Purpose | +|---|---| +| `targets-extensions.map` | Add per-board / per-branch `ENABLE_EXTENSIONS` entries. | +| `targets-extensions.map.blacklist` | Remove auto- or manually-added extensions per board / branch. | +| `targets-release-.blacklist` | Boards to exclude from a target type (one per line). | +| `targets-release-.manual` | YAML appended to the auto-generated section for that type. | +| `exposed.map.overrides.yaml` | Per-board / boardfamily overrides for the regex patterns in `exposed.map`. | +| `reusable.yml` | "Virtual board" definitions that reuse another board's image set with custom branding (see [reusable.yml header](reusable.yml) for schema and examples). | -### 1. targets-release-apps.yaml -Application-specific images for conf/wip boards -- `apps-ha`: Home Assistant images (Ubuntu Noble + Gnome) -- `apps-openhab`: openHAB images (Ubuntu Noble + Gnome) -- `apps-kali`: Kali Linux images (Kali rolling + XFCE) +`` is one of: `apps`, `nightly`, `standard-support`, `community-maintained`. -### 2. targets-release-standard-support.yaml -Standard support release images for conf/wip boards, split by performance: +## Outputs -**Lists:** -- `stable-current-fast-hdmi`: Fast 64-bit boards with video -- `stable-current-slow-hdmi`: Slow 32-bit boards with video -- `stable-current-headless`: Boards without video output -- `stable-vendor-fast-hdmi`: Fast 64-bit vendor branch boards -- `stable-vendor-slow-hdmi`: Slow 32-bit vendor branch boards -- `stable-vendor-headless`: Headless vendor branch boards +Five files written to the output directory. -**Targets:** -- `minimal-stable-debian`: Debian Trixie minimal -- `minimal-stable-ubuntu`: Ubuntu Noble minimal -- `desktop-stable-ubuntu`: Ubuntu Noble XFCE desktop (fast-hdmi only) +| File | What it drives | +|---|---| +| `targets-release-apps.yaml` | Application-specific images (Home Assistant, openHAB, Kali). | +| `targets-release-standard-support.yaml` | Standard-support release images for `conf` / `wip` boards, split by performance class and branch. | +| `targets-release-nightly.yaml` | Nightly builds for `conf` / `wip` boards. | +| `targets-release-community-maintained.yaml` | Community / experimental builds for `csc` / `tvb` boards. | +| `exposed.map` | Per-board regex patterns the website uses to pick the "recommended image" link from the live image set. | -### 3. targets-release-nightly.yaml -Nightly builds for conf/wip boards, split by performance: +### Targets emitted in each file -**Lists:** -- `nightly-fast-hdmi`: Fast 64-bit boards with video -- `nightly-slow-hdmi`: Slow 32-bit boards with video -- `nightly-headless`: Boards without video output +The four `targets-release-*.yaml` files share a common shape: a header, a set of YAML anchors (`stable-current-fast-hdmi: &stable-current-fast-hdmi …`) listing the boards in each (branch, performance) bucket, and a set of build targets (`desktop-stable-ubuntu-…`, `minimal-stable-debian-…`, …) that compose those anchors via `*alias` references. -**Targets:** -- `nightly-forky-all`: Debian Forky minimal CLI for all boards -- `nightly-noble-gnome`: Ubuntu Noble GNOME desktop (fast HDMI) -- `nightly-noble-xfce`: Ubuntu Noble XFCE desktop (slow HDMI) -- `nightly-noble-minimal`: Ubuntu Noble minimal CLI (headless/exotic) +Targets are emitted only for combinations that have at least one board: -### 4. targets-release-community-maintained.yaml -Community-maintained builds for csc/tvb boards: +- `minimal-…` — Debian or Ubuntu CLI image, per branch (current / vendor / legacy / edge), per architecture class (default / riscv64 / loongarch). +- `desktop-…` — Ubuntu desktop image, per `DESKTOP_ENVIRONMENT` (xfce / gnome / bianbu) the matrix supports for that release × arch combo. Fast HDMI boards get GNOME; slow / riscv64 get XFCE; the SpacemiT K1 family on the legacy branch gets the Bianbu desktop. +- `apps-…` — Home Assistant + openHAB on Ubuntu (the `apps` scope tracks the last LTS for build-image stability), Kali on `sid`. -**Lists:** -- `community-current`: Current branch community boards -- `community-vendor`: Vendor branch community boards +The exact codename each `RELEASE:` line resolves to depends on which `---` flags the workflow was dispatched with — see [Per-scope codename flags](#per-scope-codename-flags). -**Targets:** -- `community-forky-all`: Debian Forky minimal CLI for all boards -- `community-noble-gnome`: Ubuntu Noble GNOME desktop (fast HDMI) -- `community-noble-xfce`: Ubuntu Noble XFCE desktop (slow HDMI) -- `community-noble-minimal`: Ubuntu Noble minimal CLI (headless/exotic) +## Board classification -## Board Classification +### By performance / arch (driven by `KERNEL_SRC_ARCH` and `BOARD_HAS_VIDEO`) -### By Performance (KERNEL_SRC_ARCH) -- **Slow HDMI**: `arm` (32-bit boards) -- **Fast HDMI**: `arm64`, `x86` (modern 64-bit boards) -- **RISC-V**: `riscv64` (separate category) -- **LoongArch**: `loongarch64` (separate category) -- **Headless**: Boards with `BOARD_HAS_VIDEO: false` +- **Fast HDMI** — `arm64`, `x86` boards with video. Get `gnome_desktop` recommendation, automatic `mesa-vpu` + `v4l2loopback-dkms` extensions. +- **Slow HDMI** — `arm` (32-bit) boards with video. Get `xfce_desktop` recommendation. +- **Headless** — `BOARD_HAS_VIDEO: false`. Get `minimal` (CLI) recommendation. +- **RISC-V** — `riscv64`. Separate category, single XFCE desktop pattern (with the SpacemiT K1 family overridden to Bianbu via `exposed.map.overrides.yaml`). +- **LoongArch** — `loongarch64`. Separate category, currently Debian-minimal only (no Ubuntu image). -### By Support Level -- **conf/wip**: Higher quality support (stable and nightly builds) -- **csc/tvb**: Community/experimental support (community builds) +### By support level (driven by file extension under `config/boards/`) -## Configuration File Formats +- **`conf` / `wip`** — first-tier support. Land in `standard-support` and `nightly`. +- **`csc` / `tvb`** — community / experimental. Land in `community-maintained`. -### targets-extensions.map +## Configuration file formats -Add manual extensions for specific boards (merged with automatic fast‑HDMI extensions): +### `targets-extensions.map` ```ini -# Format: BOARD_NAME:branch1:branch2:...:ENABLE_EXTENSIONS="extension1,extension2" +# Format: BOARD:branch1:branch2:...:ENABLE_EXTENSIONS="ext1,ext2" +# An empty branch list (e.g. board:::) means "all branches". -# Add to specific branches khadas-vim1s:legacy:current:edge::ENABLE_EXTENSIONS="image-output-oowow" - -# Add to single branch rock-5b:current::ENABLE_EXTENSIONS="custom-extension" - -# Add to all branches nanopi-r4s:::ENABLE_EXTENSIONS="test-extension" - -# Multiple extensions -board-name:current::ENABLE_EXTENSIONS="ext1,ext2,ext3" ``` -### targets-extensions.map.blacklist +Manual entries are **merged** with the automatic fast-HDMI extensions (`mesa-vpu`, `v4l2loopback-dkms`). -Remove extensions from specific boards (overrides automatic and manual extensions): +### `targets-extensions.map.blacklist` ```ini -# Format: BOARD_NAME:branch1:branch2:...:REMOVE_EXTENSIONS="extension1,extension2" +# Format: BOARD:branch1:branch2:...:REMOVE_EXTENSIONS="ext1,ext2" -# Remove from all branches radxa-e54c:::REMOVE_EXTENSIONS="v4l2loopback-dkms,mesa-vpu" - -# Remove from specific branch only uefi-x86:current::REMOVE_EXTENSIONS="mesa-vpu" - -# Remove from multiple branches -board-name:vendor:edge::REMOVE_EXTENSIONS="ext1,ext2" ``` -**Note**: The REMOVE_EXTENSIONS blacklist takes precedence over both automatic extensions (like `mesa-vpu` for fast HDMI boards) AND manual extensions from `targets-extensions.map`. Extensions listed here will be removed even if they were added by either mechanism. +The blacklist takes precedence over both automatic and manual extensions. -### targets-release-.blacklist +### `targets-release-.blacklist` -Exclude specific boards from a target type (one board name per line): +One board name per line. `#` comments allowed. -``` -# Lines starting with # are comments +```text +# Boards we don't want in standard-support builds ayn-odin2 -mekotronics-r58hd -mekotronics-r58-4x4 bananapim5 ``` -### targets-release-.manual +### `targets-release-.manual` -Additional YAML that gets appended to the auto-generated targets section: +YAML fragment appended verbatim to the auto-generated targets section. Use the symbolic `RELEASE: UBUNTU` / `RELEASE: DEBIAN` tokens unless the block needs to pin to a specific codename regardless of the scope flag (see the Bianbu emit in `generate_targets.py` for that pattern). ```yaml -# Ubuntu minimal cloud -minimal-cli-stable-ubuntu-cloud: +desktop-stable-ubuntu-cinnamon: enabled: yes - configs: [ armbian-cloud ] + configs: [ armbian-images ] pipeline: gha: *armbian-gha build-image: "yes" vars: - # Symbolic codename token — substituted with the actual codename - # by scripts/generate_targets.py (defaults to whatever - # --ubuntu- flag the workflow was dispatched with). - # Use literal codenames only when a block must pin to a specific - # codename regardless of the per-scope flag. - RELEASE: UBUNTU - BUILD_MINIMAL: "yes" - BUILD_DESKTOP: "no" + RELEASE: UBUNTU # substituted at generation time + BUILD_MINIMAL: "no" + BUILD_DESKTOP: "yes" + DESKTOP_ENVIRONMENT: "cinnamon" + DESKTOP_ENVIRONMENT_CONFIG_NAME: "config_base" + DESKTOP_APPGROUPS_SELECTED: "" + DESKTOP_TIER: "mid" # required for desktop blocks; armbian-config picks + # minimal / mid / full from this when assembling + # the rootfs items: - - { BOARD: uefi-arm64, BRANCH: cloud, ENABLE_EXTENSIONS: "image-output-qcow2" } + - *stable-current-fast-hdmi + - *stable-vendor-fast-hdmi ``` -## Automatic Extensions +### `exposed.map.overrides.yaml` -All fast HDMI boards (64-bit boards with video output) automatically get: -- `v4l2loopback-dkms` -- `mesa-vpu` +Per-board / boardfamily overrides for the regex patterns in `exposed.map`. The generator picks `(release, branch, suffix)` algorithmically (riscv64 → `xfce_desktop`, fast video → `gnome_desktop`, etc.); when a vendor BSP needs a combination outside that algorithm, redirect via this file. + +```yaml +overrides: + - boardfamily: # OR boards: [b1, b2, ...] + minimal: # pattern 1 override (default: Debian + board's branch + "minimal") + release: + branch: + suffix: # default: "minimal" + desktop: # pattern 2 override (default: Ubuntu + board's branch + algorithmic suffix) + release: + branch: + suffix: # full literal tail, e.g. "bianbu_desktop" +``` + +Either inner block may be omitted to leave that pattern at its algorithmic default. Inside each block, every field is optional and falls through. + +A per-board entry **overlays** a boardfamily entry block-by-block then field-by-field — a per-board entry that sets only `minimal:` keeps the family's `desktop:` block intact; a per-board `desktop: {suffix: x}` keeps the family's `desktop.{release, branch}` while replacing only `suffix`. Use this to carve a partial exception out of a family rule without repeating its other blocks. -**Note**: -- Manual extensions from `targets-extensions.map` are MERGED with automatic extensions. -- Extensions in `targets-extensions.map.blacklist` are REMOVED from both automatic and manual extensions. -- The blacklist takes precedence over all other extension sources. +Schema is regex-only (no bash sourcing); cycles in source-via-yaml references are guarded; malformed entries (missing match key, non-mapping inner blocks, unknown top-level keys) are dropped with a warning at load time. ## Release codename substitution -Both this generator's hardcoded YAML stanzas and every `*.manual` -override file use **two symbolic release tokens** instead of literal -Debian/Ubuntu codenames: +Both this generator's hardcoded YAML stanzas and every `*.manual` override file use **two symbolic release tokens** instead of literal Debian / Ubuntu codenames: -| Token | Substituted with | -|----------|-------------------------------------| +| Token | Substituted with | +|---|---| | `DEBIAN` | the codename passed via `--debian-` | | `UBUNTU` | the codename passed via `--ubuntu-` | -Each output file (`apps`, `standard`, `nightly`, `community`) gets its -own (debian, ubuntu) flag pair, so promoting one release line is -independent of the others. +Each output file (`apps`, `standard`, `nightly`, `community`) gets its own (debian, ubuntu) flag pair, so promoting one release line is independent of the others. -**Promoting a release** is now a flag flip on the workflow dispatch -inputs — no script edit, no manual-file edit: +The substitution is anchored to start-of-line so a sibling key like `KERNEL_RELEASE: UBUNTU` won't have its `RELEASE: UBUNTU` substring corrupted. + +**Promoting a release line** is a flag flip on the workflow dispatch inputs — no script edit, no manual-file edit: ```bash -# Standard support stays on noble; nightly moves Ubuntu to oracular +# Move nightly Ubuntu to the next interim while standard stays on its current LTS python3 scripts/generate_targets.py image-info.json release-targets/ \ - --ubuntu-nightly oracular + --ubuntu-nightly questing ``` -`apps-kali` keeps `RELEASE: sid` literal — Kali tracks Debian unstable -forever, that's not a "current Debian stable" pin. +`apps-kali` keeps `RELEASE: sid` literal — Kali tracks Debian unstable forever, that's not a "current Debian stable" pin. Same trick is used in the Bianbu emit (`RELEASE: noble` literal) where the SpacemiT Mesa fork is only published for noble. -## Usage +## Per-scope codename flags -```bash -python3 scripts/generate_targets.py [output_directory] \ - [--debian-standard CODENAME] [--ubuntu-standard CODENAME] \ - [--debian-nightly CODENAME] [--ubuntu-nightly CODENAME] \ - [--debian-community CODENAME] [--ubuntu-community CODENAME] \ - [--debian-apps CODENAME] [--ubuntu-apps CODENAME] -``` +| Flag | Default | Used in | +|---|---|---| +| `--debian-standard` | `trixie` | `targets-release-standard-support.yaml` | +| `--ubuntu-standard` | `resolute` | `targets-release-standard-support.yaml` | +| `--debian-nightly` | `forky` | `targets-release-nightly.yaml` | +| `--ubuntu-nightly` | `resolute` | `targets-release-nightly.yaml` | +| `--debian-community` | `trixie` | `targets-release-community-maintained.yaml` | +| `--ubuntu-community` | `resolute` | `targets-release-community-maintained.yaml` | +| `--debian-apps` | `trixie` | `targets-release-apps.yaml` | +| `--ubuntu-apps` | `noble` | `targets-release-apps.yaml` | -`image-info.json` is required. `output_directory` defaults to the -current directory and should contain `targets-extensions.map` plus any -`.blacklist` / `.manual` files. +The same per-scope codenames also drive the regex patterns in `exposed.map`, so the build matrix and the "recommended images" filter on the website stay in lockstep — bumping `--ubuntu-standard` updates both atomically. -### Per-scope codename flags +`SCOPE_DEFAULTS` in `scripts/generate_targets.py` is the single place to change the project-wide default; per-run overrides are passed via the workflow's `workflow_dispatch` inputs. -| Flag | Default | Substitutes `RELEASE: …` in | -|-----------------------|------------|-------------------------------| -| `--debian-standard` | `trixie` | `targets-release-standard-support.yaml` | -| `--ubuntu-standard` | `resolute` | `targets-release-standard-support.yaml` | -| `--debian-nightly` | `forky` | `targets-release-nightly.yaml` | -| `--ubuntu-nightly` | `resolute` | `targets-release-nightly.yaml` | -| `--debian-community` | `trixie` | `targets-release-community-maintained.yaml` | -| `--ubuntu-community` | `resolute` | `targets-release-community-maintained.yaml` | -| `--debian-apps` | `trixie` | `targets-release-apps.yaml` | -| `--ubuntu-apps` | `noble` | `targets-release-apps.yaml` | +## Automatic extensions -The same per-scope codenames also drive the regex patterns in -`exposed.map`, so the build matrix and the "recommended images" filter -on the website stay in lockstep — bumping `--ubuntu-standard` updates -both atomically. +All fast-HDMI boards (64-bit boards with video output) automatically get: -Defaults reproduce the previous literal pins exactly; running with no -flags is byte-identical to the pre-substitution behaviour (modulo the -symbolic-token round-trip). +- `v4l2loopback-dkms` +- `mesa-vpu` -## Example +Manual entries from `targets-extensions.map` are merged with these; entries in `targets-extensions.map.blacklist` are removed from both automatic and manual sets. The blacklist wins. -```bash -# Download latest image-info.json -curl -L -o image-info.json https://github.armbian.com/image-info.json +## Examples -# Generate target YAML files using all default codenames +```bash +# Default — produces the configured-default codenames for every scope python3 scripts/generate_targets.py image-info.json release-targets/ -# Roll standard-support's Ubuntu line back to the previous LTS -# (default is resolute; this pins to noble for one invocation) +# Roll standard-support's Ubuntu line back to the previous LTS for one invocation python3 scripts/generate_targets.py image-info.json release-targets/ \ --ubuntu-standard noble + +# Pin nightly to a forward-looking interim while leaving everything else alone +python3 scripts/generate_targets.py image-info.json release-targets/ \ + --ubuntu-nightly questing + +# Fetch the live inventory then regenerate +curl -L -o image-info.json https://github.armbian.com/image-info.json +python3 scripts/generate_targets.py image-info.json release-targets/ ``` ## Requirements - Python 3.6+ -- image-info.json (from Armbian build framework or github.armbian.com) -- targets-extensions.map (optional, for adding manual extensions) -- targets-extensions.map.blacklist (optional, for removing unwanted extensions) -- targets-release-.blacklist (optional, per target type) -- targets-release-.manual (optional, per target type) +- `image-info.json` from the Armbian build framework (or `https://github.armbian.com/image-info.json`) +- `pyyaml` (loaded for `exposed.map.overrides.yaml`; available by default on most CI images) + +All other inputs (`targets-extensions.map`, blacklists, `*.manual`, `exposed.map.overrides.yaml`, `reusable.yml`) are optional.