Add 'nix diff-closures' command
This command makes it easier to see what changed between two closures,
i.e. what packages/versions got added or removed, and whether there
were any notable changes in path size.
For example:
$ nix diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link
blender-bin: 2.83.0 → 2.83.2, -294.2 KiB
curl: 7.68.0 → 7.70.0, +19.1 KiB
firmware-linux-nonfree: 2020-01-22 → 2020-05-19, +30827.7 KiB
ibus: -21.8 KiB
initrd-linux: 5.4.46 → 5.4.51, +16.9 KiB
libexif: 0.6.21 → 0.6.22, +497.6 KiB
linux: 5.4.46 → 5.4.51, +13.2 KiB
mesa: 19.3.3 → 19.3.5, -183.9 KiB
nix: 2.4pre20200701_6ff9aa8 → 2.4pre20200708_9223603, +9.7 KiB
nix-bash-completions: 0.6.8 → ∅, -57.6 KiB
nixos-system-hagbard: 20.03.20200615.a84b797 → 20.03.20200713.add5529
nvidia-persistenced: 440.82 → 440.100
nvidia-settings: 440.82 → 440.100
nvidia-x11: 440.82-5.4.46 → 440.100-5.4.51, +664.7 KiB
pcre: 8.43 → 8.44
php: 7.3.16 → 7.3.20, -26.2 KiB
python3.7-youtube-dl: 2020.06.06 → 2020.06.16.1, +8.4 KiB
samba: 4.11.5 → 4.11.9, +30.1 KiB
sane-backends: 1.0.28 → 1.0.30, +680.5 KiB
source: -182.0 KiB
zfs-kernel: 0.8.3-5.4.46 → 0.8.4-5.4.51, +9.9 KiB
zfs-user: 0.8.3 → 0.8.4, +20.1 KiB
2020-07-15 14:18:06 +00:00
|
|
|
#include "command.hh"
|
|
|
|
#include "shared.hh"
|
|
|
|
#include "store-api.hh"
|
|
|
|
#include "common-args.hh"
|
|
|
|
#include "names.hh"
|
|
|
|
|
|
|
|
#include <regex>
|
|
|
|
|
|
|
|
using namespace nix;
|
|
|
|
|
|
|
|
struct Info
|
|
|
|
{
|
|
|
|
std::string outputName;
|
|
|
|
};
|
|
|
|
|
|
|
|
// name -> version -> store paths
|
|
|
|
typedef std::map<std::string, std::map<std::string, std::map<StorePath, Info>>> GroupedPaths;
|
|
|
|
|
|
|
|
GroupedPaths getClosureInfo(ref<Store> store, const StorePath & toplevel)
|
|
|
|
{
|
|
|
|
StorePathSet closure;
|
|
|
|
store->computeFSClosure({toplevel}, closure);
|
|
|
|
|
|
|
|
GroupedPaths groupedPaths;
|
|
|
|
|
|
|
|
for (auto & path : closure) {
|
|
|
|
/* Strip the output name. Unfortunately this is ambiguous (we
|
|
|
|
can't distinguish between output names like "bin" and
|
|
|
|
version suffixes like "unstable"). */
|
|
|
|
static std::regex regex("(.*)-([a-z]+|lib32|lib64)");
|
|
|
|
std::smatch match;
|
|
|
|
std::string name(path.name());
|
|
|
|
std::string outputName;
|
|
|
|
if (std::regex_match(name, match, regex)) {
|
|
|
|
name = match[1];
|
|
|
|
outputName = match[2];
|
|
|
|
}
|
|
|
|
|
|
|
|
DrvName drvName(name);
|
|
|
|
groupedPaths[drvName.name][drvName.version].emplace(path, Info { .outputName = outputName });
|
|
|
|
}
|
|
|
|
|
|
|
|
return groupedPaths;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string showVersions(const std::set<std::string> & versions)
|
|
|
|
{
|
|
|
|
if (versions.empty()) return "∅";
|
|
|
|
std::set<std::string> versions2;
|
|
|
|
for (auto & version : versions)
|
|
|
|
versions2.insert(version.empty() ? "ε" : version);
|
|
|
|
return concatStringsSep(", ", versions2);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CmdDiffClosures : SourceExprCommand
|
|
|
|
{
|
|
|
|
std::string _before, _after;
|
|
|
|
|
|
|
|
CmdDiffClosures()
|
|
|
|
{
|
|
|
|
expectArg("before", &_before);
|
|
|
|
expectArg("after", &_after);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "show what packages and versions were added and removed between two closures";
|
|
|
|
}
|
|
|
|
|
|
|
|
Category category() override { return catSecondary; }
|
|
|
|
|
|
|
|
Examples examples() override
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
{
|
|
|
|
"To show what got added and removed between two versions of the NixOS system profile:",
|
|
|
|
"nix diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(ref<Store> store) override
|
|
|
|
{
|
2020-07-16 12:25:51 +00:00
|
|
|
auto before = parseInstallable(store, _before);
|
|
|
|
auto beforePath = toStorePath(store, Realise::Outputs, operateOn, before);
|
|
|
|
auto after = parseInstallable(store, _after);
|
|
|
|
auto afterPath = toStorePath(store, Realise::Outputs, operateOn, after);
|
Add 'nix diff-closures' command
This command makes it easier to see what changed between two closures,
i.e. what packages/versions got added or removed, and whether there
were any notable changes in path size.
For example:
$ nix diff-closures /nix/var/nix/profiles/system-655-link /nix/var/nix/profiles/system-658-link
blender-bin: 2.83.0 → 2.83.2, -294.2 KiB
curl: 7.68.0 → 7.70.0, +19.1 KiB
firmware-linux-nonfree: 2020-01-22 → 2020-05-19, +30827.7 KiB
ibus: -21.8 KiB
initrd-linux: 5.4.46 → 5.4.51, +16.9 KiB
libexif: 0.6.21 → 0.6.22, +497.6 KiB
linux: 5.4.46 → 5.4.51, +13.2 KiB
mesa: 19.3.3 → 19.3.5, -183.9 KiB
nix: 2.4pre20200701_6ff9aa8 → 2.4pre20200708_9223603, +9.7 KiB
nix-bash-completions: 0.6.8 → ∅, -57.6 KiB
nixos-system-hagbard: 20.03.20200615.a84b797 → 20.03.20200713.add5529
nvidia-persistenced: 440.82 → 440.100
nvidia-settings: 440.82 → 440.100
nvidia-x11: 440.82-5.4.46 → 440.100-5.4.51, +664.7 KiB
pcre: 8.43 → 8.44
php: 7.3.16 → 7.3.20, -26.2 KiB
python3.7-youtube-dl: 2020.06.06 → 2020.06.16.1, +8.4 KiB
samba: 4.11.5 → 4.11.9, +30.1 KiB
sane-backends: 1.0.28 → 1.0.30, +680.5 KiB
source: -182.0 KiB
zfs-kernel: 0.8.3-5.4.46 → 0.8.4-5.4.51, +9.9 KiB
zfs-user: 0.8.3 → 0.8.4, +20.1 KiB
2020-07-15 14:18:06 +00:00
|
|
|
|
|
|
|
auto beforeClosure = getClosureInfo(store, beforePath);
|
|
|
|
auto afterClosure = getClosureInfo(store, afterPath);
|
|
|
|
|
|
|
|
std::set<std::string> allNames;
|
|
|
|
for (auto & [name, _] : beforeClosure) allNames.insert(name);
|
|
|
|
for (auto & [name, _] : afterClosure) allNames.insert(name);
|
|
|
|
|
|
|
|
for (auto & name : allNames) {
|
|
|
|
auto & beforeVersions = beforeClosure[name];
|
|
|
|
auto & afterVersions = afterClosure[name];
|
|
|
|
|
|
|
|
auto totalSize = [&](const std::map<std::string, std::map<StorePath, Info>> & versions)
|
|
|
|
{
|
|
|
|
uint64_t sum = 0;
|
|
|
|
for (auto & [_, paths] : versions)
|
|
|
|
for (auto & [path, _] : paths)
|
|
|
|
sum += store->queryPathInfo(path)->narSize;
|
|
|
|
return sum;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto beforeSize = totalSize(beforeVersions);
|
|
|
|
auto afterSize = totalSize(afterVersions);
|
|
|
|
auto sizeDelta = (int64_t) afterSize - (int64_t) beforeSize;
|
|
|
|
auto showDelta = abs(sizeDelta) >= 8 * 1024;
|
|
|
|
|
|
|
|
std::set<std::string> removed, unchanged;
|
|
|
|
for (auto & [version, _] : beforeVersions)
|
|
|
|
if (!afterVersions.count(version)) removed.insert(version); else unchanged.insert(version);
|
|
|
|
|
|
|
|
std::set<std::string> added;
|
|
|
|
for (auto & [version, _] : afterVersions)
|
|
|
|
if (!beforeVersions.count(version)) added.insert(version);
|
|
|
|
|
|
|
|
if (showDelta || !removed.empty() || !added.empty()) {
|
|
|
|
std::vector<std::string> items;
|
|
|
|
if (!removed.empty() || !added.empty())
|
|
|
|
items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added)));
|
|
|
|
if (showDelta)
|
|
|
|
items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0));
|
|
|
|
std::cout << fmt("%s: %s\n", name, concatStringsSep(", ", items));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static auto r1 = registerCommand<CmdDiffClosures>("diff-closures");
|