From 56b4ec961323c95f35ed1f35c4e8eb0726374a38 Mon Sep 17 00:00:00 2001 From: krolmiki2011 Date: Wed, 26 Aug 2026 17:56:56 +0200 Subject: [PATCH 1/3] no-rpm-anymore: consolidate changes Signed-off-by: krolmiki2011 --- .cci.jenkinsfile | 1 - Cargo.lock | 8 +- Cargo.toml | 3 +- Makefile | 11 +- README-devel.md | 24 ++- contrib/packaging/bootupd.spec | 1 + packagesystem/query-file-owner-apk | 21 +++ packagesystem/query-file-owner-dpkg | 20 +++ packagesystem/query-file-owner-pacman | 20 +++ packagesystem/query-file-owner-rpm | 20 +++ src/efi.rs | 5 +- src/ostreeutil.rs | 50 ------ src/packagesystem.rs | 225 +++++++++++--------------- tests/e2e-update/e2e-update-in-vm.sh | 8 +- tests/e2e-update/e2e-update.sh | 1 + tests/kola/test-bootupd | 12 +- 16 files changed, 222 insertions(+), 208 deletions(-) create mode 100755 packagesystem/query-file-owner-apk create mode 100755 packagesystem/query-file-owner-dpkg create mode 100755 packagesystem/query-file-owner-pacman create mode 100755 packagesystem/query-file-owner-rpm diff --git a/.cci.jenkinsfile b/.cci.jenkinsfile index 6407d948..f92d2b30 100644 --- a/.cci.jenkinsfile +++ b/.cci.jenkinsfile @@ -17,7 +17,6 @@ parallel build: { } stage("Unit tests") { shwrap(""" - cargo test --features rpm cargo test """) } diff --git a/Cargo.lock b/Cargo.lock index 91d671da..a3720680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,7 +185,7 @@ dependencies = [ "log", "openssl", "os-release", - "rpm-version", + "regex", "rustix", "serde", "serde_json", @@ -1045,12 +1045,6 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" -[[package]] -name = "rpm-version" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56568fb1bf1d2c0a640aac216b3d84d65e210ca1997df922bf124f2bfaf9efd9" - [[package]] name = "rustix" version = "1.1.4" diff --git a/Cargo.toml b/Cargo.toml index e3ccd79e..aca4fb43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,6 @@ log = "^0.4" cap-std = "4.0.2" openssl = "^0.10" os-release = "0.1.0" -rpm-version = { version = "0.5.0", default-features = false, optional = true } rustix = { version = "1.1.4", features = ["process", "fs"] } serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" @@ -60,4 +59,4 @@ tag-message = "bootupd {{version}}" [features] default = [] -rpm = ["rpm-version"] +rpm = [] diff --git a/Makefile b/Makefile index 76d52e02..619c4a36 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ RELEASE ?= 1 CONTAINER_RUNTIME ?= podman IMAGE_PREFIX ?= IMAGE_NAME ?= bootupd-build +PACKAGESYSTEM ?= rpm ifeq ($(RELEASE),1) PROFILE ?= release @@ -24,10 +25,16 @@ all: ln -f target/${PROFILE}/bootupd target/${PROFILE}/bootupctl .PHONY: install -install: +install: query-file-$(PACKAGESYSTEM) mkdir -p "${DESTDIR}$(PREFIX)/bin" "${DESTDIR}$(LIBEXECDIR)" install -D -t "${DESTDIR}$(LIBEXECDIR)" target/${PROFILE}/bootupd - ln -f ${DESTDIR}$(LIBEXECDIR)/bootupd ${DESTDIR}$(PREFIX)/bin/bootupctl + ln -f "${DESTDIR}$(LIBEXECDIR)/bootupd" "${DESTDIR}$(PREFIX)/bin/bootupctl" + +.PHONY: query-file-$(PACKAGESYSTEM) +query-file-$(PACKAGESYSTEM): + install -D -m 755 \ + "packagesystem/query-file-owner-$(PACKAGESYSTEM)" \ + "${DESTDIR}$(PREFIX)/lib/bootupd/packagesystem/query-file-owner" .PHONY: install-grub-static install-grub-static: diff --git a/README-devel.md b/README-devel.md index 7ea45891..098ebd7c 100644 --- a/README-devel.md +++ b/README-devel.md @@ -21,13 +21,31 @@ There's a reference [Dockerfile](Dockerfile) that builds on [CentOS Stream bootc ## Integrating bootupd into a distribution/OS -Today, bootupd only really works on systems that use RPMs and ostree. -(Which usually means rpm-ostree, but not strictly necessarily) +Today, bootupd only really works on systems that use ostree. Many bootupd developers (and current CI flows) target Fedora CoreOS and derivatives, so it can be used as a "reference" for integration. -There's two parts to integration: +There's three parts to integration: + +### Modifying query-file-owner + +This script provides package ownership detection for `packagesystem.rs`, there are already done scripts for the package systems in `packagesystem/query-file-owner-*`, +in the file filesystem there's only one script for one package system in `usr/lib/bootupd/packagesystem/query-file-owner` in the sysroot. + +Now you need to modify this file to work with your package manager, ex. `deb/pacman/apk/rpm` + +You can test it like this: +`query-file-owner [FILE] [FILE...]` + +- Make sure it returns a list of packages info and it should look like this: + `grub2-efi-x64 1:2.06-95.fc38 + shim-x64 15.6-2` (one package per line) + + So, the output of package looks like this: + `NAME VERSION` + +- You can use already done query-file-owner using for ex.: `make install-all PACKAGESYSTEM=rpm` the supported package systems are: `deb`, `rpm`, `pacman` and `apk` ### Generating an update payload diff --git a/contrib/packaging/bootupd.spec b/contrib/packaging/bootupd.spec index 9f427719..2cedf0eb 100644 --- a/contrib/packaging/bootupd.spec +++ b/contrib/packaging/bootupd.spec @@ -54,6 +54,7 @@ Conflicts: bootc < 1.14.1 %{_bindir}/bootupctl %{_libexecdir}/bootupd %{_prefix}/lib/bootupd/grub2-static/ +%{_prefix}/lib/bootupd/packagesystem/query-file-owner %{_unitdir}/bootloader-update.service %prep diff --git a/packagesystem/query-file-owner-apk b/packagesystem/query-file-owner-apk new file mode 100755 index 00000000..dae16e84 --- /dev/null +++ b/packagesystem/query-file-owner-apk @@ -0,0 +1,21 @@ +#!/usr/bin/bash +set -euo pipefail + +# Script to detect package ownership of a file in a Linux system. +# Required environment setup for consistent output +export LC_ALL=C +export LANG=C + +# Usage: ./query-file-owner [FILE] [FILE...] +if [ "$#" -eq 0 ]; then + echo "Usage: $0 [FILE] [FILE...]" + exit 1 +fi + +for FILE in "$@"; do + # Use your package manager to find the package that owns the file + # Package Manager should return two space-separated values: NAME and VERSION + apk info -W "$FILE" | sed 's/.*is owned by //' | \ + sed -E 's/-([0-9])/\ \1/' +done + diff --git a/packagesystem/query-file-owner-dpkg b/packagesystem/query-file-owner-dpkg new file mode 100755 index 00000000..22f4c904 --- /dev/null +++ b/packagesystem/query-file-owner-dpkg @@ -0,0 +1,20 @@ +#!/usr/bin/bash +set -euo pipefail + +# Script to detect package ownership of a file in a Linux system. +# Required environment setup for consistent output +export LC_ALL=C +export LANG=C + +# Usage: ./query-file-owner [FILE] [FILE...] +if [ "$#" -eq 0 ]; then + echo "Usage: $0 [FILE] [FILE...]" + exit 1 +fi + +for FILE in "$@"; do + # Use your package manager to find the package that owns the file + # Package Manager should return two space-separated values: NAME and VERSION + dpkg-query -W -f '${Package} ${Version}\n' "$(dpkg -S "$FILE" | cut -d: -f1)" +done + diff --git a/packagesystem/query-file-owner-pacman b/packagesystem/query-file-owner-pacman new file mode 100755 index 00000000..f4daa78c --- /dev/null +++ b/packagesystem/query-file-owner-pacman @@ -0,0 +1,20 @@ +#!/usr/bin/bash +set -euo pipefail + +# Script to detect package ownership of a file in a Linux system. +# Required environment setup for consistent output +export LC_ALL=C +export LANG=C + +# Usage: ./query-file-owner [FILE] [FILE...] +if [ "$#" -eq 0 ]; then + echo "Usage: $0 [FILE] [FILE...]" + exit 1 +fi + +for FILE in "$@"; do + # Use your package manager to find the package that owns the file + # Package Manager should return two space-separated values: NAME and VERSION + pacman -Q "$(pacman -Qoq "$FILE")" +done + diff --git a/packagesystem/query-file-owner-rpm b/packagesystem/query-file-owner-rpm new file mode 100755 index 00000000..2545807d --- /dev/null +++ b/packagesystem/query-file-owner-rpm @@ -0,0 +1,20 @@ +#!/usr/bin/bash +set -euo pipefail + +# Script to detect package ownership of a file in a Linux system. +# Required environment setup for consistent output +export LC_ALL=C +export LANG=C + +# Usage: ./query-file-owner [FILE] [FILE...] +if [ "$#" -eq 0 ]; then + echo "Usage: $0 [FILE] [FILE...]" + exit 1 +fi + +for FILE in "$@"; do + # Use your package manager to find the package that owns the file + # Package Manager should return two space-separated values: NAME and VERSION + rpm -q --qf '%{NAME} %{EVR}\n' -f "$FILE" +done + diff --git a/src/efi.rs b/src/efi.rs index 21750435..c7b77b73 100644 --- a/src/efi.rs +++ b/src/efi.rs @@ -31,7 +31,8 @@ use crate::bootupd::RootContext; use crate::freezethaw::fsfreeze_thaw_cycle; use crate::model::*; use crate::ostreeutil; -use crate::util::{self, get_metadata_timestamp}; +use crate::util; +use crate::util::get_metadata_timestamp; use crate::{component::*, packagesystem::*}; use crate::{filetree, grubconfigs}; @@ -902,7 +903,7 @@ fn generate_meta_from_usr_efi(sysroot_path: &Utf8Path) -> Result = vec![]; for efi in efi_components { - packages.push(format!("{}-{}", efi.name, efi.version)); + packages.push(format!("{} {}", efi.name, efi.version)); modules_vec.push(Module { name: efi.name, rpm_evr: efi.version, diff --git a/src/ostreeutil.rs b/src/ostreeutil.rs index bc53a3a3..a53c8b90 100644 --- a/src/ostreeutil.rs +++ b/src/ostreeutil.rs @@ -4,10 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::path::Path; - use anyhow::{Context, Result}; -use log::debug; /// https://github.com/coreos/rpm-ostree/pull/969/commits/dc0e8db5bd92e1f478a0763d1a02b48e57022b59 #[cfg(any( @@ -16,53 +13,6 @@ use log::debug; target_arch = "riscv64" ))] pub(crate) const BOOT_PREFIX: &str = "usr/lib/ostree-boot"; -const LEGACY_RPMOSTREE_DBPATH: &str = "usr/share/rpm"; -const SYSIMAGE_RPM_DBPATH: &str = "usr/lib/sysimage/rpm"; - -/// Returns true if the target directory contains at least one file that does -/// not start with `.` -fn is_nonempty_dir(path: impl AsRef) -> Result { - let path = path.as_ref(); - let it = match std::fs::read_dir(path) { - Ok(r) => r, - Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(false), - Err(e) => return Err(e.into()), - }; - for ent in it { - let ent = ent?; - let name = ent.file_name(); - if name.as_encoded_bytes().starts_with(b".") { - continue; - } - return Ok(true); - } - Ok(false) -} - -pub(crate) fn rpm_cmd>(sysroot: P) -> Result { - let mut c = std::process::Command::new("rpm"); - let sysroot = sysroot.as_ref(); - // Take the first non-empty database path - let mut arg = None; - for dbpath in [SYSIMAGE_RPM_DBPATH, LEGACY_RPMOSTREE_DBPATH] { - let dbpath = sysroot.join(dbpath); - if !is_nonempty_dir(&dbpath)? { - continue; - } - let mut s = std::ffi::OsString::new(); - s.push("--dbpath="); - s.push(dbpath.as_os_str()); - arg = Some(s); - break; - } - if let Some(arg) = arg { - debug!("Using dbpath {arg:?}"); - c.arg(arg); - } else { - debug!("Failed to find dbpath"); - } - Ok(c) -} /// Get sysroot.bootloader in ostree repo config. pub(crate) fn get_ostree_bootloader() -> Result> { diff --git a/src/packagesystem.rs b/src/packagesystem.rs index 605a3ca4..6026a4a3 100644 --- a/src/packagesystem.rs +++ b/src/packagesystem.rs @@ -1,19 +1,20 @@ use std::cmp::Ordering; -use std::collections::{BTreeMap, BTreeSet}; -use std::io::Write; +use std::collections::BTreeSet; use std::path::Path; use anyhow::{bail, Context, Result}; -use chrono::prelude::*; use serde::{Deserialize, Serialize}; use uapi_version::Version; use crate::model::*; -use crate::ostreeutil; +use crate::util::get_metadata_timestamp; + +pub(crate) const QUERY_FILE_OWNER_SCRIPT: &str = "usr/lib/bootupd/packagesystem/query-file-owner"; #[derive(Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)] pub(crate) struct Module { pub(crate) name: String, + pub(crate) rpm_evr: String, } @@ -37,42 +38,35 @@ impl PartialOrd for Module { } } -/// Parse the output of `rpm -q` -fn rpm_parse_metadata(stdout: &[u8]) -> Result { - let pkgs = std::str::from_utf8(stdout)? - .split_whitespace() - .map(|s| -> Result<_> { - let parts: Vec<_> = s.splitn(2, ',').collect(); - let name = parts[0]; - if let Some(ts) = parts.get(1) { - let nt = DateTime::parse_from_str(ts, "%s") - .context("Failed to parse rpm buildtime")? - .with_timezone(&chrono::Utc); - Ok((name, nt)) - } else { - bail!("Failed to parse: {}", s); - } - }) - .collect::>>>()?; - if pkgs.is_empty() { - bail!("Failed to find any RPM packages matching files in source efidir"); - } - let timestamps: BTreeSet<&DateTime> = pkgs.values().collect(); - // Unwrap safety: We validated pkgs has at least one value above - let largest_timestamp = timestamps.iter().last().unwrap(); - let version = pkgs.keys().fold("".to_string(), |mut s, n| { - if !s.is_empty() { - s.push(','); +/// Parse the output of the `query-file-owner` script. +/// +/// Each line contains one package with two space-separated values: NAME and VERSION. +/// The format depends on the package manager (rpm, dpkg, pacman, etc.): +fn parse_package_metadata(stdout: &[u8]) -> Result { + let output = std::str::from_utf8(stdout)?; + + let mut packages = BTreeSet::new(); + + for line in output.lines() { + let package = line.trim(); + + if package.is_empty() { + continue; } - s.push_str(n); - s - }); - // Map the version into Module struct - let mut modules_vec: Vec = pkgs.keys().map(|pkg_str| parse_evr(pkg_str)).collect(); - modules_vec.sort_unstable(); + packages.insert(package); + } + + if packages.is_empty() { + bail!("Failed to find any packages matching files"); + } + + let version = packages.iter().copied().collect::>().join(","); + + let modules_vec: Vec<_> = packages.iter().map(|pkg| parse_module(pkg)).collect(); + Ok(ContentMetadata { - timestamp: **largest_timestamp, + timestamp: get_metadata_timestamp()?, version, versions: Some(modules_vec), #[cfg(efi_arch)] @@ -80,7 +74,7 @@ fn rpm_parse_metadata(stdout: &[u8]) -> Result { }) } -/// Query the rpm database and list the package and build times. +/// Query the package owner of the given files using `query-file-owner`. pub(crate) fn query_files( sysroot_path: &str, paths: impl IntoIterator, @@ -88,69 +82,42 @@ pub(crate) fn query_files( where T: AsRef, { - let mut c = ostreeutil::rpm_cmd(sysroot_path)?; - c.args(["-q", "--queryformat", "%{nevra},%{buildtime} ", "-f"]); - for arg in paths { - c.arg(arg.as_ref()); + //Combine with sysroot + let query_files_script_path = Path::new(sysroot_path).join(QUERY_FILE_OWNER_SCRIPT); + if !query_files_script_path.exists() { + bail!( + "Query file owner script not found at {:?}", + query_files_script_path + ); } - let rpmout = c.output()?; - if !rpmout.status.success() { - std::io::stderr().write_all(&rpmout.stderr)?; - bail!("Failed to invoke rpm -qf"); + let mut cmd = std::process::Command::new(query_files_script_path); + for path in paths { + cmd.arg(path.as_ref()); } + let output = cmd.output().context("Failed to invoke query-file-owner")?; - rpm_parse_metadata(&rpmout.stdout) -} - -#[cfg(not(feature = "rpm"))] -fn split_name_version(input: &str) -> Option<(String, String)> { - // assume it is "grub2-tools-1:2.06-110.el9.x86_64" - // strip .arch - let main = input.rsplit_once('.')?.0; - - // find last two '-' - let mut parts = main.rsplitn(3, '-'); - let release = parts.next()?; // after last '-' - let version = parts.next()?; // between last two '-' - let name = parts.next()?; // the rest (may contain '-') - - Some((name.to_string(), format!("{version}-{release}"))) -} - -fn parse_evr(pkg: &str) -> Module { - // assume it is "grub2-1:2.12-28.fc42" (from usr/lib/efi) - if !pkg.ends_with(std::env::consts::ARCH) { - let (name, evr) = pkg.split_once('-').unwrap_or((pkg, "")); - return Module { - name: name.to_string(), - rpm_evr: evr.to_string(), - }; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + bail!("query-file-owner failed: {}", stderr.trim()); } - let (name_str, rpm_evr) = { - #[cfg(not(feature = "rpm"))] - { - split_name_version(pkg).unwrap() - } - #[cfg(feature = "rpm")] - { - let nevra = rpm_version::Nevra::parse(pkg); - (nevra.name().to_string(), nevra.evr().to_string()) - } - }; + parse_package_metadata(&output.stdout) +} - let (name, _) = name_str.split_once('-').unwrap_or((&name_str, "")); +fn parse_module(pkg: &str) -> Module { + // Format: "NAME VERSION" space-separated (e.g., "grub2 1:2.06-110.el9" from rpm) + let (name, evr) = pkg.split_once(' ').unwrap_or((pkg, "")); Module { name: name.to_string(), - rpm_evr, + rpm_evr: evr.to_string(), } } -fn parse_evr_vec(input: &str) -> Vec { +fn parse_module_vec(input: &str) -> Vec { let mut pkgs: Vec = input .split(',') - .map(|pkg| parse_evr(pkg)) // parse_evr returns owned Package + .map(|pkg| parse_module(pkg)) // parse_module returns owned Module .collect(); // Sort packages to ensure a consistent order for comparison, which is // required by `compare_package_slices`. @@ -196,8 +163,8 @@ pub(crate) fn compare_package_versions(a: &str, b: &str) -> Ordering { if a == b { return Ordering::Equal; } - let pkg_a = parse_evr_vec(a); - let pkg_b = parse_evr_vec(b); + let pkg_a = parse_module_vec(a); + let pkg_b = parse_module_vec(b); compare_package_slices(&pkg_a, &pkg_b) } @@ -206,20 +173,29 @@ mod tests { use super::*; #[test] - fn test_parse_rpmout() { - let testdata = "grub2-efi-x64-1:2.06-95.fc38.x86_64,1681321788 grub2-efi-x64-1:2.06-95.fc38.x86_64,1681321788 shim-x64-15.6-2.x86_64,1657222566 shim-x64-15.6-2.x86_64,1657222566 shim-x64-15.6-2.x86_64,1657222566"; - let parsed = rpm_parse_metadata(testdata.as_bytes()).unwrap(); + fn test_parse_package_metadata() { + let testdata = "\ + grub2-efi-x64 1:2.06-95.fc38 + grub2-efi-x64 1:2.06-95.fc38 + shim-x64 15.6-2 + shim-x64 15.6-2 + shim-x64 15.6-2 + "; + + let parsed = parse_package_metadata(testdata.as_bytes()).unwrap(); + assert_eq!( parsed.version, - "grub2-efi-x64-1:2.06-95.fc38.x86_64,shim-x64-15.6-2.x86_64" + "grub2-efi-x64 1:2.06-95.fc38,shim-x64 15.6-2" ); + let expected_modules = vec![ Module { - name: "grub2".to_string(), + name: "grub2-efi-x64".to_string(), rpm_evr: "1:2.06-95.fc38".to_string(), }, Module { - name: "shim".to_string(), + name: "shim-x64".to_string(), rpm_evr: "15.6-2".to_string(), }, ]; @@ -261,33 +237,27 @@ mod tests { #[test] fn test_compare_package_versions() { - let current = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-efi-x64-1:2.12-29.fc42.x86_64,shim-x64-15.8-3.x86_64"; + // Test 1: Same packages, different versions + let current = "grub2-efi-x64 1:2.12-28.fc42,shim-x64 15.8-3"; + let target = "grub2-efi-x64 1:2.12-29.fc42,shim-x64 15.8-3"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); // current < target let ord = compare_package_versions(target, current); assert_eq!(ord, Ordering::Greater); - let current = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-1:2.12-29.fc42,shim-15.8-3"; + // Test 2: Different package names but same version comparison logic + let current = "grub2 1:2.12-28.fc42,shim 15.8-3"; + let target = "grub2 1:2.12-28.fc42,shim 15.8-4"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); // current < target let ord = compare_package_versions(target, current); assert_eq!(ord, Ordering::Greater); - let current = "grub2-1:2.12-28.fc42,shim-15.8-3"; - let target = "grub2-1:2.12-28.fc42,shim-15.8-4"; - let ord = compare_package_versions(current, target); - assert_eq!(ord, Ordering::Less); // current < target - - let ord = compare_package_versions(target, current); - assert_eq!(ord, Ordering::Greater); - - // The target includes new package, should upgrade - let current = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64,test"; + // Test 3: Target includes new package, should upgrade + let current = "grub2-efi-x64 1:2.12-28.fc42,shim-x64 15.8-3"; + let target = "grub2-efi-x64 1:2.12-28.fc42,shim-x64 15.8-3,test 1.0"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); @@ -295,13 +265,10 @@ mod tests { let ord = compare_package_versions(target, current); assert_eq!(ord, Ordering::Greater); - // Not sure if this would happen - // current_grub2 > target_grub2 - // current_shim < target_shim - // In this case there is Ordering::Less, return Ordering::Less + // Test 4: Mixed comparison (different ordering) { - let current = "grub2-1:2.12-28.fc42,shim-15.8-3"; - let target = "grub2-1:2.12-27.fc42,shim-15.8-4"; + let current = "grub2 1:2.12-28.fc42,shim 15.8-3"; + let target = "grub2 1:2.12-27.fc42,shim 15.8-4"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); @@ -309,35 +276,31 @@ mod tests { assert_eq!(ord, Ordering::Less); } - // Test Equal + // Test 5: Equal versions { - let current = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let ord = compare_package_versions(current, target); - assert_eq!(ord, Ordering::Equal); - - let current = "grub2-efi-x64-1:2.12-28.fc42.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-1:2.12-28.fc42,shim-15.8-3"; + let current = "grub2-efi-x64 1:2.12-28.fc42,shim-x64 15.8-3"; + let target = "grub2-efi-x64 1:2.12-28.fc42,shim-x64 15.8-3"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Equal); - let current = "grub2-1:2.12-28.fc42,shim-15.8-3"; - let target = "grub2-1:2.12-28.fc42,shim-15.8-3"; + let current = "grub2 1:2.12-28.fc42,shim 15.8-3"; + let target = "grub2 1:2.12-28.fc42,shim 15.8-3"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Equal); } - // Test only grub2 - let current = "grub2-tools-1:2.06-86.el9_4.3.x86_64"; - let target = "grub2-tools-1:2.06-110.el9.x86_64"; + // Test 6: Single package comparison + let current = "grub2-tools 1:2.06-86.el9_4.3"; + let target = "grub2-tools 1:2.06-110.el9"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); let ord = compare_package_versions(target, current); assert_eq!(ord, Ordering::Greater); - let current = "grub2-efi-ia32-1:2.12-21.fc41.x86_64,grub2-efi-x64-1:2.12-21.fc41.x86_64,shim-ia32-15.8-3.x86_64,shim-x64-15.8-3.x86_64"; - let target = "grub2-1:2.12-28.fc42,shim-15.8-3"; + // Test 7: Multiple packages with different names + let current = "grub2-efi-ia32 1:2.12-21.fc41,grub2-efi-x64 1:2.12-21.fc41,shim-ia32 15.8-3,shim-x64 15.8-3"; + let target = "grub2-efi-ia32 1:2.12-28.fc42,grub2-efi-x64 1:2.12-28.fc42,shim-ia32 15.8-3,shim-x64 15.8-3"; let ord = compare_package_versions(current, target); assert_eq!(ord, Ordering::Less); diff --git a/tests/e2e-update/e2e-update-in-vm.sh b/tests/e2e-update/e2e-update-in-vm.sh index 008ed2b8..f343f53b 100755 --- a/tests/e2e-update/e2e-update-in-vm.sh +++ b/tests/e2e-update/e2e-update-in-vm.sh @@ -55,11 +55,11 @@ bootupctl status | tee out.txt assert_file_has_content_literal out.txt 'Component EFI' if [[ -d /usr/lib/efi ]]; then - assert_file_has_content_literal out.txt ' Installed: grub2-1:' + assert_file_has_content_literal out.txt ' Installed: grub2 1:' assert_not_file_has_content out.txt ' Installed:.*test_bootupd_payload' assert_not_file_has_content out.txt ' Installed:.*'"${TARGET_GRUB_EVR}" assert_file_has_content out.txt 'Update: Available:.*'"${TARGET_GRUB_EVR}" - assert_file_has_content out.txt 'Update: Available:.*test_bootupd_payload-1.0' + assert_file_has_content out.txt 'Update: Available:.*test_bootupd_payload 1.0' else assert_file_has_content_literal out.txt ' Installed: grub2-efi-x64-' assert_not_file_has_content out.txt ' Installed:.*test_bootupd_payload' @@ -84,7 +84,7 @@ assert_file_has_content err.txt "error: .*synthetic failpoint" bootupctl update -vvv | tee out.txt assert_file_has_content out.txt "Previous EFI: .*" -assert_file_has_content out.txt "Updated EFI: .*${TARGET_GRUB_EVR}.*,test_bootupd_payload-1.0" +assert_file_has_content out.txt "Updated EFI: .*${TARGET_GRUB_EVR}.*test_bootupd_payload 1.0" assert_file_has_content ${tmpefimount}/EFI/fedora/test-bootupd.efi test-payload @@ -110,7 +110,7 @@ bootupctl adopt-and-update | tee out.txt assert_file_has_content out.txt "Adopted and updated: BIOS: .*" assert_file_has_content out.txt "Adopted and updated: EFI: .*" if bootupctl validate 2>err.txt; then - fatal "unexpectedly passed validation" + fatal "unexpectedly passed validation" fi tap_finish diff --git a/tests/e2e-update/e2e-update.sh b/tests/e2e-update/e2e-update.sh index 07c6da00..9efa583c 100755 --- a/tests/e2e-update/e2e-update.sh +++ b/tests/e2e-update/e2e-update.sh @@ -93,6 +93,7 @@ systemd: enabled: true contents: | [Unit] + Before=bootloader-update.service RequiresMountsFor=/run/testtmp [Service] Type=oneshot diff --git a/tests/kola/test-bootupd b/tests/kola/test-bootupd index 31c87cf3..8a47ec24 100755 --- a/tests/kola/test-bootupd +++ b/tests/kola/test-bootupd @@ -86,7 +86,7 @@ bootupctl status > out.txt evr=$(rpm -q grub2-common --qf '%{EVR}') assert_file_has_content_literal out.txt 'Component EFI' if $efilib_exists; then - assert_file_has_content_literal out.txt ' Installed: grub2-'"${evr}" + assert_file_has_content_literal out.txt ' Installed: grub2 '"${evr}" else assert_file_has_content_literal out.txt ' Installed: grub2-efi-x64-' fi @@ -131,11 +131,11 @@ mv new.json ${bootupdir}/EFI.json bootupctl status | tee out.txt assert_file_has_content_literal out.txt 'Component EFI' if $efilib_exists; then - assert_file_has_content_literal out.txt ' Installed: grub2-'"${evr}" + assert_file_has_content_literal out.txt ' Installed: grub2 '"${evr}" else assert_file_has_content_literal out.txt ' Installed: grub2-efi-x64-' fi -assert_not_file_has_content out.txt ' Installed: grub2-.*,test' +assert_not_file_has_content out.txt ' Installed: grub2 .*,test' assert_file_has_content_literal out.txt 'Update: Available:' ok update avail @@ -143,17 +143,17 @@ bootupctl status --json > status.json jq -r '.components.EFI.installed.version' < status.json > installed.txt if $efilib_exists; then - assert_file_has_content installed.txt '^grub2-'"${evr}" + assert_file_has_content installed.txt '^grub2 '"${evr}" else assert_file_has_content installed.txt '^grub2-efi-x64' fi bootupctl update | tee out.txt -assert_file_has_content out.txt 'Updated EFI: grub2-.*,test' +assert_file_has_content out.txt 'Updated EFI: grub2 .*,test' bootupctl status > out.txt assert_file_has_content_literal out.txt 'Component EFI' -assert_file_has_content out.txt ' Installed: grub2-.*,test' +assert_file_has_content out.txt ' Installed: grub2 .*,test' assert_file_has_content_literal out.txt 'Update: At latest version' ok status after update From 7b3c5e21536455e6f384bf9315246e4a661dec82 Mon Sep 17 00:00:00 2001 From: krolmiki2011 Date: Wed, 26 Aug 2026 18:00:06 +0200 Subject: [PATCH 2/3] no-rpm-anymore: finishing touches Signed-off-by: krolmiki2011 --- Cargo.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index a3720680..ffac409d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,7 +185,6 @@ dependencies = [ "log", "openssl", "os-release", - "regex", "rustix", "serde", "serde_json", From 338b3c23d02616cfed2043de85bf8d0614b48bec Mon Sep 17 00:00:00 2001 From: krolmiki2011 Date: Thu, 27 Aug 2026 18:20:19 +0200 Subject: [PATCH 3/3] Update APK query-file-owner Signed-off-by: krolmiki2011 --- packagesystem/query-file-owner-apk | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packagesystem/query-file-owner-apk b/packagesystem/query-file-owner-apk index dae16e84..3f175d8d 100755 --- a/packagesystem/query-file-owner-apk +++ b/packagesystem/query-file-owner-apk @@ -1,5 +1,5 @@ -#!/usr/bin/bash -set -euo pipefail +#!/usr/bin/sh +set -eu # Script to detect package ownership of a file in a Linux system. # Required environment setup for consistent output @@ -15,7 +15,16 @@ fi for FILE in "$@"; do # Use your package manager to find the package that owns the file # Package Manager should return two space-separated values: NAME and VERSION - apk info -W "$FILE" | sed 's/.*is owned by //' | \ - sed -E 's/-([0-9])/\ \1/' + ABS_FILE=$(realpath "$FILE" 2>/dev/null || echo "$FILE") + + apk info -W "$ABS_FILE" 2>/dev/null | \ + sed 's/.*is owned by //' | \ + awk -F'-' '{ + # Build version from the end + ver = $(NF-1) "-" $NF; + name = $1; + for(i=2; i