Fedora – How to Replicate Installed Package Selection

dnffedorayum

I have a Fedora system (A) where I have installed some packages over the time. Now I want to install Fedora on another computer (B) and I want to install the same packages on it.

In Debian terms I want to accomplish something like this:

$ dpkg --get-selections > pkg_sel_host_a  # on host_a
$ dpkg --set-selections < pkg_sel_host_a  # on host_b

But to be honest, I really want a better method to select the same packages on the new Fedora 19 system (B): I just want to install the packages from system A that were explicitly mentioned on a dnf install (or yum install) command line – and not those that were installed as dependencies!

Why? Because perhaps dependencies have changed – and I don't want to install outdated dependencies on the new system. Plus, when I remove packages I want to remove the (possibly) then unneeded automatically installed dependencies (i.e. orphans) as well.

I've found dnf list installed – but it does not display if a package was explicitly selected or just installed because of a dependency.

How do I get that information on Fedora?

What is the Fedora/dnf way to replicate package selections?

Best Answer

Since Fedora 26, the Dnf repoquery subcommand supports has a new option for listing all user-installed packages:

$ dnf repoquery --qf '%{name}' --userinstalled \
 | grep -v -- '-debuginfo$' \
 | grep -v '^\(kernel-modules\|kernel\|kernel-core\|kernel-devel\)$' > pkgs_a.lst

In contrast to other methods, it also lists all debuginfo packages. The additional grep in the above example filters them out.

To install the list on host B:

$ < pkgs_a.lst xargs dnf -y install

Dnf API

With recent Dnf versions (e.g. Fedora >= 23), the package database can be queried for user installed package names via the Dnf Python API:

$ python3 -c 'import dnf; b = dnf.Base(); b.fill_sack(); \
  l = sorted(set(x.name for x in b.iter_userinstalled() \
       if not x.name.endswith("-debuginfo") \
          and x.name not in \
             ["kernel-modules", "kernel", "kernel-core", "kernel-devel"] )); \
  print("\n".join(l)) ' > pkgs_a.lst

# dnf install $(cat pkgs_a.lst) # on host_b

By default, dnf install aborts if one or more packages aren't available anymore. Alternatively, dnf can be forced to install all remaining ones:

# dnf install --setopt=strict=0 $(cat pkgs_a.lst) # on host_b

PS: Put the above code and more into user-installed.py that also supports other distributions.

history userinstalled

On Fedora 23 and later, Dnf provides the

# dnf history userinstalled

command that lists all user installed packages. As of 2016-11, its usefulness is limited because there is no way to control its output and it prints packages fully qualified (i.e. including version information).

userinstalled Limitations

Note that the marking of packages as user-installed has some limitations on some Fedora versions, for Fedora 23-ish era systems (from around 2015-11) the following issues are relevant):

Repoquery

On older Fedora systems, where Dnf, the Dnf API and dnf history userinstalled aren't available, one can use repoquery instead, e.g.:

$ repoquery --installed \
     --qf '%{n} | %{yumdb_info.reason} | %{yumdb_info.installed_by}' --all \
    | awk -F'|' ' $2 ~ /user/ && ($3 != 4294967295) { print $1 }'  \
    | sort -u > pkgs_a.lst

The second awk condition is used to exclude packages that were installed by the installer. The installer's user-id was apparently stored as 4294967295 - alternatively you can write something like ($3 == 0 || $3 == your-user-id).

Note that this command works on Fedora up to release 21 - but e.g. not on release 23, because the command repoquery was replaced with dnf repoquery. And dnf repoquery does not understand the %{yumdb_info.reason} tag.

Related Question