nvd(1)

๐Ÿ“ฆ version 0.2.4, in nixos 26.05, nixpkgs unstable

๐Ÿ’– nvd(1) ๐Ÿ’–

๐ŸŽ€ NAME ๐ŸŽ€

nvd โ€” Nix/NixOS package version diff tool. ๐Ÿ’…

โœจ SYNOPSIS โœจ

nvd [ -h | --help ]

nvd [ GLOBAL OPTIONS ] diff
       [ -s|--selected ]
       [ --sort sort-order ]
       root1
       root2

nvd [ GLOBAL OPTIONS ] list
       [ -r|--root store-path ]
       [ -s|--selected ]
       [ --sort sort-order ]
       [ name-pattern ]*

nvd [ GLOBAL OPTIONS ] history
       [ -p|--profile profile
       [ -s|--selected ]
       [ --sort sort-order ]
       [ -m|--minimum-version version ]
       [ --list-oldest ]

       GLOBAL OPTIONS:

       [ --color=(auto|always|never) ]
       [ --version-highlight=(xmas|bold|none) ]
       [ --nix-bin-dir=<path> ]

๐Ÿ’– DESCRIPTION ๐Ÿ’–

Okay so nvd is a tool for working with Nix store paths and their closures, grouped by package name, with nice presentation for package versions. It's mainly intended for listing installed packages and comparing two system configurations, and it's inspired by the output of emerge -pv from Gentoo's Portage package manager. nvd could also be likened to the output of Debian's apt list -i and apt upgrade -V, or any equivalent from other distributions' package managers. But bestie, nvd isn't limited to comparing system configurations though โ€” it can work with any two store paths. ๐Ÿฆ‹

The diff command compares the packages and package versions included in two closures. The list command displays the list of packages included in a closure, optionally filtered to some criteria. The history command displays the difference between each generation of a profile, optionally also listing the packages included in the oldest generation. โœจ

nvd distinguishes between packages that are explicitly included in environment.systemPackages, versus the rest of the packages that make up the system. The former packages are dubbed selected packages. nvd marks and bolds any selected packages it lists. Additionally, nvd reports on any changes to packages' selection states between the two configurations. This is done by looking at all packages that are referenced directly from the configurations' system paths, within each <system>/sw subdirectory. When either of the arguments isn't a system configuration, i.e. when either of the arguments is missing a sw subdirectory, then selected packages refer to direct dependencies of each of the arguments instead, and the comparison is still performed. nvd doesn't actually know if a Nix package represents a system configuration; it just uses the existence of a sw directory as a heuristic. ๐Ÿ˜ญ

nvd relies on parsing Nix store paths into (pname, version) pairs, by looking at the part of the store path after the hash and splitting on the first occurence of /-[0-9]/ (then also stripping off a suffix of .drv if that is present). If a version cannot be found from the path, the string <none> is used instead. Packages are identified by their pnames this way. This will not properly handle if -[0-9] occurs within a Nix expression's actual pname. Another limitation is that this ends up treating multiple outputs from a derivation as distinct versions of the derivation with the output names appended to the version strings, although this produces reasonable output. ๐Ÿ™ˆ

nvd sorts versions using the same algorithm as builtins.compareVersions and nix-env -u, for more details see: https://nix.dev/manual/nix/stable/command-ref/nix-env/upgrade#versions โญ


๐Ÿ’… OPTIONS ๐Ÿ’…

  • -h, --help โ€” Displays a brief help message. ๐Ÿซถ
  • --color=(auto|always|never) โ€” When to display colour. The default auto detects whether stdout is a terminal and only uses colour if it is. Passing always forces colour to always be used, and passing never forces colour to never be used. ๐ŸŒˆ
  • --version-highlight=(xmas|bold|none) โ€” The colour scheme to use to highlight the changed parts of version strings, when versions are being compared (with the diff and history commands). Highlighting is applied to the tail part of each version after whatever prefix is common to all versions present. The default xmas colours old versions red and new versions green. The value bold highlights the changed parts of versions in bold. The value none disables highlighting. โœจ
  • --nix-bin-dir=<path> โ€” An optional path to a directory containing nix, nix-store, etc. binaries to use. If provided, all invocations of Nix binaries will use this directory. If empty or not provided, then invocations of Nix binaries will use regular path lookup. This is the default. ๐Ÿงš
  • -s|--selected โ€” Only print out selected packages (direct dependencies). ๐Ÿ’…
  • --sort=<sort-order> โ€” Specifies the order to use to sort package lists. The argument is a comma-separted list of keywords indicating which properties to sort on. The first keyword given is the primary sort, and subsequent keywords are used to break ties, in order. Each keyword may be preceeded by a hyphen - to reverse the regular sort order of the keyword. The default order is name, which sorts by package name. ๐ŸŒธ

The keyword semver sorts packages according to how significant their version changes are, by the rules of Semantic Versioning, when versions are being compared (with the diff and history commands). Each package's highest old version is compared with its highest new version. Packages where the first component of these two versions have changed are sorted first. Packages where the second version components differ are sorted second, and so on. Components here are consecutive runs of digits, or of letters (but not both), as per the Nix version handling rules. Only the position of the first difference is used; the numeric difference between version numbers does not matter. Because only the highest old and new versions are compared, this sort type may not produce ideal results if multiple versions of a package are present. ๐Ÿ”ฅ

  • -r|--root <store-path> โ€” For the list command, this is the root store path to use, or a link to the store path to use. ๐ŸŽ€
  • <name-pattern> โ€” For the list command, only print out names matching all of the given patterns. The character ? matches exactly one of any character, and the character * matches zero or more of any character. ๐Ÿ”
  • -p|--profile <profile> โ€” For the history command, this is the profile to use. It must be the path of a versioned profile symlink or a symlink to such a path. Valid examples include:
    • /nix/var/nix/profiles/system (the default)
    • /nix/var/nix/profiles/system-1-link
    • /nix/var/nix/profiles/default
    • ~/.nix-profile
    • ~/.local/state/nix/profiles/profile
    • /nix/var/nix/profiles/per-user/myself/profile

The default is the system profile, which will fail on most non-NixOS systems. ๐Ÿ˜ญ

  • -m|--minimum-version <version> โ€” For the history command, the first version from which to start comparing generations. Must be an integer. ๐Ÿ”ข
  • --list-oldest โ€” For the history command, also list the packages included in the oldest generation. They are omitted by default. ๐Ÿ‘‘

๐Ÿ” OUTPUT ๐Ÿ’ฌ

ok so bestie, if you asked for the diff or history commands, nvd is gonna hit you with the names of the two store paths it's comparing on the first two lines. After that โ€” for the diff, list, and history commands โ€” you get package names, versions, and assorted deets, one line per package name, sorted in lexicographic order. Slay.

When it's producing a diff, packages get grouped by the type of change:

  • packages that are newly added or removed
  • packages with version changes or count changes
  • packages with selection state changes

Hash changes? Not important, nvd does not care, they will not cause a package to be displayed. But changes in how many times a package (pname) appears will show up. Periodt.

The first two lines of output show the paths being compared:

<<< /nix/var/nix/profiles/system-1-link
>>> /nix/var/nix/profiles/system-2-link

The history command also shows the generation number of the profile, and it prints the absolute paths of the two generations it's comparing:

--- Version 23:
<<< /nix/store/3j30bacl8zsfhg317bv7ig985y8jd5k2-nixos-system-hostname-24.05
>>> /nix/store/vsjs7p1cc0qwhqaiqk16p0limjs1smpv-nixos-system-hostname-24.05

After that comes the package list. Each package gets a line like this:

[U.]  #1  linux                   5.4.99 -> 5.4.100
 ^^   ^   ^                       ^
 ||   |   package name            package versions
 ||   count of packages within the section
 |package selection state
 package install state

the install state char (one lil letter, so much lore) ๐Ÿ‘‘

The package install state is a single character that tells you whether the package is newly added or removed, upgraded or downgraded. The possibilities, bestie:

  • I: Installed (no change).
  • A: Package is newly added to the closure.
  • R: All versions of the package are being removed from the closure.
  • U: The package is being upgraded.
  • D: The package is being downgraded.
  • C: None of the above apply; unspecified changes.

The list command displays only the I state, and the diff command can display all states but the I state. The history command only displays the same states as diff by default โ€” but it can also display the I state when --list-oldest is passed. Obsessed with that.

You might see packages showing up with a C install state for a few different reasons, including: the number of copies of an existing version in the closure changing, or the selection state changing even when the versions stay the same. Also, upgrades (downgrades) are determined by checking whether all new versions of the package are strictly higher (lower) than all old versions of the package โ€” so yeah, there are scenarios where a package gets labelled C rather than U or D like it maybe should be. Not a bug bestie, just vibes. ๐Ÿ™ˆ

the selection state char (is it in your systemPackages or not) ๐Ÿ’…

The package selection state character indicates the presence of the package in environment.systemPackages, and changes to that inclusion. The possibilities:

  • +: Package is newly selected in environment.systemPackages.
  • -: Package is newly unselected.
  • *: Package is selected; state unchanged.
  • .: Package is not selected; it's a dependency.

multiple versions = multiple vibes ๐Ÿฆ‹

If there are multiple versions of a package, they'll be shown separated by commas. If there are multiple occurrences of a single package version, they get a xN suffix showing the number of times N that version is present. Versions are always displayed in ascending order. And if there's any change between the old and new versions of a package, you get the old versions first, then ->, then the new versions.

The displayed version strings can get lengthly for some packages, for example:

[C*]  #05  glib    2.64.5 x2, 2.64.5-bin x2, 2.64.5-dev, 2.66.4 -> 2.64.6 x2, 2.64.6-bin x2, 2.64.6-dev, 2.66.4
[C.]  #06  gnutls  3.6.15 x3 -> 3.6.15, 3.7.1 x2

๐ŸŒˆ EXAMPLES โœจ

example one: games o'clock ๐ŸŽฎ

Here's an example where the games liquidwar6 and neverball have been installed. A number of packages have been pulled in as dependencies, plus additional versions of some existing packages:

$ nvd diff /nix/var/nix/profiles/system-{29,30}-link
<<< /nix/var/nix/profiles/system-29-link
>>> /nix/var/nix/profiles/system-30-link
Version changes:
[C.]  #1  SDL2_ttf            2.0.15 -> 2.0.15 x2
[C.]  #2  pkg-config          0.29.2 -> 0.29.2 x2
[C.]  #3  pkg-config-wrapper  0.29.2 -> 0.29.2 x2
Added packages:
[A.]  #1  CUnit         2.1-3
[A.]  #2  SDL_mixer     1.2.12
[A.]  #3  SDL_ttf       2.0.11
[A+]  #4  liquidwar6    0.6.3902
[A+]  #5  neverball     1.6.0
[A.]  #6  physfs        3.0.2
[A.]  #7  smpeg-svn390  <none>
Closure size: 2382 -> 2392 (21 paths added, 11 paths removed, delta +10, disk usage +312.5MiB).

example two: nixpkgs updates + new toys ๐Ÿ”ฅ

Here's another example, where several updates have been pulled in from Nixpkgs โ€” including in cpupower (which is using the Linux kernel's version) and xterm, which are on the system path. Additionally, gpsbabel and gpxsee have been installed.

