mirror of
https://gitlab.com/khumba/nvd.git
synced 2024-11-13 00:19:28 +01:00
Implement separate list command for listing matching packages (closes #5).
This commit is contained in:
parent
2c2fa1fbc9
commit
95c7ab7070
3 changed files with 196 additions and 36 deletions
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
|
- Add a mandatory action argument to the CLI. The existing diff functionality
|
||||||
|
is under the `diff` command. A new `list` command is now implemented as well
|
||||||
|
(issue #5).
|
||||||
|
|
||||||
- Optimized first level dependency calculation to read depenencies from
|
- Optimized first level dependency calculation to read depenencies from
|
||||||
`nix-store` rather than walking a directory tree manually (issue #4). This
|
`nix-store` rather than walking a directory tree manually (issue #4). This
|
||||||
also fixes nvd's support for things other than simple `buildEnv`s, e.g. file
|
also fixes nvd's support for things other than simple `buildEnv`s, e.g. file
|
||||||
|
|
138
src/nvd
138
src/nvd
|
@ -19,6 +19,7 @@
|
||||||
# See the nvd(1) man page for user-facing docs.
|
# See the nvd(1) man page for user-facing docs.
|
||||||
|
|
||||||
# Package install state (by pname):
|
# Package install state (by pname):
|
||||||
|
# I: Installed (unchanged)
|
||||||
# A: Added to closure
|
# A: Added to closure
|
||||||
# R: Removed from closure
|
# R: Removed from closure
|
||||||
# U: Upgrade
|
# U: Upgrade
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
# R: Package manifest only available on the right; selected there.
|
# R: Package manifest only available on the right; selected there.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import fnmatch
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
@ -46,24 +48,7 @@ from pathlib import Path
|
||||||
from subprocess import PIPE
|
from subprocess import PIPE
|
||||||
from typing import Dict, List, Optional, Tuple, Union
|
from typing import Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
USE_COLOUR = False
|
||||||
description="Nix/NixOS package version diff tool",
|
|
||||||
epilog="See the nvd(1) manual page for more information.",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"--color",
|
|
||||||
default="auto",
|
|
||||||
help="Controls use of colour; one of 'auto', 'never', 'always'.")
|
|
||||||
parser.add_argument(
|
|
||||||
"root1",
|
|
||||||
help="The first Nix store ptah to compare.")
|
|
||||||
parser.add_argument(
|
|
||||||
"root2",
|
|
||||||
help="The second Nix store path to compare.")
|
|
||||||
|
|
||||||
ARGS = parser.parse_args()
|
|
||||||
|
|
||||||
USE_COLOUR = ARGS.color == "always" or (ARGS.color == "auto" and sys.stdout.isatty())
|
|
||||||
|
|
||||||
SGR_RESET = 0
|
SGR_RESET = 0
|
||||||
SGR_BOLD = 1
|
SGR_BOLD = 1
|
||||||
|
@ -85,6 +70,7 @@ def sgr(*args):
|
||||||
return ""
|
return ""
|
||||||
return "\x1b[" + ";".join(str(arg) for arg in args) + "m"
|
return "\x1b[" + ";".join(str(arg) for arg in args) + "m"
|
||||||
|
|
||||||
|
INST_INSTALLED = "I"
|
||||||
INST_ADDED = sgr(SGR_FG + SGR_BRIGHT + SGR_GREEN) + "A"
|
INST_ADDED = sgr(SGR_FG + SGR_BRIGHT + SGR_GREEN) + "A"
|
||||||
INST_REMOVED = sgr(SGR_FG + SGR_BRIGHT + SGR_RED) + "R"
|
INST_REMOVED = sgr(SGR_FG + SGR_BRIGHT + SGR_RED) + "R"
|
||||||
INST_UPGRADED = sgr(SGR_FG + SGR_BRIGHT + SGR_CYAN) + "U"
|
INST_UPGRADED = sgr(SGR_FG + SGR_BRIGHT + SGR_CYAN) + "U"
|
||||||
|
@ -257,6 +243,9 @@ class PackageManifest:
|
||||||
def contains_pname(self, pname: str) -> bool:
|
def contains_pname(self, pname: str) -> bool:
|
||||||
return pname in self._packages_by_pname
|
return pname in self._packages_by_pname
|
||||||
|
|
||||||
|
def all_pnames(self):
|
||||||
|
return self._packages_by_pname.keys()
|
||||||
|
|
||||||
class PackageManifestPair:
|
class PackageManifestPair:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -378,6 +367,10 @@ def print_package_list(
|
||||||
"Expect either one versions map, or a fixed install state."
|
"Expect either one versions map, or a fixed install state."
|
||||||
have_single_version = right_versions_map is None
|
have_single_version = right_versions_map is None
|
||||||
|
|
||||||
|
if not pnames:
|
||||||
|
print("No packages to display.")
|
||||||
|
return
|
||||||
|
|
||||||
count = 1
|
count = 1
|
||||||
count_width = len(str(len(pnames)))
|
count_width = len(str(len(pnames)))
|
||||||
count_format_str = "#{:0" + str(count_width) + "d}"
|
count_format_str = "#{:0" + str(count_width) + "d}"
|
||||||
|
@ -430,9 +423,51 @@ def print_package_list(
|
||||||
))
|
))
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
def main():
|
def run_list(*, root, only_selected, name_patterns):
|
||||||
left_path = Path(ARGS.root1)
|
path = Path(root)
|
||||||
right_path = Path(ARGS.root2)
|
|
||||||
|
if not path.exists():
|
||||||
|
sys.stderr.write(f"Path does not exist: {path}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
path = path.resolve()
|
||||||
|
if (path / "sw").is_dir():
|
||||||
|
manifest = PackageManifest.parse_tree((path / "sw").resolve())
|
||||||
|
else:
|
||||||
|
manifest = PackageManifest.parse_tree(path / "sw")
|
||||||
|
|
||||||
|
closure_paths: List[str] = subprocess.run(
|
||||||
|
["nix-store", "-qR", str(path)],
|
||||||
|
stdout=PIPE,
|
||||||
|
text=True,
|
||||||
|
).stdout.rstrip("\n").split("\n")
|
||||||
|
|
||||||
|
closure_map: Dict[str, List[str]] = closure_paths_to_map(closure_paths)
|
||||||
|
|
||||||
|
if only_selected:
|
||||||
|
target_pnames = list(manifest.all_pnames())
|
||||||
|
else:
|
||||||
|
target_pnames = list(closure_map.keys())
|
||||||
|
|
||||||
|
target_pnames = [
|
||||||
|
pname
|
||||||
|
for pname in target_pnames
|
||||||
|
if all(fnmatch.fnmatch(pname, pattern) for pattern in name_patterns)
|
||||||
|
]
|
||||||
|
|
||||||
|
target_pnames.sort()
|
||||||
|
|
||||||
|
print_package_list(
|
||||||
|
pnames=target_pnames,
|
||||||
|
manifest_pair=PackageManifestPair(manifest, manifest),
|
||||||
|
left_versions_map=closure_map,
|
||||||
|
right_versions_map=None,
|
||||||
|
fixed_install_state=INST_INSTALLED,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run_diff(*, root1, root2):
|
||||||
|
left_path = Path(root1)
|
||||||
|
right_path = Path(root2)
|
||||||
|
|
||||||
if not left_path.exists():
|
if not left_path.exists():
|
||||||
sys.stderr.write(f"Path does not exist: {left_path}\n")
|
sys.stderr.write(f"Path does not exist: {left_path}\n")
|
||||||
|
@ -547,6 +582,67 @@ def main():
|
||||||
f"delta {right_only_paths_count - left_only_paths_count:+d})."
|
f"delta {right_only_paths_count - left_only_paths_count:+d})."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Nix/NixOS package version diff tool",
|
||||||
|
epilog="See the nvd(1) manual page for more information.",
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"--color",
|
||||||
|
default="auto",
|
||||||
|
help="Controls use of colour; one of 'auto', 'never', 'always'.")
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(dest="action")
|
||||||
|
|
||||||
|
diff_parser = subparsers.add_parser(
|
||||||
|
"diff",
|
||||||
|
help="Diff two Nix store paths and their closures.")
|
||||||
|
diff_parser.add_argument(
|
||||||
|
"root1",
|
||||||
|
help="The first Nix store path to compare.")
|
||||||
|
diff_parser.add_argument(
|
||||||
|
"root2",
|
||||||
|
help="The second Nix store path to compare.")
|
||||||
|
|
||||||
|
list_parser = subparsers.add_parser(
|
||||||
|
"list",
|
||||||
|
help="List packages in the closure of a Nix store path.")
|
||||||
|
list_parser.add_argument(
|
||||||
|
"-r", "--root",
|
||||||
|
default="/run/current-system",
|
||||||
|
help="The The Nix store path to work with.")
|
||||||
|
list_parser.add_argument(
|
||||||
|
"-s", "--selected",
|
||||||
|
dest="only_selected",
|
||||||
|
action="store_true",
|
||||||
|
help="Only show selected packages (direct dependencies).")
|
||||||
|
list_parser.add_argument(
|
||||||
|
nargs="*",
|
||||||
|
default=[],
|
||||||
|
dest="name_patterns",
|
||||||
|
help="Patterns (globs) that must all match package names.")
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global USE_COLOUR
|
||||||
|
|
||||||
|
args = parse_args()
|
||||||
|
action = args.action
|
||||||
|
USE_COLOUR = args.color == "always" or (args.color == "auto" and sys.stdout.isatty())
|
||||||
|
del args.action
|
||||||
|
del args.color
|
||||||
|
|
||||||
|
if action is None:
|
||||||
|
print("nvd: Subcommand required, see 'nvd --help'.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
{
|
||||||
|
"list": run_list,
|
||||||
|
"diff": run_diff,
|
||||||
|
}[action](**vars(args))
|
||||||
|
|
||||||
# Importing this module is kinda broken because of the constant initializations
|
# Importing this module is kinda broken because of the constant initializations
|
||||||
# at the top.
|
# at the top.
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
90
src/nvd.1
90
src/nvd.1
|
@ -1,28 +1,52 @@
|
||||||
.TH nvd 1 2021-04-09 nvd "User Commands"
|
.TH nvd 1 2021-05-16 nvd "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
nvd \- Nix/NixOS package version diff tool
|
nvd \- Nix/NixOS package version diff tool
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.P
|
.P
|
||||||
.B nvd [ -h | --help ]
|
.B nvd [ -h | --help ]
|
||||||
.P
|
.P
|
||||||
.B nvd [ --color=(auto|always|never) ] root1 root2
|
.B nvd [ --color=(auto|always|never) ] diff
|
||||||
|
.I root1
|
||||||
|
.I root2
|
||||||
|
.P
|
||||||
|
.B nvd [ --color=(auto|always|never) ] list
|
||||||
|
.RS
|
||||||
|
.B [ -r|--root
|
||||||
|
.I store-path
|
||||||
|
.B ]
|
||||||
|
.br
|
||||||
|
.B [ -s|--selected ]
|
||||||
|
.br
|
||||||
|
.B [
|
||||||
|
.I name-pattern
|
||||||
|
.B ]*
|
||||||
|
.RE
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
.P
|
.P
|
||||||
.B nvd
|
.B nvd
|
||||||
is a tool for diffing the versions of all store paths in the closures of two Nix
|
is a tool for working with Nix store paths and their closures, grouped by
|
||||||
store paths, neatly summarizing the differences. This is mainly intended for
|
package name, with nice presentation for package versions. This is mainly
|
||||||
comparing two system configurations and is inspired by the output of
|
intended for listing installed packages and comparing two system configurations,
|
||||||
|
and is inspired by the output of
|
||||||
.B emerge -pv
|
.B emerge -pv
|
||||||
from Gentoo's Portage package manager.
|
from Gentoo's Portage package manager.
|
||||||
.B nvd
|
.B nvd
|
||||||
could also be likened to the output of Debian's
|
could also be likened to the output of Debian's
|
||||||
|
.B apt list -i
|
||||||
|
and
|
||||||
.B apt upgrade -V,
|
.B apt upgrade -V,
|
||||||
or any equivalent from other distributions' package managers.
|
or any equivalent from other distributions' package managers.
|
||||||
.B nvd
|
.B nvd
|
||||||
isn't limited to comparing system configurations though, and can work with any
|
isn't limited to comparing system configurations though, and can work with any
|
||||||
two store paths.
|
two store paths.
|
||||||
|
.P The
|
||||||
|
.B diff
|
||||||
|
command compares the packages and package versions included in two closures.
|
||||||
|
The
|
||||||
|
.B list
|
||||||
|
command displays the list of packages included in a closure, optionally filtered
|
||||||
|
to some criteria.
|
||||||
.P
|
.P
|
||||||
When given two system configurations,
|
|
||||||
.B nvd
|
.B nvd
|
||||||
distinguishes between packages that are explicitly included in
|
distinguishes between packages that are explicitly included in
|
||||||
.B environment.systemPackages,
|
.B environment.systemPackages,
|
||||||
|
@ -88,14 +112,41 @@ detects whether stdout is a terminal and only uses colour if it is. Passing
|
||||||
forces colour to always be used, and passing
|
forces colour to always be used, and passing
|
||||||
.B never
|
.B never
|
||||||
forces colour to never be used.
|
forces colour to never be used.
|
||||||
|
.TP
|
||||||
|
-r|--root <store-path>
|
||||||
|
For the
|
||||||
|
.B list
|
||||||
|
command, this is the root store path to use, or a link to the store path to use.
|
||||||
|
.TP
|
||||||
|
-s|--selected
|
||||||
|
For the
|
||||||
|
.B list
|
||||||
|
command, only print out selected packages (direct dependencies).
|
||||||
|
.TP
|
||||||
|
<name-pattern>
|
||||||
|
For the
|
||||||
|
.B list
|
||||||
|
command, only print out names matching all of the given patterns. The character
|
||||||
|
.B ?
|
||||||
|
matches exactly one of any character, and the character
|
||||||
|
.B *
|
||||||
|
matches zero or more of any character.
|
||||||
.SH OUTPUT
|
.SH OUTPUT
|
||||||
.P
|
.P
|
||||||
The output of
|
If the
|
||||||
|
.B diff
|
||||||
|
command is requested, then the output of
|
||||||
.B nvd
|
.B nvd
|
||||||
displays the given names of the two store paths being compared on the first two
|
displays the given names of the two store paths being compared on the first two
|
||||||
lines, then changed packages on subsequent lines, one line per package, grouped
|
lines. After this, for both the
|
||||||
by the type of change. Newly added or removed packages, packages with version
|
.B diff
|
||||||
or count changes, and packages with selection state changes are displayed. Hash changes are
|
and
|
||||||
|
.B list
|
||||||
|
commands, package names, versions, and other information is printed out, with
|
||||||
|
one line per package name, in lexicographic order. When producing a diff,
|
||||||
|
packages are grouped by the type of change: newly added or removed packages,
|
||||||
|
packages with version or count changes, and packages with selection state
|
||||||
|
changes. Hash changes are
|
||||||
.B not
|
.B not
|
||||||
considered important, and will not cause a package to be displayed, however
|
considered important, and will not cause a package to be displayed, however
|
||||||
changes in the number of times a package
|
changes in the number of times a package
|
||||||
|
@ -132,6 +183,7 @@ removed, upgraded or downgraded. The possibilities here are:
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
|
I: Installed (no change).
|
||||||
A: Package is newly added to the closure.
|
A: Package is newly added to the closure.
|
||||||
R: All versions of the package are being removed the closure.
|
R: All versions of the package are being removed the closure.
|
||||||
U: The package is being upgraded.
|
U: The package is being upgraded.
|
||||||
|
@ -140,7 +192,15 @@ C: None of the above apply; unspecified changes.
|
||||||
.EE
|
.EE
|
||||||
.RE
|
.RE
|
||||||
.P
|
.P
|
||||||
Packages can be displayed with
|
The
|
||||||
|
.B list
|
||||||
|
command displays only the
|
||||||
|
.B I
|
||||||
|
state, and the
|
||||||
|
.B diff
|
||||||
|
command can display all states but the
|
||||||
|
.B I
|
||||||
|
state. Packages can be displayed with
|
||||||
.B C
|
.B C
|
||||||
install state for a few different reasons, including the number of copies of an
|
install state for a few different reasons, including the number of copies of an
|
||||||
existing version in the closure changing, or due to the selection state changing
|
existing version in the closure changing, or due to the selection state changing
|
||||||
|
@ -200,7 +260,7 @@ plus additional versions of some existing packages:
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
$ nvd /nix/var/nix/profiles/system-{29,30}-link
|
$ nvd diff /nix/var/nix/profiles/system-{29,30}-link
|
||||||
<<< /nix/var/nix/profiles/system-29-link
|
<<< /nix/var/nix/profiles/system-29-link
|
||||||
>>> /nix/var/nix/profiles/system-30-link
|
>>> /nix/var/nix/profiles/system-30-link
|
||||||
Version changes:
|
Version changes:
|
||||||
|
@ -232,7 +292,7 @@ have been installed.
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
$ nvd /nix/var/nix/profiles/system-{33,34}-link
|
$ nvd diff /nix/var/nix/profiles/system-{33,34}-link
|
||||||
<<< /nix/var/nix/profiles/system/system-33-link
|
<<< /nix/var/nix/profiles/system/system-33-link
|
||||||
>>> /nix/var/nix/profiles/system/system-34-link
|
>>> /nix/var/nix/profiles/system/system-34-link
|
||||||
Version changes:
|
Version changes:
|
||||||
|
@ -263,7 +323,7 @@ one, despite this actually being a newer commit than the old one:
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
$ nvd /nix/var/nix/profiles/system-{43,44}-link
|
$ nvd diff /nix/var/nix/profiles/system-{43,44}-link
|
||||||
<<< /nix/var/nix/profiles/system/system-43-link
|
<<< /nix/var/nix/profiles/system/system-43-link
|
||||||
>>> /nix/var/nix/profiles/system/system-44-link
|
>>> /nix/var/nix/profiles/system/system-44-link
|
||||||
Version changes:
|
Version changes:
|
||||||
|
@ -278,7 +338,7 @@ to output no packages at all if no versions have changed:
|
||||||
.P
|
.P
|
||||||
.RS
|
.RS
|
||||||
.EX
|
.EX
|
||||||
$ nvd /nix/var/nix/profiles/system-{23,24}-link
|
$ nvd diff /nix/var/nix/profiles/system-{23,24}-link
|
||||||
<<< /nix/var/nix/profiles/system/system-23-link
|
<<< /nix/var/nix/profiles/system/system-23-link
|
||||||
>>> /nix/var/nix/profiles/system/system-24-link
|
>>> /nix/var/nix/profiles/system/system-24-link
|
||||||
No version or selection state changes.
|
No version or selection state changes.
|
||||||
|
|
Loading…
Reference in a new issue