From 44210ab49774965b1feed271d6cfe65302f8f111 Mon Sep 17 00:00:00 2001 From: samueltaripin Date: Mon, 23 Feb 2026 16:37:15 +0800 Subject: [PATCH 1/5] ptl bronze and rename allow package --- image-templates/emt3-x86_64-ptl-emf-raw.yml | 73 ++++++++++++++++- .../emt3-x86_64-ptl-emf-rt-raw.yml | 79 +++++++++++++++++-- image-templates/rcd10-x86_64-dlstreamer.yml | 2 +- internal/config/config.go | 2 +- .../schema/os-image-template.schema.json | 2 +- 5 files changed, 145 insertions(+), 13 deletions(-) diff --git a/image-templates/emt3-x86_64-ptl-emf-raw.yml b/image-templates/emt3-x86_64-ptl-emf-raw.yml index 8a8f7c8b..fc4e4cb2 100644 --- a/image-templates/emt3-x86_64-ptl-emf-raw.yml +++ b/image-templates/emt3-x86_64-ptl-emf-raw.yml @@ -30,9 +30,32 @@ packageRepositories: url: "https://files-rs.edgeorchestration.intel.com/files-edge-orch/microvisor/rpms/next/base" pkey: "https://raw.githubusercontent.com/open-edge-platform/edge-microvisor-toolkit/refs/heads/3.0/SPECS/edge-repos/INTEL-RPM-GPG-KEY" # list of allowed packages from this repository - AllowPackages: + allowPackages: - kernel-6.17.11 - kernel-drivers-gpu-6.17.11 + - gstreamer1-plugins-base + - spice-server + - spice-protocol + # Dependencies for gstreamer1-plugins-base + - libvorbis + - libvisual + - libtheora + - orc + - libogg + - graphene + - iso-codes + - libcdda_interface0 + - libcdda_paranoia0 + # Dependencies for spice-server + - opus + # libepoxy and QEMU graphics dependencies + - libepoxy + - qemu-audio-spice + - qemu-char-spice + - qemu-device-display-qxl + - qemu-ui-opengl + - qemu-ui-spice-app + - qemu-ui-spice-core disk: name: edge-non-rt # 1:1 mapping to the systemConfigs name @@ -40,7 +63,7 @@ disk: - type: raw # image file format, valid value [raw, vhd, vhdx, qcow2, vmdk, vdi] compression: gz # image compression format (optional) - size: 4GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) + size: 32GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) partitionTableType: gpt # Partition table type, valid value: [gpt, mbr] partitions: # Required for raw, optional for ISO, not needed for rootfs. - id: boot @@ -65,11 +88,15 @@ disk: - id: edge_persistent type: linux start: 3584MiB - end: "0" # use the rest of the disk space + end: 12288MiB fsType: ext4 mountPoint: /opt - + - id: swap + type: linux-swap + start: 12288MiB + end: "0" # use the rest of the disk space + fsType: linux-swap systemConfig: @@ -93,6 +120,44 @@ systemConfig: - tpm-cryptsetup - persistent-mount - intel-npu-driver + # Development tools + - perl + - gcc + - gcc-c++ + - make + # QEMU full system emulation + - qemu + - qemu-user + - qemu-img + - qemu-tools + - qemu-system-aarch64 + - qemu-system-arm + - qemu-system-riscv + - qemu-system-x86 + # Graphics and display stack + - mesa-dri-drivers + - mesa-libGL + - mesa-libEGL + - libX11 + - libepoxy + - libwayland-client + - cairo + - pango + - freetype + - fontconfig + # Multimedia + - gstreamer1 + - gstreamer1-plugins-base + # Container runtime + - containerd2 + - containerd2-core + # Additional tools + - helm + - intel-level-zero-zello_world + - lm-sensors + - llvm + - spice-server + - spice-protocol # Kernel Configuration kernel: diff --git a/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml b/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml index 870da4a5..d9912d32 100644 --- a/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml +++ b/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml @@ -32,9 +32,32 @@ packageRepositories: url: "https://files-rs.edgeorchestration.intel.com/files-edge-orch/microvisor/rpms/next/base" pkey: "https://raw.githubusercontent.com/open-edge-platform/edge-microvisor-toolkit/refs/heads/3.0/SPECS/edge-repos/INTEL-RPM-GPG-KEY" # list of allowed packages from this repository - AllowPackages: - - kernel-rt-6.17.11 - - kernel-rt-drivers-gpu-6.17.11 + allowPackages: + - kernel-6.17.11 + - kernel-drivers-gpu-6.17.11 + - gstreamer1-plugins-base + - spice-server + - spice-protocol + # Dependencies for gstreamer1-plugins-base + - libvorbis + - libvisual + - libtheora + - orc + - libogg + - graphene + - iso-codes + - libcdda_interface0 + - libcdda_paranoia0 + # Dependencies for spice-server + - opus + # libepoxy and QEMU graphics dependencies + - libepoxy + - qemu-audio-spice + - qemu-char-spice + - qemu-device-display-qxl + - qemu-ui-opengl + - qemu-ui-spice-app + - qemu-ui-spice-core disk: name: edge-rt # 1:1 mapping to the systemConfigs name @@ -42,7 +65,7 @@ disk: - type: raw # image file format, valid value [raw, vhd, vhdx, qcow2, vmdk, vdi] compression: gz # image compression format (optional) - size: 4GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) + size: 32GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) partitionTableType: gpt # Partition table type, valid value: [gpt, mbr] partitions: # Required for raw, optional for ISO, not needed for rootfs. - id: boot @@ -62,15 +85,21 @@ disk: end: 3584MiB fsType: ext4 mountPoint: / - mountOptions: defaults, ro + mountOptions: defaults - id: edge_persistent type: linux start: 3584MiB - end: "0" # use the rest of the disk space + end: 12288MiB fsType: ext4 mountPoint: /opt + - id: swap + type: linux-swap + start: 12288MiB + end: "0" # use the rest of the disk space + fsType: linux-swap + systemConfig: additionalFiles: @@ -93,6 +122,44 @@ systemConfig: - tpm-cryptsetup - persistent-mount - intel-npu-driver + # Development tools + - perl + - gcc + - gcc-c++ + - make + # QEMU full system emulation + - qemu + - qemu-user + - qemu-img + - qemu-tools + - qemu-system-aarch64 + - qemu-system-arm + - qemu-system-riscv + - qemu-system-x86 + # Graphics and display stack + - mesa-dri-drivers + - mesa-libGL + - mesa-libEGL + - libX11 + - libepoxy + - libwayland-client + - cairo + - pango + - freetype + - fontconfig + # Multimedia + - gstreamer1 + - gstreamer1-plugins-base + # Container runtime + - containerd2 + - containerd2-core + # Additional tools + - helm + - intel-level-zero-zello_world + - lm-sensors + - llvm + - spice-server + - spice-protocol # Kernel Configuration kernel: diff --git a/image-templates/rcd10-x86_64-dlstreamer.yml b/image-templates/rcd10-x86_64-dlstreamer.yml index 3252d0d7..d3e6f67c 100644 --- a/image-templates/rcd10-x86_64-dlstreamer.yml +++ b/image-templates/rcd10-x86_64-dlstreamer.yml @@ -12,7 +12,7 @@ packageRepositories: - codename: "edge-base" url: "https://files-rs.edgeorchestration.intel.com/files-edge-orch/microvisor/rpms/3.0/base" pkey: "https://raw.githubusercontent.com/open-edge-platform/edge-microvisor-toolkit/refs/heads/3.0/SPECS/edge-repos/INTEL-RPM-GPG-KEY" # Uncomment and replace in real config - AllowPackages: + allowPackages: - kernel-drivers-gpu - kernel - grub2-configuration diff --git a/internal/config/config.go b/internal/config/config.go index 622040b7..aaffe146 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -48,7 +48,7 @@ type PackageRepository struct { PKey string `yaml:"pkey"` // Public GPG key URL for verification Component string `yaml:"component,omitempty"` // Repository component (e.g., "main", "restricted") Priority int `yaml:"priority,omitempty"` // Repository priority (higher numbers = higher priority) - AllowPackages []string `yaml:"AllowPackages,omitempty"` // Optional: specific packages to include from this repo (pinning) + AllowPackages []string `yaml:"allowPackages,omitempty"` // Optional: specific packages to include from this repo (pinning) } // ProviderRepoConfig represents the repository configuration for a provider diff --git a/internal/config/schema/os-image-template.schema.json b/internal/config/schema/os-image-template.schema.json index a0296c66..ad6ff1c9 100644 --- a/internal/config/schema/os-image-template.schema.json +++ b/internal/config/schema/os-image-template.schema.json @@ -344,7 +344,7 @@ "maximum": 9999, "default": 0 }, - "AllowPackages": { + "allowPackages": { "type": "array", "description": "Optional: specific packages to include from this repository (package pinning)", "items": { From ae916add9d698fc5045141c04aa63f11220c0e9b Mon Sep 17 00:00:00 2001 From: samueltaripin Date: Thu, 26 Feb 2026 13:33:25 +0800 Subject: [PATCH 2/5] ptl bronze missing packages added, add feature repo priority and wildcard package name in rpm --- image-templates/emt3-x86_64-ptl-emf-raw.yml | 171 +++++++++++++- .../emt3-x86_64-ptl-emf-rt-raw.yml | 173 +++++++++++++- .../ubuntu24-x86_64-minimal-ptl-dls.yml | 5 + .../ubuntu24-x86_64-minimal-ptl.yml | 5 + .../schema/os-image-template.schema.json | 7 +- internal/config/validate/validate_test.go | 72 ++++++ internal/ospackage/rpmutils/helper.go | 211 +++++++++++++----- internal/ospackage/rpmutils/helper_test.go | 60 +++++ internal/ospackage/rpmutils/resolver.go | 32 ++- internal/ospackage/rpmutils/resolver_test.go | 80 +++++++ 10 files changed, 743 insertions(+), 73 deletions(-) diff --git a/image-templates/emt3-x86_64-ptl-emf-raw.yml b/image-templates/emt3-x86_64-ptl-emf-raw.yml index fc4e4cb2..9baaef4a 100644 --- a/image-templates/emt3-x86_64-ptl-emf-raw.yml +++ b/image-templates/emt3-x86_64-ptl-emf-raw.yml @@ -27,6 +27,7 @@ target: packageRepositories: - codename: "emtNext" + priority: 1001 url: "https://files-rs.edgeorchestration.intel.com/files-edge-orch/microvisor/rpms/next/base" pkey: "https://raw.githubusercontent.com/open-edge-platform/edge-microvisor-toolkit/refs/heads/3.0/SPECS/edge-repos/INTEL-RPM-GPG-KEY" # list of allowed packages from this repository @@ -36,6 +37,19 @@ packageRepositories: - gstreamer1-plugins-base - spice-server - spice-protocol + - busybox + - ethtool + - iperf3 + - msr-tools + - lsscsi + - tpm2-abrmd + - binutils + - cifs-utils + - opencl-headers + - linuxptp + - libva-utils + - pulseaudio + - libsndfile # Dependencies for gstreamer1-plugins-base - libvorbis - libvisual @@ -50,12 +64,85 @@ packageRepositories: - opus # libepoxy and QEMU graphics dependencies - libepoxy + - qemu + - qemu-common + - qemu-user + - qemu-img + - qemu-tools + - qemu-system-aarch64 + - qemu-system-arm + - qemu-system-riscv + - qemu-system-x86-core - qemu-audio-spice - qemu-char-spice - qemu-device-display-qxl - qemu-ui-opengl - qemu-ui-spice-app - qemu-ui-spice-core + # Packages aligned with installer script + - gdb + - ovmf + - openbox + - libpng-devel + - libXinerama-devel + - libXrandr-devel + - libXt-devel + - libXcursor-devel + - libxml2-devel + - pango-devel + - freefont + - xorg-x11-xkb-utils + - xorg-x11-server-utils + - xorg-x11-apps + - dracut-virtio + - qemu-guest-agent + - qemu-audio-pa + - qemu-with-ui + - qemu-kvm + - qemu-system-x86 + - qemu-ui-curses + - qemu-device-usb-redirect + - qemu-device-usb-host + - qemu-device-display-virtio-vga + - qemu-device-display-virtio-gpu-pci + - qemu-device-display-virtio-gpu-ccw + - qemu-device-display-virtio-gpu + - qemu-block-nfs + - qemu-block-iscsi + - qemu-block-dmg + - qemu-block-curl + - qemu-audio-oss + - qemu-audio-alsa + - qemu-ui-gtk + - qemu-ui-egl-headless + - qemu-ipxe + - libvirt + - swtpm-tools + - dmidecode + - cvt + - xorg-x11-xinit + - unzip + - alsa-lib + - libvpl + - wayland* + - tpm2-tools + - i2c-tools + - mesa-filesystem + - mesa-demos + - mesa-libgbm + - mesa-libEGL-devel + - mesa-libGL-devel + - alsa-utils + - libva* + - intel-lms + - xorg-x11-server-Xorg + - intel-opencl + - ocl-icd + - clinfo + - kernel-drivers-sound + - codename: "edgeai" + url: "https://yum.repos.intel.com/edgeai/" + pkey: "https://yum.repos.intel.com/edgeai/GPG-PUB-KEY-INTEL-DLS.gpg" disk: name: edge-non-rt # 1:1 mapping to the systemConfigs name @@ -63,7 +150,7 @@ disk: - type: raw # image file format, valid value [raw, vhd, vhdx, qcow2, vmdk, vdi] compression: gz # image compression format (optional) - size: 32GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) + size: 37376MiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) partitionTableType: gpt # Partition table type, valid value: [gpt, mbr] partitions: # Required for raw, optional for ISO, not needed for rootfs. - id: boot @@ -80,21 +167,21 @@ disk: - id: rootfs type: linux-root-amd64 start: 384MiB - end: 3584MiB + end: 8192MiB fsType: ext4 mountPoint: / mountOptions: defaults - id: edge_persistent type: linux - start: 3584MiB - end: 12288MiB + start: 8192MiB + end: 16896MiB fsType: ext4 mountPoint: /opt - id: swap type: linux-swap - start: 12288MiB + start: 16896MiB end: "0" # use the rest of the disk space fsType: linux-swap @@ -120,6 +207,19 @@ systemConfig: - tpm-cryptsetup - persistent-mount - intel-npu-driver + - busybox + - ethtool + - iperf3 + - msr-tools + - lsscsi + - tpm2-abrmd + - binutils + - cifs-utils + - opencl-headers + - linuxptp + - libva-utils + - pulseaudio + - libsndfile # Development tools - perl - gcc @@ -158,12 +258,71 @@ systemConfig: - llvm - spice-server - spice-protocol + # installer_emt.sh package set + - gdb + - edk2-ovmf + - openbox + - libpng-devel + - libXinerama-devel + - libXrandr-devel + - libXt-devel + - libXcursor-devel + - libxml2-devel + - pango-devel + - freefont + - xorg-x11-xkb-utils + - xorg-x11-server-utils + - xorg-x11-apps + - dracut-virtio + - qemu-guest-agent + - qemu-audio-pa + - qemu-with-ui + - qemu-kvm + - libvirt + - swtpm-tools + - dmidecode + - cvt + - xorg-x11-xinit + - unzip + - alsa-lib + - libvpl + - wayland* + - tpm2-tools + - i2c-tools + - mesa-filesystem + - mesa-demos + - mesa-libgbm + - mesa-libEGL-devel + - mesa-libGL-devel + - alsa-utils + - libva* + - intel-lms + - xorg-x11-server-Xorg + - intel-opencl + - ocl-icd + - clinfo + - kernel-drivers-sound + - intel-dlstreamer # Kernel Configuration kernel: version: "6.17" - cmdline: "root=/dev/mapper/rootfs_verity console=ttyS0,115200 console=tty0 loglevel=7 sysctl.vm.overcommit_memory=1 sysctl.kernel.panic=10 sysctl.kernel.panic_on_oops=1 sysctl.fs.inotify.max_user_instances=8192 rd.parallel=1 rd.shell=0 rd.timeout=200 rd.emergency=reboot" + cmdline: "root=/dev/mapper/rootfs_verity loglevel=7 sysctl.vm.overcommit_memory=1 sysctl.kernel.panic=10 sysctl.kernel.panic_on_oops=1 sysctl.fs.inotify.max_user_instances=8192 rd.parallel=1 rd.shell=0 rd.timeout=200 rd.emergency=reboot xe.max_vfs=7 xe.force_probe=* modprobe.blacklist=i915 udmabuf.list_limit=8192 console=tty0 console=ttyS0,115200n8" enableExtraModules: "intel_vpu uas" packages: - kernel-drivers-gpu-6.17.11 + + configurations: + - cmd: "echo 'user ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/user-sudo" + - cmd: "chmod 440 /etc/sudoers.d/user-sudo" + - cmd: "grep -q '^http_proxy=http://proxy-dmz.intel.com:911$' /etc/environment || echo 'http_proxy=http://proxy-dmz.intel.com:911' >> /etc/environment" + - cmd: "grep -q '^https_proxy=http://proxy-dmz.intel.com:912$' /etc/environment || echo 'https_proxy=http://proxy-dmz.intel.com:912' >> /etc/environment" + - cmd: "grep -q '^ftp_proxy=http://proxy-dmz.intel.com:911$' /etc/environment || echo 'ftp_proxy=http://proxy-dmz.intel.com:911' >> /etc/environment" + - cmd: "grep -q '^socks_server=http://proxy-dmz.intel.com:1080$' /etc/environment || echo 'socks_server=http://proxy-dmz.intel.com:1080' >> /etc/environment" + - cmd: "grep -q '^no_proxy=.internal,10.*,10.0.0.0/8,127.0.0.1,169.254.169.254,::1,localhost,10.49.76.92$' /etc/environment || echo 'no_proxy=.internal,10.*,10.0.0.0/8,127.0.0.1,169.254.169.254,::1,localhost,10.49.76.92' >> /etc/environment" + - cmd: "grep -q '^no_proxy=localhost,127.0.0.1,127.0.1.1,127.0.0.0/8,172.16.0.0/20,192.168.0.0/16,10.0.0.0/8,10.1.0.0/16,10.152.183.0/24,devtools.intel.com,jf.intel.com,teamcity-or.intel.com,caas.intel.com,inn.intel.com,isscorp.intel.com,gfx-assets.fm.intel.com$' /etc/environment || echo 'no_proxy=localhost,127.0.0.1,127.0.1.1,127.0.0.0/8,172.16.0.0/20,192.168.0.0/16,10.0.0.0/8,10.1.0.0/16,10.152.183.0/24,devtools.intel.com,jf.intel.com,teamcity-or.intel.com,caas.intel.com,inn.intel.com,isscorp.intel.com,gfx-assets.fm.intel.com' >> /etc/environment" + - cmd: "grep -q '^http_proxy = http://proxy-dmz.intel.com:911$' /etc/wgetrc || echo 'http_proxy = http://proxy-dmz.intel.com:911' >> /etc/wgetrc" + - cmd: "grep -q '^https_proxy = http://proxy-dmz.intel.com:912$' /etc/wgetrc || echo 'https_proxy = http://proxy-dmz.intel.com:912' >> /etc/wgetrc" + + \ No newline at end of file diff --git a/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml b/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml index d9912d32..1c3f96b1 100644 --- a/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml +++ b/image-templates/emt3-x86_64-ptl-emf-rt-raw.yml @@ -38,6 +38,19 @@ packageRepositories: - gstreamer1-plugins-base - spice-server - spice-protocol + - busybox + - ethtool + - iperf3 + - msr-tools + - lsscsi + - tpm2-abrmd + - binutils + - cifs-utils + - opencl-headers + - linuxptp + - libva-utils + - pulseaudio + - libsndfile # Dependencies for gstreamer1-plugins-base - libvorbis - libvisual @@ -52,12 +65,85 @@ packageRepositories: - opus # libepoxy and QEMU graphics dependencies - libepoxy + - qemu + - qemu-common + - qemu-user + - qemu-img + - qemu-tools + - qemu-system-aarch64 + - qemu-system-arm + - qemu-system-riscv + - qemu-system-x86-core - qemu-audio-spice - qemu-char-spice - qemu-device-display-qxl - qemu-ui-opengl - qemu-ui-spice-app - qemu-ui-spice-core + # Packages aligned with installer script + - gdb + - ovmf + - openbox + - libpng-devel + - libXinerama-devel + - libXrandr-devel + - libXt-devel + - libXcursor-devel + - libxml2-devel + - pango-devel + - freefont + - xorg-x11-xkb-utils + - xorg-x11-server-utils + - xorg-x11-apps + - dracut-virtio + - qemu-guest-agent + - qemu-audio-pa + - qemu-with-ui + - qemu-kvm + - qemu-system-x86 + - qemu-ui-curses + - qemu-device-usb-redirect + - qemu-device-usb-host + - qemu-device-display-virtio-vga + - qemu-device-display-virtio-gpu-pci + - qemu-device-display-virtio-gpu-ccw + - qemu-device-display-virtio-gpu + - qemu-block-nfs + - qemu-block-iscsi + - qemu-block-dmg + - qemu-block-curl + - qemu-audio-oss + - qemu-audio-alsa + - qemu-ui-gtk + - qemu-ui-egl-headless + - qemu-ipxe + - libvirt + - swtpm-tools + - dmidecode + - cvt + - xorg-x11-xinit + - unzip + - alsa-lib + - libvpl + - wayland* + - tpm2-tools + - i2c-tools + - mesa-filesystem + - mesa-demos + - mesa-libgbm + - mesa-libEGL-devel + - mesa-libGL-devel + - alsa-utils + - libva* + - intel-lms + - xorg-x11-server-Xorg + - intel-opencl + - ocl-icd + - clinfo + - kernel-rt-drivers-sound + - codename: "edgeai" + url: "https://yum.repos.intel.com/edgeai/" + pkey: "https://yum.repos.intel.com/edgeai/GPG-PUB-KEY-INTEL-DLS.gpg" disk: name: edge-rt # 1:1 mapping to the systemConfigs name @@ -65,7 +151,7 @@ disk: - type: raw # image file format, valid value [raw, vhd, vhdx, qcow2, vmdk, vdi] compression: gz # image compression format (optional) - size: 32GiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) + size: 37376MiB # 4G, 4GB, 4096 MiB also valid. (Required for raw) partitionTableType: gpt # Partition table type, valid value: [gpt, mbr] partitions: # Required for raw, optional for ISO, not needed for rootfs. - id: boot @@ -82,21 +168,21 @@ disk: - id: rootfs type: linux-root-amd64 start: 384MiB - end: 3584MiB + end: 8192MiB fsType: ext4 mountPoint: / mountOptions: defaults - id: edge_persistent type: linux - start: 3584MiB - end: 12288MiB + start: 8192MiB + end: 16896MiB fsType: ext4 mountPoint: /opt - id: swap type: linux-swap - start: 12288MiB + start: 16896MiB end: "0" # use the rest of the disk space fsType: linux-swap @@ -122,6 +208,19 @@ systemConfig: - tpm-cryptsetup - persistent-mount - intel-npu-driver + - busybox + - ethtool + - iperf3 + - msr-tools + - lsscsi + - tpm2-abrmd + - binutils + - cifs-utils + - opencl-headers + - linuxptp + - libva-utils + - pulseaudio + - libsndfile # Development tools - perl - gcc @@ -160,11 +259,69 @@ systemConfig: - llvm - spice-server - spice-protocol + # installer_emt.sh package set + - gdb + - edk2-ovmf + - openbox + - libpng-devel + - libXinerama-devel + - libXrandr-devel + - libXt-devel + - libXcursor-devel + - libxml2-devel + - pango-devel + - freefont + - xorg-x11-xkb-utils + - xorg-x11-server-utils + - xorg-x11-apps + - dracut-virtio + - qemu-guest-agent + - qemu-audio-pa + - qemu-with-ui + - qemu-kvm + - libvirt + - swtpm-tools + - dmidecode + - cvt + - xorg-x11-xinit + - unzip + - alsa-lib + - libvpl + - wayland* + - tpm2-tools + - i2c-tools + - mesa-filesystem + - mesa-demos + - mesa-libgbm + - mesa-libEGL-devel + - mesa-libGL-devel + - alsa-utils + - libva* + - intel-lms + - xorg-x11-server-Xorg + - intel-opencl + - ocl-icd + - clinfo + - kernel-rt-drivers-sound + - intel-dlstreamer # Kernel Configuration kernel: - version: "6.12" - cmdline: "root=/dev/mapper/rootfs_verity console=ttyS0,115200 console=tty0 loglevel=7 sysctl.vm.overcommit_memory=1 sysctl.kernel.panic=10 sysctl.kernel.panic_on_oops=1 sysctl.fs.inotify.max_user_instances=8192 rd.parallel=1 rd.shell=0 rd.timeout=200 rd.emergency=reboot" + version: "6.17" + cmdline: "modprobe.blacklist=i915 processor.max_cstate=0 intel.max_cstate=0 processor_idle.max_cstate=0 intel_idle.max_cstate=0 clocksource=tsc tsc=reliable nowatchdog intel_pstate=disable idle=poll nosmt isolcpus=2,3 rcu_nocbs=2,3 rcupdate.rcu_cpu_stall_suppress=1 rcu_nocb_poll irqaffinity=0 mce=off hpet=disable numa_balancing=disable igb.blacklist=no nmi_watchdog=0 nosoftlockup" enableExtraModules: "intel_vpu uas" packages: - - kernel-rt-drivers-gpu \ No newline at end of file + - kernel-rt-drivers-gpu + + configurations: + - cmd: "echo 'user ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/user-sudo" + - cmd: "chmod 440 /etc/sudoers.d/user-sudo" + - cmd: "grep -q '^http_proxy=http://proxy-dmz.intel.com:911$' /etc/environment || echo 'http_proxy=http://proxy-dmz.intel.com:911' >> /etc/environment" + - cmd: "grep -q '^https_proxy=http://proxy-dmz.intel.com:912$' /etc/environment || echo 'https_proxy=http://proxy-dmz.intel.com:912' >> /etc/environment" + - cmd: "grep -q '^ftp_proxy=http://proxy-dmz.intel.com:911$' /etc/environment || echo 'ftp_proxy=http://proxy-dmz.intel.com:911' >> /etc/environment" + - cmd: "grep -q '^socks_server=http://proxy-dmz.intel.com:1080$' /etc/environment || echo 'socks_server=http://proxy-dmz.intel.com:1080' >> /etc/environment" + - cmd: "grep -q '^no_proxy=.internal,10.*,10.0.0.0/8,127.0.0.1,169.254.169.254,::1,localhost,10.49.76.92$' /etc/environment || echo 'no_proxy=.internal,10.*,10.0.0.0/8,127.0.0.1,169.254.169.254,::1,localhost,10.49.76.92' >> /etc/environment" + - cmd: "grep -q '^no_proxy=localhost,127.0.0.1,127.0.1.1,127.0.0.0/8,172.16.0.0/20,192.168.0.0/16,10.0.0.0/8,10.1.0.0/16,10.152.183.0/24,devtools.intel.com,jf.intel.com,teamcity-or.intel.com,caas.intel.com,inn.intel.com,isscorp.intel.com,gfx-assets.fm.intel.com$' /etc/environment || echo 'no_proxy=localhost,127.0.0.1,127.0.1.1,127.0.0.0/8,172.16.0.0/20,192.168.0.0/16,10.0.0.0/8,10.1.0.0/16,10.152.183.0/24,devtools.intel.com,jf.intel.com,teamcity-or.intel.com,caas.intel.com,inn.intel.com,isscorp.intel.com,gfx-assets.fm.intel.com' >> /etc/environment" + - cmd: "grep -q '^http_proxy = http://proxy-dmz.intel.com:911$' /etc/wgetrc || echo 'http_proxy = http://proxy-dmz.intel.com:911' >> /etc/wgetrc" + - cmd: "grep -q '^https_proxy = http://proxy-dmz.intel.com:912$' /etc/wgetrc || echo 'https_proxy = http://proxy-dmz.intel.com:912' >> /etc/wgetrc" + diff --git a/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml b/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml index b4b3ea75..a0d9df2d 100755 --- a/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml +++ b/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml @@ -364,6 +364,11 @@ systemConfig: - xnest_2:21.1.12-1ppa1~noble3 - xserver-xorg-dev_2:21.1.12-1ppa1~noble3 - xvfb_2:21.1.12-1ppa1~noble3 + # additinal packages + - tcpdump + - usb-modeswitch + - fwupd + - clinfo #dls based pkg - docker.io - docker-compose-plugin diff --git a/image-templates/ubuntu24-x86_64-minimal-ptl.yml b/image-templates/ubuntu24-x86_64-minimal-ptl.yml index 6fdf73ac..8a5c770d 100644 --- a/image-templates/ubuntu24-x86_64-minimal-ptl.yml +++ b/image-templates/ubuntu24-x86_64-minimal-ptl.yml @@ -357,6 +357,11 @@ systemConfig: - xnest_2:21.1.12-1ppa1~noble3 - xserver-xorg-dev_2:21.1.12-1ppa1~noble3 - xvfb_2:21.1.12-1ppa1~noble3 + # additinal packages + - tcpdump + - usb-modeswitch + - fwupd + - clinfo additionalFiles: - local: ../additionalfiles/ptl/01-network-manager-all.yaml diff --git a/internal/config/schema/os-image-template.schema.json b/internal/config/schema/os-image-template.schema.json index ad6ff1c9..78af42e4 100644 --- a/internal/config/schema/os-image-template.schema.json +++ b/internal/config/schema/os-image-template.schema.json @@ -257,8 +257,8 @@ "bootloader": { "$ref": "#/$defs/Bootloader" }, "packages": { "type": "array", - "description": "List of packages to include in the system", - "items": { "type": "string", "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9+_.:~-]*[A-Za-z0-9+])?$" }, + "description": "List of packages to include in the system (supports glob patterns like wayland* and libva*)", + "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9+_.:~*?\\[\\]-]*$" }, "uniqueItems": true }, "additionalFiles": { @@ -349,7 +349,8 @@ "description": "Optional: specific packages to include from this repository (package pinning)", "items": { "type": "string", - "minLength": 1 + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9+_.:~*?\\[\\]-]*$" } } }, diff --git a/internal/config/validate/validate_test.go b/internal/config/validate/validate_test.go index cc404e33..c6cc5a12 100644 --- a/internal/config/validate/validate_test.go +++ b/internal/config/validate/validate_test.go @@ -137,6 +137,39 @@ systemConfig: } } +func TestValidMergedTemplateWithWildcardPackages(t *testing.T) { + mergedTemplateYAML := `image: + name: test-merged-image + version: "1.0.0" + +target: + os: edge-microvisor-toolkit + dist: emt3 + arch: x86_64 + imageType: raw + +systemConfig: + name: default + packages: + - wayland* + - libva* +` + + var raw interface{} + if err := yaml.Unmarshal([]byte(mergedTemplateYAML), &raw); err != nil { + t.Fatalf("yml parsing error: %v", err) + } + + dataJSON, err := json.Marshal(raw) + if err != nil { + t.Fatalf("json marshaling error: %v", err) + } + + if err := ValidateImageTemplateJSON(dataJSON); err != nil { + t.Errorf("expected wildcard package template to pass validation, but got: %v", err) + } +} + func TestInvalidMergedTemplate(t *testing.T) { // Create an invalid merged template (missing required fields) invalidMergedTemplateYAML := `image: @@ -169,6 +202,45 @@ target: } } +func TestInvalidMergedTemplateWithMalformedAllowPackage(t *testing.T) { + invalidTemplateYAML := `image: + name: test-merged-image + version: "1.0.0" + +target: + os: edge-microvisor-toolkit + dist: emt3 + arch: x86_64 + imageType: raw + +packageRepositories: + - codename: "emtNext" + url: "https://example.com/repo" + pkey: "[trusted=yes]" + allowPackages: + - qemu-audio-oss" + +systemConfig: + name: default + packages: + - qemu-system-x86 +` + + var raw interface{} + if err := yaml.Unmarshal([]byte(invalidTemplateYAML), &raw); err != nil { + t.Fatalf("yml parsing error: %v", err) + } + + dataJSON, err := json.Marshal(raw) + if err != nil { + t.Fatalf("json marshaling error: %v", err) + } + + if err := ValidateImageTemplateJSON(dataJSON); err == nil { + t.Errorf("expected template with malformed allowPackages entry to fail validation") + } +} + // Test global config validation func TestValidConfig(t *testing.T) { v := loadFile(t, "/testdata/valid-config.yml") diff --git a/internal/ospackage/rpmutils/helper.go b/internal/ospackage/rpmutils/helper.go index 38e66ca2..7fd08d18 100644 --- a/internal/ospackage/rpmutils/helper.go +++ b/internal/ospackage/rpmutils/helper.go @@ -13,6 +13,87 @@ import ( "github.com/open-edge-platform/os-image-composer/internal/utils/logger" ) +const defaultRepoPriority = 500 + +func normalizeRepositoryPriority(priority int) int { + if priority == 0 { + return defaultRepoPriority + } + return priority +} + +func getRepositoryPriority(packageURL string) int { + normalizedPkgURL := strings.TrimRight(packageURL, "/") + highestPriority := defaultRepoPriority + + for _, repo := range UserRepo { + repoURL := strings.TrimRight(repo.URL, "/") + if repoURL == "" { + continue + } + + if normalizedPkgURL == repoURL || strings.HasPrefix(normalizedPkgURL, repoURL+"/") { + repoPriority := normalizeRepositoryPriority(repo.Priority) + if repoPriority > highestPriority { + highestPriority = repoPriority + } + } + } + + return highestPriority +} + +func selectByPriorityThenRepo(parentBase string, candidates []ospackage.PackageInfo) ospackage.PackageInfo { + highestPriority := -1 + highestPriorityCandidates := make([]ospackage.PackageInfo, 0, len(candidates)) + + for _, candidate := range candidates { + candidatePriority := getRepositoryPriority(candidate.URL) + if candidatePriority > highestPriority { + highestPriority = candidatePriority + highestPriorityCandidates = highestPriorityCandidates[:0] + highestPriorityCandidates = append(highestPriorityCandidates, candidate) + continue + } + + if candidatePriority == highestPriority { + highestPriorityCandidates = append(highestPriorityCandidates, candidate) + } + } + + if len(highestPriorityCandidates) == 1 { + return highestPriorityCandidates[0] + } + + var sameRepoCandidates []ospackage.PackageInfo + for _, candidate := range highestPriorityCandidates { + candidateBase, err := extractRepoBase(candidate.URL) + if err != nil { + continue + } + if candidateBase == parentBase { + sameRepoCandidates = append(sameRepoCandidates, candidate) + } + } + + if len(sameRepoCandidates) == 1 { + return sameRepoCandidates[0] + } + + if len(sameRepoCandidates) > 1 { + latest := sameRepoCandidates[0] + for _, candidate := range sameRepoCandidates[1:] { + cmp := compareVersions(candidate.Version, latest.Version) + if cmp > 0 { + latest = candidate + } + } + return latest + } + + return highestPriorityCandidates[0] +} + func resolveMultiCandidates(parentPkg ospackage.PackageInfo, candidates []ospackage.PackageInfo) (ospackage.PackageInfo, error) { parentBase, err := extractRepoBase(parentPkg.URL) if err != nil { @@ -31,16 +112,9 @@ func resolveMultiCandidates(parentPkg ospackage.PackageInfo, candidates []ospack } if hasVersionConstraint { - // First pass: look for candidates from the same repo that meet version constraint - var sameRepoMatches []ospackage.PackageInfo - var otherRepoMatches []ospackage.PackageInfo + var matchingCandidates []ospackage.PackageInfo for _, candidate := range candidates { - candidateBase, err := extractRepoBase(candidate.URL) - if err != nil { - continue - } - // Check if version constraint is satisfied cmp, err := comparePackageVersions(candidate.Version, ver) if err != nil { @@ -62,22 +136,12 @@ func resolveMultiCandidates(parentPkg ospackage.PackageInfo, candidates []ospack } if versionMatches { - if candidateBase == parentBase { - sameRepoMatches = append(sameRepoMatches, candidate) - } else { - otherRepoMatches = append(otherRepoMatches, candidate) - } + matchingCandidates = append(matchingCandidates, candidate) } } - // Priority 1: return first match from same repo - if len(sameRepoMatches) > 0 { - return sameRepoMatches[0], nil - } - - // Priority 2: return first match from other repos - if len(otherRepoMatches) > 0 { - return otherRepoMatches[0], nil + if len(matchingCandidates) > 0 { + return selectByPriorityThenRepo(parentBase, matchingCandidates), nil } return ospackage.PackageInfo{}, fmt.Errorf("no candidates satisfy version constraint = %s%s", op, ver) @@ -97,38 +161,7 @@ func resolveMultiCandidates(parentPkg ospackage.PackageInfo, candidates []ospack return candidates[0], nil } - // Rule 1: find all candidates with the same base URL and return the latest version - var sameBaseCandidates []ospackage.PackageInfo - for _, candidate := range candidates { - candidateBase, err := extractRepoBase(candidate.URL) - if err != nil { - continue - } - if candidateBase == parentBase { - sameBaseCandidates = append(sameBaseCandidates, candidate) - } - } - - // If we found candidates with the same base URL, return the one with the latest version - if len(sameBaseCandidates) > 0 { - if len(sameBaseCandidates) == 1 { - return sameBaseCandidates[0], nil - } - - // Find the candidate with the latest version - latest := sameBaseCandidates[0] - for _, candidate := range sameBaseCandidates[1:] { - cmp := compareVersions(candidate.Version, latest.Version) - if cmp > 0 { - latest = candidate - } - } - - return latest, nil - } - - // Rule 2: If no candidate has the same repo, return the first candidate in other repos - return candidates[0], nil + return selectByPriorityThenRepo(parentBase, candidates), nil } func extractRepoBase(rawURL string) (string, error) { @@ -345,16 +378,41 @@ func findAllCandidates(parent ospackage.PackageInfo, depName string, all []ospac return candidates, nil } +func isGlobPattern(s string) bool { + return strings.ContainsAny(s, "*?[") +} + +func matchPackageRequest(want, packageName string) bool { + if !isGlobPattern(want) { + return packageName == want + } + + matched, err := filepath.Match(want, packageName) + if err != nil { + return false + } + + return matched +} + // ResolvePackage finds the best matching package for a given package name func ResolveTopPackageConflicts(want string, all []ospackage.PackageInfo) (ospackage.PackageInfo, bool) { var candidates []ospackage.PackageInfo + isGlob := isGlobPattern(want) for _, pi := range all { // 1) exact name, e.g. acct-205-25.azl3.noarch.rpm - if pi.Name == want { + if !isGlob && pi.Name == want { candidates = append(candidates, pi) break } cleanName := extractBasePackageNameFromFile(pi.Name) + + if isGlob { + if matchPackageRequest(want, cleanName) || matchPackageRequest(want, pi.Name) { + candidates = append(candidates, pi) + } + continue + } // 2) base name, e.g. acct if cleanName == want { candidates = append(candidates, pi) @@ -425,6 +483,51 @@ func ResolveTopPackageConflicts(want string, all []ospackage.PackageInfo) (ospac return candidates[0], true } +// ResolveWildcardPackageConflicts expands a wildcard request to the best package +// for each matched base package name. +func ResolveWildcardPackageConflicts(want string, all []ospackage.PackageInfo) ([]ospackage.PackageInfo, bool) { + if !isGlobPattern(want) { + pkg, found := ResolveTopPackageConflicts(want, all) + if !found { + return nil, false + } + return []ospackage.PackageInfo{pkg}, true + } + + baseNames := make(map[string]struct{}) + for _, pi := range all { + cleanName := extractBasePackageNameFromFile(pi.Name) + if matchPackageRequest(want, cleanName) || matchPackageRequest(want, pi.Name) { + baseNames[cleanName] = struct{}{} + } + } + + if len(baseNames) == 0 { + return nil, false + } + + var results []ospackage.PackageInfo + for baseName := range baseNames { + pkg, found := ResolveTopPackageConflicts(baseName, all) + if found { + results = append(results, pkg) + } + } + + if len(results) == 0 { + return nil, false + } + + sort.Slice(results, func(i, j int) bool { + if results[i].Name == results[j].Name { + return compareVersions(results[i].Version, results[j].Version) > 0 + } + return results[i].Name < results[j].Name + }) + + return results, true +} + func extractVersionRequirement(reqVers []string, depName string) (op string, ver string, found bool) { for _, reqVer := range reqVers { reqVer = strings.TrimSpace(reqVer) diff --git a/internal/ospackage/rpmutils/helper_test.go b/internal/ospackage/rpmutils/helper_test.go index 45fdc525..495885ad 100644 --- a/internal/ospackage/rpmutils/helper_test.go +++ b/internal/ospackage/rpmutils/helper_test.go @@ -5,6 +5,7 @@ import ( "strings" "testing" + "github.com/open-edge-platform/os-image-composer/internal/config" "github.com/open-edge-platform/os-image-composer/internal/ospackage" ) @@ -519,6 +520,14 @@ func TestResolveTopPackageConflicts(t *testing.T) { Name: "acct-other", Version: "2.0-1.azl3", }, + { + Name: "wayland-1.20.0-1.azl3.x86_64.rpm", + Version: "1.20.0-1.azl3", + }, + { + Name: "wayland-devel-1.22.0-1.azl3.x86_64.rpm", + Version: "1.22.0-1.azl3", + }, } tests := []struct { @@ -556,6 +565,13 @@ func TestResolveTopPackageConflicts(t *testing.T) { dist: "", expectFound: false, }, + { + name: "Wildcard match", + want: "wayland*", + dist: "", + expectedPkg: "wayland-devel-1.22.0-1.azl3.x86_64.rpm", + expectFound: true, + }, } for _, tt := range tests { @@ -578,6 +594,7 @@ func TestResolveMultiCandidates(t *testing.T) { name string parentPkg ospackage.PackageInfo candidates []ospackage.PackageInfo + userRepos []config.PackageRepository expectedName string expectError bool }{ @@ -637,10 +654,53 @@ func TestResolveMultiCandidates(t *testing.T) { expectedName: "testpkg-2.0-1.rpm", // Should pick the one that satisfies constraint expectError: false, }, + { + name: "Higher repo priority overrides parent base", + parentPkg: ospackage.PackageInfo{ + URL: "https://example.com/repo1/parent.rpm", + }, + candidates: []ospackage.PackageInfo{ + {Name: "candidate1", Version: "1.0", URL: "https://example.com/repo1/candidate1.rpm"}, + {Name: "candidate2", Version: "2.0", URL: "https://example.com/repo2/candidate2.rpm"}, + }, + userRepos: []config.PackageRepository{ + {URL: "https://example.com/repo1", Priority: 500}, + {URL: "https://example.com/repo2", Priority: 900}, + }, + expectedName: "candidate2", + expectError: false, + }, + { + name: "Default priority 500 keeps parent base preference", + parentPkg: ospackage.PackageInfo{ + URL: "https://example.com/repo1/parent.rpm", + }, + candidates: []ospackage.PackageInfo{ + {Name: "candidate1", Version: "1.0", URL: "https://example.com/repo1/candidate1.rpm"}, + {Name: "candidate2", Version: "2.0", URL: "https://example.com/repo2/candidate2.rpm"}, + }, + userRepos: []config.PackageRepository{ + {URL: "https://example.com/repo1"}, + {URL: "https://example.com/repo2"}, + }, + expectedName: "candidate1", + expectError: false, + }, } + origUserRepo := UserRepo + defer func() { + UserRepo = origUserRepo + }() + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.userRepos != nil { + UserRepo = tt.userRepos + } else { + UserRepo = nil + } + result, err := resolveMultiCandidates(tt.parentPkg, tt.candidates) if tt.expectError { if err == nil { diff --git a/internal/ospackage/rpmutils/resolver.go b/internal/ospackage/rpmutils/resolver.go index e808f3fd..9a894138 100644 --- a/internal/ospackage/rpmutils/resolver.go +++ b/internal/ospackage/rpmutils/resolver.go @@ -161,13 +161,20 @@ func GenerateDot(pkgs []ospackage.PackageInfo, file string, pkgSources map[strin } // matchesPackageFilter checks if a package name matches any of the filter patterns. -// Supports exact match and version-specific match (e.g., "kernel-6.17.11" matches "kernel-6.17.11-1.emt3.x86_64.rpm") +// Supports glob patterns, exact match, and version-specific prefix match +// (e.g., "kernel-6.17.11" matches "kernel-6.17.11-1.emt3.x86_64.rpm"). func matchesPackageFilter(pkgName string, filter []string) bool { if len(filter) == 0 { return true // No filter means include all } for _, pattern := range filter { + if isGlobPattern(pattern) { + if ok, err := path.Match(pattern, pkgName); err == nil && ok { + return true + } + } + // Exact match if pkgName == pattern { return true @@ -615,10 +622,31 @@ func convertFlags(flags string) string { func MatchRequested(requests []string, all []ospackage.PackageInfo) ([]ospackage.PackageInfo, error) { var out []ospackage.PackageInfo + seen := make(map[string]struct{}) for _, want := range requests { + if isGlobPattern(want) { + pkgs, found := ResolveWildcardPackageConflicts(want, all) + if !found { + return nil, fmt.Errorf("requested package '%q' not found in repo", want) + } + for _, pkg := range pkgs { + key := fmt.Sprintf("%s=%s", pkg.Name, pkg.Version) + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} + out = append(out, pkg) + } + continue + } + if pkg, found := ResolveTopPackageConflicts(want, all); found { + key := fmt.Sprintf("%s=%s", pkg.Name, pkg.Version) + if _, ok := seen[key]; ok { + continue + } + seen[key] = struct{}{} out = append(out, pkg) - } else { return nil, fmt.Errorf("requested package '%q' not found in repo", want) } diff --git a/internal/ospackage/rpmutils/resolver_test.go b/internal/ospackage/rpmutils/resolver_test.go index f92d8fec..e27c27e6 100644 --- a/internal/ospackage/rpmutils/resolver_test.go +++ b/internal/ospackage/rpmutils/resolver_test.go @@ -390,6 +390,67 @@ func TestParsePrimary(t *testing.T) { } } +func TestMatchesPackageFilter(t *testing.T) { + tests := []struct { + name string + pkgName string + filter []string + want bool + }{ + { + name: "empty filter allows all", + pkgName: "any-package", + filter: nil, + want: true, + }, + { + name: "exact match", + pkgName: "qemu-common", + filter: []string{"qemu-common"}, + want: true, + }, + { + name: "prefix version match", + pkgName: "kernel-drivers-gpu-6.17.11-1.emt3.x86_64", + filter: []string{"kernel-drivers-gpu-6.17.11"}, + want: true, + }, + { + name: "glob wildcard wayland", + pkgName: "wayland-protocols-devel", + filter: []string{"wayland*"}, + want: true, + }, + { + name: "glob wildcard libva", + pkgName: "libva-intel-media-driver", + filter: []string{"libva*"}, + want: true, + }, + { + name: "glob wildcard no match", + pkgName: "mesa-libEGL", + filter: []string{"wayland*", "libva*"}, + want: false, + }, + { + name: "invalid glob does not match and does not fail", + pkgName: "wayland", + filter: []string{"wayland["}, + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := matchesPackageFilter(tt.pkgName, tt.filter) + if got != tt.want { + t.Errorf("matchesPackageFilter(%q, %v) = %v, want %v", tt.pkgName, tt.filter, got, tt.want) + } + }) + } +} + // Helper function to compress content with gzip func compressGzip(t *testing.T, content string) []byte { t.Helper() @@ -447,6 +508,18 @@ func TestMatchRequestedAdvanced(t *testing.T) { Arch: "src", URL: "https://repo.example.com/package-with-src-1.0-1.src.rpm", }, + { + Name: "wayland", + Version: "1.20.0-1.azl3", + Arch: "x86_64", + URL: "https://repo.example.com/wayland-1.20.0-1.azl3.x86_64.rpm", + }, + { + Name: "wayland-devel", + Version: "1.20.0-1.azl3", + Arch: "x86_64", + URL: "https://repo.example.com/wayland-devel-1.20.0-1.azl3.x86_64.rpm", + }, } tests := []struct { @@ -498,6 +571,13 @@ func TestMatchRequestedAdvanced(t *testing.T) { expectError: true, expectedCount: 0, }, + { + name: "Wildcard request expands to multiple packages", + requests: []string{"wayland*"}, + expectError: false, + expectedCount: 2, + expectedNames: []string{"wayland", "wayland-devel"}, + }, } for _, tt := range tests { From 71f2b7c32cd7a849557c9a1ad985f17baeaba87c Mon Sep 17 00:00:00 2001 From: samueltaripin Date: Thu, 26 Feb 2026 22:44:32 +0800 Subject: [PATCH 3/5] apply whitelist feature to debian as well, update multirepo and image template document --- .../os-image-composer-multi-repo-support.md | 119 +++++++++++------- .../os-image-composer-templates.md | 62 +++++++++ internal/ospackage/debutils/download.go | 84 +++++++------ internal/ospackage/debutils/resolver.go | 39 +++++- 4 files changed, 218 insertions(+), 86 deletions(-) diff --git a/docs/architecture/os-image-composer-multi-repo-support.md b/docs/architecture/os-image-composer-multi-repo-support.md index b1140324..4bf35cfc 100644 --- a/docs/architecture/os-image-composer-multi-repo-support.md +++ b/docs/architecture/os-image-composer-multi-repo-support.md @@ -11,8 +11,10 @@ components. This document describes the key aspects of the multiple package repo repositories, enabling the OS Image Composer tool to access and pull custom packages that are unavailable in the base repository. - **Package conflict priority consideration**: Outlines how the tool determines - which package to use when duplicates exist across repositories, prioritizing - the user-specified order and base repository integrity. + which package to use when duplicates exist across repositories using + per-repository `priority`. +- **Allow-list support**: Describes `allowPackages` (white list) to restrict + which packages are indexed from a repository. - **Architectural design**: Describes how the design extends the tool's package and dependency pre-download framework to resolve dependencies without relying on package managers like APT or @@ -38,6 +40,7 @@ components. This document describes the key aspects of the multiple package repo - [Example 4: Unresolvable dependency](#example-4-unresolvable-dependency) - [Example 5: Conflicting dependency versions](#example-5-conflicting-dependency-versions) - [Benefits of Repository Affinity](#benefits-of-repository-affinity) +- [AllowPackages White List](#allowpackages-white-list) - [Architectural Design](#architectural-design) - [Single Repository Support](#single-repository-support) - [Multiple Repositories Support](#multiple-repositories-support) @@ -50,16 +53,27 @@ You can specify additional package repositories in the OS Image Composer user template. Here's an example: ```yaml -... -additionalrepo: - intel1: "https://www.intel.com/repo1" # Add new package repo URL - intel2: "https://www.intel.com/repo2" # Add another new package repo URL - -packages: - - intelpackage01 # Package from intel1 repo - - intelpackage02 # Package from intel2 repo - - systemd-boot # Package from base repo -... +packageRepositories: + - codename: "repoA" + url: "https://repo.example.com/os/base" + pkey: "https://repo.example.com/keys/RPM-GPG-KEY" + priority: 1001 + allowPackages: + - kernel-6.17.11 + - kernel-drivers-gpu-6.17.11 + - libva* + - wayland* + + - codename: "repoB" + url: "https://repo2.example.com/os/extra" + pkey: "https://repo2.example.com/keys/RPM-GPG-KEY" + priority: 500 + +systemConfig: + packages: + - kernel-6.17.11 + - libva-utils + - my-custom-package ``` Each repository must follow the standard Debian or RPM structure, including all @@ -73,61 +87,78 @@ setup references: ## Package Conflict Priority Consideration When multiple repositories contain packages with the same name, the OS Image -Composer tool uses a simple two-rule priority system for package selection. +Composer tool uses repository `priority` to select candidates. ### Priority Rules -The OS Image Composer tool follows these straightforward rules to resolve -package conflicts: +The OS Image Composer tool follows these rules: -1. **Version Priority**: If the same package exists in multiple repositories - with different versions, the tool always selects the package with the latest - version number, regardless of which repository contains it. +1. **Higher numeric priority wins**: repositories with higher `priority` + values are preferred. +2. **Version tie-breaker**: when candidates are in the same priority class, + the resolver picks the most suitable version (usually the newest one that + satisfies constraints). +3. **Repository affinity for dependencies**: when possible, dependencies are + chosen from the same repository family as the parent package. -2. **Repository Order Priority**: If the same package exists in multiple - repositories with identical versions, the OS Image Composer tool follows - the following priority order: +Debian resolver supports additional APT-like priority behavior: - - Base OS repository (highest priority) - - Additional repositories in the order they appear in configuration +- `priority < 0`: block packages from that repository +- `priority = 990`: prefer over default repos +- `priority = 1000`: install even if version is lower +- `priority > 1000`: force preference ### Resolution Process Decision Flow: -1. Check if package versions differ → Select latest version -2. If versions are identical → Follow repository priority order: - - - Base OS repository - - intel1 (first additional repo in config) - - intel2 (second additional repo in config) - - ... (subsequent repos in config order) +1. Gather all matching candidates across configured repositories. +2. Apply repository `priority` rules. +3. Apply version constraints from dependency metadata. +4. Select candidate with best priority/constraint fit; use repository affinity + for transitive dependencies when available. ### Conflict Resolution Examples #### Example 1: Different versions across repositories -- Base repo contains: `curl-7.68.0` -- intel1 repo contains: `curl-8.0.1` -- **Result**: The tool selects `curl-8.0.1` from intel1 (latest version rule). +- repoA (`priority: 1001`) contains: `curl-7.68.0` +- repoB (`priority: 500`) contains: `curl-8.0.1` +- **Result**: repoA candidate is preferred because repository priority is + higher. #### Example 2: Same version in multiple repositories -- Base repo contains: `mypackage-1.0.0` -- intel1 repo contains: `mypackage-1.0.0` -- intel2 repo contains: `mypackage-1.0.0` -- **Result**: The tool selects `mypackage-1.0.0` from base repo (repository - order priority). +- repoA (`priority: 900`) contains: `mypackage-1.0.0` +- repoB (`priority: 900`) contains: `mypackage-1.0.0` +- **Result**: resolver uses tie-breakers (version/equivalent candidate and + repository affinity when resolving dependencies). #### Example 3: Mixed scenario -- intel1 repo contains: `testpackage-2.0.0` -- intel2 repo contains: `testpackage-1.5.0` -- **Result**: The tool selects `testpackage-2.0.0` from intel1 (latest version - rule). +- repoA (`priority: -1`) contains: `testpackage-2.0.0` +- repoB (`priority: 500`) contains: `testpackage-1.5.0` +- **Result**: repoA package is blocked due to negative priority; repoB package + is selected. + +These priority semantics provide explicit control for pinning, preference, and +blocking behavior in multi-repo builds. + +## AllowPackages White List + +`allowPackages` is an optional per-repository white list in +`packageRepositories`. + +- If omitted or empty, all packages from that repository metadata are eligible. +- If provided, only matching package names are indexed from that repository. +- Matching supports: + - exact names (for example `spice-server`) + - prefix/version pin patterns (for example `kernel-6.17.11`) + - glob patterns (for example `libva*`, `wayland*`) -This simplified priority system ensures you always get the most recent package -versions while maintaining predictable behavior for identical versions. +This filtering happens during repository metadata parsing, before package +matching and dependency resolution. It is useful for tightening repository +scope and reducing accidental package selection. ### Dependencies Package diff --git a/docs/architecture/os-image-composer-templates.md b/docs/architecture/os-image-composer-templates.md index 91b27a37..81fb7295 100644 --- a/docs/architecture/os-image-composer-templates.md +++ b/docs/architecture/os-image-composer-templates.md @@ -13,6 +13,10 @@ image-creation workflow. - [Using Templates to Build Images](#using-templates-to-build-images) - [Template Storage](#template-storage) - [Template Variables](#template-variables) +- [Package Repositories](#package-repositories) + - [Repository Fields](#repository-fields) + - [Priority Behavior](#priority-behavior) + - [AllowPackages White List](#allowpackages-white-list) - [Best Practices](#best-practices) - [Template Organization](#template-organization) - [Template Design](#template-design) @@ -92,6 +96,22 @@ systemConfig: kernel: version: "6.12" cmdline: "console=ttyS0,115200 console=tty0 loglevel=7" + +# Optional additional repositories +packageRepositories: + - codename: emtNext + url: https://example.com/rpms/next/base + pkey: https://example.com/RPM-GPG-KEY + priority: 1001 + allowPackages: + - kernel-6.17.11 + - kernel-drivers-gpu-6.17.11 + - libva* + + - codename: edgeai + url: https://example2.com/edgeai/ + pkey: https://example2.com/edgeai/GPG-PUB-KEY.gpg + priority: 500 ``` To learn about patterns that work well as templates, see @@ -141,6 +161,48 @@ For details on customizations that you can apply, see the [Configuration Stage](./os-image-composer-build-process.md#4-configuration-stage) of the build process. +## Package Repositories + +Use `packageRepositories` to add extra Debian or RPM repositories to a build. +Each entry defines where to fetch package metadata and how candidates are +selected when the same package exists in multiple repositories. + +### Repository Fields + +- `codename`: repository identifier. +- `url`: repository base URL. +- `pkey`: GPG key URL (or trusted marker for supported Debian flows). +- `priority`: numeric repository preference used in conflict resolution. +- `allowPackages`: optional package white list for metadata filtering. + +### Priority Behavior + +`priority` is evaluated during package candidate selection across repositories. + +- Higher numeric values are preferred. +- Debian resolver also supports APT-like behavior: + - `< 0`: block packages from that repository + - `990`: prefer over default repositories + - `1000`: install even if lower version + - `> 1000`: force preference + +When candidates have equivalent priority, version constraints and dependency +context determine the final package choice. + +### AllowPackages White List + +`allowPackages` limits which package names are indexed from a specific +repository. + +- If omitted or empty, all repository packages are eligible. +- If present, only matching package names are indexed. +- Supported matching modes: + - exact name (for example `spice-server`) + - prefix/version pin (for example `kernel-6.17.11`) + - glob patterns (for example `libva*`, `wayland*`) + +Filtering happens at metadata-parse time, before dependency resolution. + ## Best Practices ### Template Organization diff --git a/internal/ospackage/debutils/download.go b/internal/ospackage/debutils/download.go index e1cbb348..9ac033b0 100644 --- a/internal/ospackage/debutils/download.go +++ b/internal/ospackage/debutils/download.go @@ -23,29 +23,31 @@ import ( // Repository represents a Debian repository type Repository struct { - ID string - Codename string - URL string - PKey string - Component string - Priority int + ID string + Codename string + URL string + PKey string + Component string + Priority int + AllowPackages []string } // repoConfig hold repo related info type RepoConfig struct { - Section string // raw section header - Name string // human-readable name from name= - PkgList string - PkgPrefix string - GPGCheck bool - RepoGPGCheck bool - Enabled bool - PbGPGKey string - ReleaseFile string - ReleaseSign string - BuildPath string // path to store builds, relative to the root of the repo - Arch string // architecture, e.g., amd64, all - Priority int // repository priority (higher numbers = higher priority) + Section string // raw section header + Name string // human-readable name from name= + PkgList string + PkgPrefix string + GPGCheck bool + RepoGPGCheck bool + Enabled bool + PbGPGKey string + ReleaseFile string + ReleaseSign string + BuildPath string // path to store builds, relative to the root of the repo + Arch string // architecture, e.g., amd64, all + Priority int // repository priority (higher numbers = higher priority) + AllowPackages []string // optional package filter for this repository } type pkgChecksum struct { @@ -68,7 +70,7 @@ func Packages() ([]ospackage.PackageInfo, error) { log := logger.Logger() log.Infof("fetching packages from %s", RepoCfg.PkgList) - packages, err := ParseRepositoryMetadata(RepoCfg.PkgPrefix, GzHref, RepoCfg.ReleaseFile, RepoCfg.ReleaseSign, RepoCfg.PbGPGKey, RepoCfg.BuildPath, RepoCfg.Arch) + packages, err := ParseRepositoryMetadata(RepoCfg.PkgPrefix, GzHref, RepoCfg.ReleaseFile, RepoCfg.ReleaseSign, RepoCfg.PbGPGKey, RepoCfg.BuildPath, RepoCfg.Arch, RepoCfg.AllowPackages) if err != nil { return nil, fmt.Errorf("parsing default repo failed: %w", err) } @@ -92,7 +94,7 @@ func PackagesFromMultipleRepos() ([]ospackage.PackageInfo, error) { for i, repoCfg := range RepoCfgs { log.Infof("fetching packages from repository %d: %s (%s)", i+1, repoCfg.Name, repoCfg.PkgList) - packages, err := ParseRepositoryMetadata(repoCfg.PkgPrefix, repoCfg.PkgList, repoCfg.ReleaseFile, repoCfg.ReleaseSign, repoCfg.PbGPGKey, repoCfg.BuildPath, repoCfg.Arch) + packages, err := ParseRepositoryMetadata(repoCfg.PkgPrefix, repoCfg.PkgList, repoCfg.ReleaseFile, repoCfg.ReleaseSign, repoCfg.PbGPGKey, repoCfg.BuildPath, repoCfg.Arch, repoCfg.AllowPackages) if err != nil { log.Warnf("Failed to parse repository %s: %v", repoCfg.Name, err) failedRepos = append(failedRepos, repoCfg.Name) @@ -139,18 +141,19 @@ func BuildRepoConfigs(userRepoList []Repository, arch string) ([]RepoConfig, err } fmt.Printf("SUCCESS: baseURL %s codename %s localArch %s componentName %s\n", baseURL, codename, localArch, componentName) repo := RepoConfig{ - PkgList: package_list_url, - ReleaseFile: fmt.Sprintf("%s/dists/%s/%s", baseURL, codename, releaseNm), - ReleaseSign: fmt.Sprintf("%s/dists/%s/%s.gpg", baseURL, codename, releaseNm), - PkgPrefix: baseURL, - Name: id, - GPGCheck: true, - RepoGPGCheck: true, - Enabled: true, - PbGPGKey: pkey, - BuildPath: filepath.Join(config.TempDir(), "builds", fmt.Sprintf("%s_%s_%s", id, localArch, componentName)), - Arch: localArch, - Priority: repoItem.Priority, + PkgList: package_list_url, + ReleaseFile: fmt.Sprintf("%s/dists/%s/%s", baseURL, codename, releaseNm), + ReleaseSign: fmt.Sprintf("%s/dists/%s/%s.gpg", baseURL, codename, releaseNm), + PkgPrefix: baseURL, + Name: id, + GPGCheck: true, + RepoGPGCheck: true, + Enabled: true, + PbGPGKey: pkey, + BuildPath: filepath.Join(config.TempDir(), "builds", fmt.Sprintf("%s_%s_%s", id, localArch, componentName)), + Arch: localArch, + Priority: repoItem.Priority, + AllowPackages: repoItem.AllowPackages, } userRepo = append(userRepo, repo) connectSuccess = true @@ -179,11 +182,14 @@ func UserPackages() ([]ospackage.PackageInfo, error) { } baseURL := strings.TrimPrefix(strings.TrimPrefix(repo.URL, "http://"), "https://") repoList = append(repoList, Repository{ - ID: fmt.Sprintf("%s%d", repoGroup+"-"+baseURL, i+1), - Codename: repo.Codename, - URL: repo.URL, - PKey: repo.PKey, - Component: repo.Component, Priority: repo.Priority}) + ID: fmt.Sprintf("%s%d", repoGroup+"-"+baseURL, i+1), + Codename: repo.Codename, + URL: repo.URL, + PKey: repo.PKey, + Component: repo.Component, + Priority: repo.Priority, + AllowPackages: repo.AllowPackages, + }) } // If no valid repositories were found (all were placeholders), return empty package list @@ -199,7 +205,7 @@ func UserPackages() ([]ospackage.PackageInfo, error) { var allUserPackages []ospackage.PackageInfo for _, rpItx := range userRepo { - userPkgs, err := ParseRepositoryMetadata(rpItx.PkgPrefix, rpItx.PkgList, rpItx.ReleaseFile, rpItx.ReleaseSign, rpItx.PbGPGKey, rpItx.BuildPath, rpItx.Arch) + userPkgs, err := ParseRepositoryMetadata(rpItx.PkgPrefix, rpItx.PkgList, rpItx.ReleaseFile, rpItx.ReleaseSign, rpItx.PbGPGKey, rpItx.BuildPath, rpItx.Arch, rpItx.AllowPackages) if err != nil { return nil, fmt.Errorf("parsing user repo failed: %w", err) } diff --git a/internal/ospackage/debutils/resolver.go b/internal/ospackage/debutils/resolver.go index f2ae82f5..f8a2677a 100644 --- a/internal/ospackage/debutils/resolver.go +++ b/internal/ospackage/debutils/resolver.go @@ -6,6 +6,7 @@ import ( "io" "net/url" "os" + "path" "path/filepath" "sort" "strings" @@ -24,6 +25,34 @@ type VersionConstraint struct { Alternative string // Alternative package name for constraints like "logsave | e2fsprogs (<< 1.45.3-1~)" } +func isGlobPattern(pattern string) bool { + return strings.ContainsAny(pattern, "*?[]") +} + +func matchesPackageFilter(pkgName string, filter []string) bool { + if len(filter) == 0 { + return true + } + + for _, pattern := range filter { + if isGlobPattern(pattern) { + if ok, err := path.Match(pattern, pkgName); err == nil && ok { + return true + } + } + + if pkgName == pattern { + return true + } + + if strings.HasPrefix(pkgName, pattern+"-") || strings.HasPrefix(pkgName, pattern) { + return true + } + } + + return false +} + func GenerateDot(pkgs []ospackage.PackageInfo, file string, pkgSources map[string]config.PackageSource) error { log := logger.Logger() log.Infof("Generating DOT file %s", file) @@ -80,7 +109,7 @@ func GenerateDot(pkgs []ospackage.PackageInfo, file string, pkgSources map[strin } // ParseRepositoryMetadata parses the Packages.gz file from gzHref. -func ParseRepositoryMetadata(baseURL string, pkggz string, releaseFile string, releaseSign string, pbGPGKey string, buildPath string, arch string) ([]ospackage.PackageInfo, error) { +func ParseRepositoryMetadata(baseURL string, pkggz string, releaseFile string, releaseSign string, pbGPGKey string, buildPath string, arch string, packageFilter []string) ([]ospackage.PackageInfo, error) { log := logger.Logger() // Ensure pkgMetaDir exists, create if not @@ -196,7 +225,9 @@ func ParseRepositoryMetadata(baseURL string, pkggz string, releaseFile string, r if line == "" { // End of one package entry if pkg.Name != "" { - pkgs = append(pkgs, pkg) + if matchesPackageFilter(pkg.Name, packageFilter) { + pkgs = append(pkgs, pkg) + } pkg = ospackage.PackageInfo{} } if err == io.EOF { @@ -289,7 +320,9 @@ func ParseRepositoryMetadata(baseURL string, pkggz string, releaseFile string, r // Add the last package if file doesn't end with a blank line if pkg.Name != "" { - pkgs = append(pkgs, pkg) + if matchesPackageFilter(pkg.Name, packageFilter) { + pkgs = append(pkgs, pkg) + } } return pkgs, nil From ed66ee7ae9e70757d6758bf111dd6733ca897d6e Mon Sep 17 00:00:00 2001 From: samueltaripin <108398368+samueltaripin@users.noreply.github.com> Date: Fri, 27 Feb 2026 14:19:26 +0800 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml | 2 +- image-templates/ubuntu24-x86_64-minimal-ptl.yml | 2 +- internal/config/schema/os-image-template.schema.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml b/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml index a0d9df2d..bea291e5 100755 --- a/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml +++ b/image-templates/ubuntu24-x86_64-minimal-ptl-dls.yml @@ -364,7 +364,7 @@ systemConfig: - xnest_2:21.1.12-1ppa1~noble3 - xserver-xorg-dev_2:21.1.12-1ppa1~noble3 - xvfb_2:21.1.12-1ppa1~noble3 - # additinal packages + # additional packages - tcpdump - usb-modeswitch - fwupd diff --git a/image-templates/ubuntu24-x86_64-minimal-ptl.yml b/image-templates/ubuntu24-x86_64-minimal-ptl.yml index 8a5c770d..3612d137 100644 --- a/image-templates/ubuntu24-x86_64-minimal-ptl.yml +++ b/image-templates/ubuntu24-x86_64-minimal-ptl.yml @@ -357,7 +357,7 @@ systemConfig: - xnest_2:21.1.12-1ppa1~noble3 - xserver-xorg-dev_2:21.1.12-1ppa1~noble3 - xvfb_2:21.1.12-1ppa1~noble3 - # additinal packages + # additional packages - tcpdump - usb-modeswitch - fwupd diff --git a/internal/config/schema/os-image-template.schema.json b/internal/config/schema/os-image-template.schema.json index 78af42e4..e7e7169e 100644 --- a/internal/config/schema/os-image-template.schema.json +++ b/internal/config/schema/os-image-template.schema.json @@ -257,7 +257,7 @@ "bootloader": { "$ref": "#/$defs/Bootloader" }, "packages": { "type": "array", - "description": "List of packages to include in the system (supports glob patterns like wayland* and libva*)", + "description": "List of packages to include in the system. Supports simple glob-style patterns on package names, such as wayland* or libva-?[0-9]. Wildcards are limited to *, ?, and bracket ranges (e.g. [0-9]); patterns must match the allowed character set in this schema and are applied only to package names (versioned globs or comparison operators like pkg>=1.2 are not supported). Invalid patterns will cause template validation to fail.", "items": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9+_.:~*?\\[\\]-]*$" }, "uniqueItems": true }, From 9da9468f5512ab7702c3671b7ea3b7cd21e225d0 Mon Sep 17 00:00:00 2001 From: srmungar Date: Fri, 27 Feb 2026 12:56:25 -0700 Subject: [PATCH 5/5] separting the docs from the code for easier updates --- .../os-image-composer-multi-repo-support.md | 119 +++++++----------- .../os-image-composer-templates.md | 62 --------- 2 files changed, 44 insertions(+), 137 deletions(-) diff --git a/docs/architecture/os-image-composer-multi-repo-support.md b/docs/architecture/os-image-composer-multi-repo-support.md index 4bf35cfc..b1140324 100644 --- a/docs/architecture/os-image-composer-multi-repo-support.md +++ b/docs/architecture/os-image-composer-multi-repo-support.md @@ -11,10 +11,8 @@ components. This document describes the key aspects of the multiple package repo repositories, enabling the OS Image Composer tool to access and pull custom packages that are unavailable in the base repository. - **Package conflict priority consideration**: Outlines how the tool determines - which package to use when duplicates exist across repositories using - per-repository `priority`. -- **Allow-list support**: Describes `allowPackages` (white list) to restrict - which packages are indexed from a repository. + which package to use when duplicates exist across repositories, prioritizing + the user-specified order and base repository integrity. - **Architectural design**: Describes how the design extends the tool's package and dependency pre-download framework to resolve dependencies without relying on package managers like APT or @@ -40,7 +38,6 @@ components. This document describes the key aspects of the multiple package repo - [Example 4: Unresolvable dependency](#example-4-unresolvable-dependency) - [Example 5: Conflicting dependency versions](#example-5-conflicting-dependency-versions) - [Benefits of Repository Affinity](#benefits-of-repository-affinity) -- [AllowPackages White List](#allowpackages-white-list) - [Architectural Design](#architectural-design) - [Single Repository Support](#single-repository-support) - [Multiple Repositories Support](#multiple-repositories-support) @@ -53,27 +50,16 @@ You can specify additional package repositories in the OS Image Composer user template. Here's an example: ```yaml -packageRepositories: - - codename: "repoA" - url: "https://repo.example.com/os/base" - pkey: "https://repo.example.com/keys/RPM-GPG-KEY" - priority: 1001 - allowPackages: - - kernel-6.17.11 - - kernel-drivers-gpu-6.17.11 - - libva* - - wayland* - - - codename: "repoB" - url: "https://repo2.example.com/os/extra" - pkey: "https://repo2.example.com/keys/RPM-GPG-KEY" - priority: 500 - -systemConfig: - packages: - - kernel-6.17.11 - - libva-utils - - my-custom-package +... +additionalrepo: + intel1: "https://www.intel.com/repo1" # Add new package repo URL + intel2: "https://www.intel.com/repo2" # Add another new package repo URL + +packages: + - intelpackage01 # Package from intel1 repo + - intelpackage02 # Package from intel2 repo + - systemd-boot # Package from base repo +... ``` Each repository must follow the standard Debian or RPM structure, including all @@ -87,78 +73,61 @@ setup references: ## Package Conflict Priority Consideration When multiple repositories contain packages with the same name, the OS Image -Composer tool uses repository `priority` to select candidates. +Composer tool uses a simple two-rule priority system for package selection. ### Priority Rules -The OS Image Composer tool follows these rules: +The OS Image Composer tool follows these straightforward rules to resolve +package conflicts: -1. **Higher numeric priority wins**: repositories with higher `priority` - values are preferred. -2. **Version tie-breaker**: when candidates are in the same priority class, - the resolver picks the most suitable version (usually the newest one that - satisfies constraints). -3. **Repository affinity for dependencies**: when possible, dependencies are - chosen from the same repository family as the parent package. +1. **Version Priority**: If the same package exists in multiple repositories + with different versions, the tool always selects the package with the latest + version number, regardless of which repository contains it. -Debian resolver supports additional APT-like priority behavior: +2. **Repository Order Priority**: If the same package exists in multiple + repositories with identical versions, the OS Image Composer tool follows + the following priority order: -- `priority < 0`: block packages from that repository -- `priority = 990`: prefer over default repos -- `priority = 1000`: install even if version is lower -- `priority > 1000`: force preference + - Base OS repository (highest priority) + - Additional repositories in the order they appear in configuration ### Resolution Process Decision Flow: -1. Gather all matching candidates across configured repositories. -2. Apply repository `priority` rules. -3. Apply version constraints from dependency metadata. -4. Select candidate with best priority/constraint fit; use repository affinity - for transitive dependencies when available. +1. Check if package versions differ → Select latest version +2. If versions are identical → Follow repository priority order: + + - Base OS repository + - intel1 (first additional repo in config) + - intel2 (second additional repo in config) + - ... (subsequent repos in config order) ### Conflict Resolution Examples #### Example 1: Different versions across repositories -- repoA (`priority: 1001`) contains: `curl-7.68.0` -- repoB (`priority: 500`) contains: `curl-8.0.1` -- **Result**: repoA candidate is preferred because repository priority is - higher. +- Base repo contains: `curl-7.68.0` +- intel1 repo contains: `curl-8.0.1` +- **Result**: The tool selects `curl-8.0.1` from intel1 (latest version rule). #### Example 2: Same version in multiple repositories -- repoA (`priority: 900`) contains: `mypackage-1.0.0` -- repoB (`priority: 900`) contains: `mypackage-1.0.0` -- **Result**: resolver uses tie-breakers (version/equivalent candidate and - repository affinity when resolving dependencies). +- Base repo contains: `mypackage-1.0.0` +- intel1 repo contains: `mypackage-1.0.0` +- intel2 repo contains: `mypackage-1.0.0` +- **Result**: The tool selects `mypackage-1.0.0` from base repo (repository + order priority). #### Example 3: Mixed scenario -- repoA (`priority: -1`) contains: `testpackage-2.0.0` -- repoB (`priority: 500`) contains: `testpackage-1.5.0` -- **Result**: repoA package is blocked due to negative priority; repoB package - is selected. - -These priority semantics provide explicit control for pinning, preference, and -blocking behavior in multi-repo builds. - -## AllowPackages White List - -`allowPackages` is an optional per-repository white list in -`packageRepositories`. - -- If omitted or empty, all packages from that repository metadata are eligible. -- If provided, only matching package names are indexed from that repository. -- Matching supports: - - exact names (for example `spice-server`) - - prefix/version pin patterns (for example `kernel-6.17.11`) - - glob patterns (for example `libva*`, `wayland*`) +- intel1 repo contains: `testpackage-2.0.0` +- intel2 repo contains: `testpackage-1.5.0` +- **Result**: The tool selects `testpackage-2.0.0` from intel1 (latest version + rule). -This filtering happens during repository metadata parsing, before package -matching and dependency resolution. It is useful for tightening repository -scope and reducing accidental package selection. +This simplified priority system ensures you always get the most recent package +versions while maintaining predictable behavior for identical versions. ### Dependencies Package diff --git a/docs/architecture/os-image-composer-templates.md b/docs/architecture/os-image-composer-templates.md index 4355a0ef..f34462eb 100644 --- a/docs/architecture/os-image-composer-templates.md +++ b/docs/architecture/os-image-composer-templates.md @@ -13,10 +13,6 @@ image-creation workflow. - [Using Templates to Build Images](#using-templates-to-build-images) - [Template Storage](#template-storage) - [Template Variables](#template-variables) -- [Package Repositories](#package-repositories) - - [Repository Fields](#repository-fields) - - [Priority Behavior](#priority-behavior) - - [AllowPackages White List](#allowpackages-white-list) - [Best Practices](#best-practices) - [Template Organization](#template-organization) - [Template Design](#template-design) @@ -96,22 +92,6 @@ systemConfig: kernel: version: "6.12" cmdline: "console=ttyS0,115200 console=tty0 loglevel=7" - -# Optional additional repositories -packageRepositories: - - codename: emtNext - url: https://example.com/rpms/next/base - pkey: https://example.com/RPM-GPG-KEY - priority: 1001 - allowPackages: - - kernel-6.17.11 - - kernel-drivers-gpu-6.17.11 - - libva* - - - codename: edgeai - url: https://example2.com/edgeai/ - pkey: https://example2.com/edgeai/GPG-PUB-KEY.gpg - priority: 500 ``` To learn about patterns that work well as templates, see @@ -161,48 +141,6 @@ For details on customizations that you can apply, see [Build Stages in Detail](./os-image-composer-build-process.md#build-stages-in-detail) in the build process documentation. -## Package Repositories - -Use `packageRepositories` to add extra Debian or RPM repositories to a build. -Each entry defines where to fetch package metadata and how candidates are -selected when the same package exists in multiple repositories. - -### Repository Fields - -- `codename`: repository identifier. -- `url`: repository base URL. -- `pkey`: GPG key URL (or trusted marker for supported Debian flows). -- `priority`: numeric repository preference used in conflict resolution. -- `allowPackages`: optional package white list for metadata filtering. - -### Priority Behavior - -`priority` is evaluated during package candidate selection across repositories. - -- Higher numeric values are preferred. -- Debian resolver also supports APT-like behavior: - - `< 0`: block packages from that repository - - `990`: prefer over default repositories - - `1000`: install even if lower version - - `> 1000`: force preference - -When candidates have equivalent priority, version constraints and dependency -context determine the final package choice. - -### AllowPackages White List - -`allowPackages` limits which package names are indexed from a specific -repository. - -- If omitted or empty, all repository packages are eligible. -- If present, only matching package names are indexed. -- Supported matching modes: - - exact name (for example `spice-server`) - - prefix/version pin (for example `kernel-6.17.11`) - - glob patterns (for example `libva*`, `wayland*`) - -Filtering happens at metadata-parse time, before dependency resolution. - ## Best Practices ### Template Organization