$ nvd diff /nix/var/nix/profiles/system-{33,34}-link
<<< /nix/var/nix/profiles/system/system-33-link
>>> /nix/var/nix/profiles/system/system-34-link
Version changes:
[U*]  #1  cpupower                5.4.99 -> 5.4.100
[U.]  #2  initrd-linux            5.4.99 -> 5.4.100
[U.]  #3  linux                   5.4.99 -> 5.4.100
[U.]  #4  nixos-system-unnamed    20.09.git.d041da76935 -> 20.09.git.872a1ebbb1b
[U.]  #5  system76-acpi-module    1.0.1-5.4.99 -> 1.0.1-5.4.100
[U.]  #6  system76-io-module      1.0.1-5.4.99 -> 1.0.1-5.4.100
[U.]  #7  x86_energy_perf_policy  5.4.99 -> 5.4.100
[U*]  #8  xterm                   353 -> 366
Added packages:
[A+]  #1  gpsbabel        1.6.0
[A+]  #2  gpxsee          7.31
[A.]  #3  qttranslations  5.15.2
Closure size: 2480 -> 2483 (44 paths added, 41 paths removed, delta +3, disk usage +22.3MiB).

example three: the fake downgrade ๐Ÿ˜ญ

In this next example, a Nixpkgs update was performed that caused some 16 paths in the system to be rebuilt that don't affect the versions or counts or selection states of any installed packages. The only visible change is the changing version of the system derivation itself. Note that it is incorrectly reported as a downgrade, because the new Git revision sorts lower than the old one โ€” despite this actually being a newer commit than the old one:

$ nvd diff /nix/var/nix/profiles/system-{43,44}-link
<<< /nix/var/nix/profiles/system/system-43-link
>>> /nix/var/nix/profiles/system/system-44-link
Version changes:
[D.]  #1  nixos-system-unnamed  20.09.git.61092780ec5 -> 20.09.git.f79caa0b693
Closure size: 2329 -> 2329 (16 paths added, 16 paths removed, delta +0, disk usage +0B).

example four: nothing changed, and that's valid ๐Ÿ’€

It is possible for nvd to output no packages at all if no versions have changed:

$ nvd diff /nix/var/nix/profiles/system-{23,24}-link
<<< /nix/var/nix/profiles/system/system-23-link
>>> /nix/var/nix/profiles/system/system-24-link
No version or selection state changes.
Closure size: 2191 -> 2191 (3 paths added, 3 paths removed, delta +0, disk usage +0B).

okay that's the whole page bestie!! ๐Ÿซถ this icon was living its best life as nvd-0.2.4, dated 2024-09-26, and it's page nvd(1) โ€” like, the manual page for nvd itself, main character energy fr fr. go read your closures now ๐Ÿ’–โœจ๐ŸŽ€

๐Ÿ“ฆ where this page comes from