diff --git a/.github/workflows/pr_check.yml b/.github/workflows/pr_check.yml index 6c82be0..986dd54 100644 --- a/.github/workflows/pr_check.yml +++ b/.github/workflows/pr_check.yml @@ -62,6 +62,17 @@ jobs: load: true push: false tags: local/cnf-ci-zypper:latest + - + name: Build openSUSE Tumbleweed image with dnf4 + uses: docker/build-push-action@v4 + with: + cache-from: type=gha + cache-to: type=gha,mode=max + context: test/ + file: test/dnf.dockerfile + load: true + push: false + tags: local/cnf-ci-dnf:latest - name: Build openSUSE Tumbleweed image with dnf5 uses: docker/build-push-action@v4 diff --git a/README.md b/README.md index 13ca679..8675925 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ | **`fish` integration** | Included with fish 1.23.1 | Included with fish 1.23.1 | | **Disable integration** | Uninstall package | Magic variable | | **Localization** | Yes (UTF-8 only) | Yes | -| **Package Managers Queried** | Dnf5, Zypper | Zypper | +| **Package Managers Queried** | Dnf5, Dnf4, Zypper | Zypper | ## **Build** @@ -77,10 +77,10 @@ cmake ## **Integration tests** -Integration tests runs inside docker images tagged `local/cnf-ci-zypper` and `local/cnf-ci-dnf5`. these are built as a part of Github Action and can be built locally with: +Integration tests runs inside docker images tagged `local/cnf-ci-zypper`, `local/cnf-ci-dnf`, and `local/cnf-ci-dnf5`. these are built as a part of Github Action and can be built locally with: ```.sh -for pm in zypper dnf5; do +for pm in zypper dnf dnf5; do docker build -t local/cnf-ci-$pm:latest -f test/$pm.dockerfile test done ``` @@ -91,7 +91,6 @@ The testing itself is wrapped in [bats](https://github.com/bats-core/bats-core) ./test/bats/bin/bats ./test/ ``` > ```.log -> test.bats > ✓ zypper root: installed /usr/bin/rpm > ✓ zypper root: installed /usr/sbin/sysctl > ✓ zypper root: not installed xnake @@ -104,6 +103,18 @@ The testing itself is wrapped in [bats](https://github.com/bats-core/bats-core) > ✓ zypper nonroot: zsh handler: not installed cmake > ✓ zypper nonroot: fish handler: not installed cmake > ✓ zypper issue26: do not list not installable files +> ✓ dnf root: installed /usr/bin/rpm +> ✓ dnf root: installed /usr/sbin/sysctl +> ✓ dnf root: not installed xnake +> ✓ dnf root: not installed make +> ✓ dnf root: not installed cmake +> ✓ dnf nonroot: not installed cmake +> ✓ dnf nonroot: bash without handler: not installed cmake +> ✓ dnf nonroot: bash handler: not installed cmake +> ✓ dnf nonroot: zsh without handler: not installed cmake +> ✓ dnf nonroot: zsh handler: not installed cmake +> ✓ dnf nonroot: fish handler: not installed cmake +> ✓ dnf issue26: do not list not installable files > ✓ dnf5 root: installed /usr/bin/rpm > ✓ dnf5 root: installed /usr/sbin/sysctl > ✓ dnf5 root: not installed xnake @@ -117,7 +128,7 @@ The testing itself is wrapped in [bats](https://github.com/bats-core/bats-core) > ✓ dnf5 nonroot: fish handler: not installed cmake > ✓ dnf5 issue26: do not list not installable files > -> 24 tests, 0 failures +> 36 tests, 0 failures ``` Every test can be executed on a command line. The `root.sh` wrapper mounts the diff --git a/cnf.1 b/cnf.1 index a4855aa..a47f1eb 100644 --- a/cnf.1 +++ b/cnf.1 @@ -9,8 +9,8 @@ cnf \- A command not found handler for openSUSE libsolv(3) under the hood. The source of truth of information about repositories, packages and their content. .PP -This works with enabled dnf5 or zypper repostiores: the first one found in /usr/bin will be used. -Use dnf5 \-\-refresh makecache or zypper \-\-force refresh to update the metadata used. +This works with enabled dnf5, dnf, or zypper repostiores: the first one found in /usr/bin will be used. +Use dnf5 \-\-refresh makecache, dnf \-\-refresh makecache, or zypper \-\-force refresh to update the metadata used. .PP It is typically called through command-not-found mechanism of bash(1) or zsh(1) or fish(1). This is enabled via installing the cnf-bash or cnf-zsh diff --git a/src/main.rs b/src/main.rs index 462a6f8..95a633d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,12 @@ mod pool; const ZYPPER_REPO_GLOB: &str = "/etc/zypp/repos.d/*.repo"; // Default value of the reposdir configuration directory +const DNF4_REPOS_GLOBS: [&str; 3] = [ + "/etc/dnf/repos.d/*.repo", + "/etc/yum.repos.d/*.repo", + "/etc/distro.repos.d/*.repo" +]; +// Default value of the reposdir configuration directory // (Note that /etc/dnf/repos.d is patched in by the openSUSE dnf5 package) const DNF5_REPOS_GLOBS: [&str; 4] = [ "/etc/dnf/repos.d/*.repo", @@ -27,6 +33,7 @@ const DNF5_REPOS_GLOBS: [&str; 4] = [ #[derive(Clone, Copy)] pub enum PackageManager { Zypper, + Dnf4, Dnf5, } pub struct SolvInput { @@ -70,12 +77,16 @@ fn main() { } let pm = if Path::exists(Path::new("/usr/bin/dnf5")) { - // Use DNF5 if it's installed (this should probably be set via a config file, in case you have both installed but prefer zypper) + // Use DNF5 if it's installed from the openSUSE repo (since zypper is the default, someone is only likely to have dnf5 installed if they want to use it) PackageManager::Dnf5 + } else if Path::exists(Path::new("/usr/bin/dnf")) { + // Use DNF4 if it's installed, but dnf5 isn't + // (in case a dnf5 user symlinks /usr/bin/dnf to /usr/bin/dnf5, this goes after the dnf5 check) + PackageManager::Dnf4 } else if Path::exists(Path::new("/usr/bin/zypper")) { PackageManager::Zypper } else { - println!("Neither /usr/bin/dnf5 nor /usr/bin/zypper could be found."); + println!("Neither /usr/bin/dnf5, /usr/bin/dnf, norr /usr/bin/zypper could be found."); exit(127); }; @@ -142,6 +153,7 @@ fn search_in_repos<'a>( ); match pm { PackageManager::Zypper => println!(" sudo zypper install {}\n", suggested_package), + PackageManager::Dnf4 => println!(" sudo dnf install {}\n", suggested_package), PackageManager::Dnf5 => println!(" sudo dnf5 install {}\n", suggested_package), } Ok(()) @@ -151,6 +163,7 @@ fn load_repos<'a>(pm: PackageManager) -> Result, ErrorKind<'a>> { let mut repos: Vec = Vec::new(); let globs = match pm { PackageManager::Zypper => &[ZYPPER_REPO_GLOB] as &[&str], + PackageManager::Dnf4 => &DNF4_REPOS_GLOBS as &[&str], PackageManager::Dnf5 => &DNF5_REPOS_GLOBS as &[&str], }; for glob in globs { @@ -165,10 +178,16 @@ fn load_repos<'a>(pm: PackageManager) -> Result, ErrorKind<'a>> { PackageManager::Zypper => { format!("/var/cache/zypp/solv/{}/solv", info.name.replace('/', "_")) } + // This uses the default system_cachedir configuration option for dnf + // Non superusers however use cachedir option (which defaults to /var/tmp/dnf--) + // I'm choosing the system one as dnf is more likely to be run with sudo + PackageManager::Dnf4 => format!( + "/var/cache/dnf/{}.solv", + info.name.replace('/', "_") + ), - // This uses the default system_cachedir configuration open for dnf5 - // None superusers however use cachedir option (which defaults to ~/.cache/libdnf5) - // I'm choosing the system one as dnf5 is more likely to be run with sudo? + // As with dnf4, this uses the default system_cachedir value + // (The default non-superuser cachedir is ~/.cache/libdnf5) PackageManager::Dnf5 => format!( "/var/cache/libdnf5/{}-*/solv/*.solv", info.name.replace('/', "_") diff --git a/test/dnf.dockerfile b/test/dnf.dockerfile new file mode 100644 index 0000000..54b35d5 --- /dev/null +++ b/test/dnf.dockerfile @@ -0,0 +1,21 @@ +# Tumbleweed based CI image for cnf integration testing with dnf4 +FROM registry.opensuse.org/opensuse/tumbleweed + +# This is the same as dnf5.dockerfile +ADD passwd /etc/passwd +ADD group /etc/group +ADD --chown=65532:65532 nonroot /home/nonroot/whoami + +# The following is exactly the same as for dnf5, except it uses 'dnf' instead of 'dnf5' +RUN zypper refresh +# As with dnf5, dnf4 fails to work without the libcurl4 package +RUN zypper --non-interactive install --no-recommends dnf libcurl4 rpm-repos-openSUSE-Tumbleweed +RUN dnf --assumeyes remove zypper libzypp +RUN rm -rf /etc/zypp /var/cache/zypp/ /var/lib/zypp + +RUN echo excludepkgs=busybox dbus diffutils pam-config perl-base systemd groff man >> /etc/dnf/dnf.conf +RUN dnf --setopt install_weak_deps=False --assumeyes install zsh fish libsolv1 +RUN curl -O https://download.opensuse.org/repositories/GNOME:/Next/openSUSE_Factory/GNOME:Next.repo --output-dir /etc/dnf/repos.d/ +RUN dnf makecache + +WORKDIR /src diff --git a/test/test.bats b/test/test.bats index 2e6b8c1..892da0d 100644 --- a/test/test.bats +++ b/test/test.bats @@ -21,7 +21,7 @@ function make_test { bats_test_function --description "$1" -- "$name" } -for PM in zypper dnf5; do +for PM in zypper dnf dnf5; do make_test "$PM root: installed /usr/bin/rpm" <