mirror of
https://github.com/NixOS/nix.git
synced 2025-12-02 23:20:59 +01:00
Add nix nario command
This replaces `nix-store --export` and `nix-store --import`.
This commit is contained in:
parent
377b60ee9b
commit
c8633ce4f8
10 changed files with 353 additions and 71 deletions
|
|
@ -5,91 +5,112 @@
|
||||||
#include "nix/store/common-protocol.hh"
|
#include "nix/store/common-protocol.hh"
|
||||||
#include "nix/store/common-protocol-impl.hh"
|
#include "nix/store/common-protocol-impl.hh"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static void exportPath(Store & store, const StorePath & path, Sink & sink)
|
static const uint32_t exportMagicV1 = 0x4558494e;
|
||||||
{
|
|
||||||
auto info = store.queryPathInfo(path);
|
|
||||||
|
|
||||||
HashSink hashSink(HashAlgorithm::SHA256);
|
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version)
|
||||||
TeeSink teeSink(sink, hashSink);
|
|
||||||
|
|
||||||
store.narFromPath(path, teeSink);
|
|
||||||
|
|
||||||
/* Refuse to export paths that have changed. This prevents
|
|
||||||
filesystem corruption from spreading to other machines.
|
|
||||||
Don't complain if the stored hash is zero (unknown). */
|
|
||||||
Hash hash = hashSink.currentHash().hash;
|
|
||||||
if (hash != info->narHash && info->narHash != Hash(info->narHash.algo))
|
|
||||||
throw Error(
|
|
||||||
"hash of path '%s' has changed from '%s' to '%s'!",
|
|
||||||
store.printStorePath(path),
|
|
||||||
info->narHash.to_string(HashFormat::Nix32, true),
|
|
||||||
hash.to_string(HashFormat::Nix32, true));
|
|
||||||
|
|
||||||
teeSink << exportMagic << store.printStorePath(path);
|
|
||||||
CommonProto::write(store, CommonProto::WriteConn{.to = teeSink}, info->references);
|
|
||||||
teeSink << (info->deriver ? store.printStorePath(*info->deriver) : "") << 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink)
|
|
||||||
{
|
{
|
||||||
auto sorted = store.topoSortPaths(paths);
|
auto sorted = store.topoSortPaths(paths);
|
||||||
std::reverse(sorted.begin(), sorted.end());
|
std::reverse(sorted.begin(), sorted.end());
|
||||||
|
|
||||||
for (auto & path : sorted) {
|
auto dumpNar = [&](const ValidPathInfo & info) {
|
||||||
sink << 1;
|
HashSink hashSink(HashAlgorithm::SHA256);
|
||||||
exportPath(store, path, sink);
|
TeeSink teeSink(sink, hashSink);
|
||||||
}
|
|
||||||
|
|
||||||
sink << 0;
|
store.narFromPath(info.path, teeSink);
|
||||||
|
|
||||||
|
/* Refuse to export paths that have changed. This prevents
|
||||||
|
filesystem corruption from spreading to other machines.
|
||||||
|
Don't complain if the stored hash is zero (unknown). */
|
||||||
|
Hash hash = hashSink.currentHash().hash;
|
||||||
|
if (hash != info.narHash && info.narHash != Hash(info.narHash.algo))
|
||||||
|
throw Error(
|
||||||
|
"hash of path '%s' has changed from '%s' to '%s'!",
|
||||||
|
store.printStorePath(info.path),
|
||||||
|
info.narHash.to_string(HashFormat::Nix32, true),
|
||||||
|
hash.to_string(HashFormat::Nix32, true));
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (version) {
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
for (auto & path : sorted) {
|
||||||
|
sink << 1;
|
||||||
|
auto info = store.queryPathInfo(path);
|
||||||
|
dumpNar(*info);
|
||||||
|
sink << exportMagicV1 << store.printStorePath(path);
|
||||||
|
CommonProto::write(store, CommonProto::WriteConn{.to = sink}, info->references);
|
||||||
|
sink << (info->deriver ? store.printStorePath(*info->deriver) : "") << 0;
|
||||||
|
}
|
||||||
|
sink << 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Error("unsupported nario version %d", version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
|
StorePaths importPaths(Store & store, Source & source, CheckSigsFlag checkSigs)
|
||||||
{
|
{
|
||||||
StorePaths res;
|
StorePaths res;
|
||||||
while (true) {
|
|
||||||
auto n = readNum<uint64_t>(source);
|
|
||||||
if (n == 0)
|
|
||||||
break;
|
|
||||||
if (n != 1)
|
|
||||||
throw Error("input doesn't look like something created by 'nix-store --export'");
|
|
||||||
|
|
||||||
/* Extract the NAR from the source. */
|
auto version = readNum<uint64_t>(source);
|
||||||
StringSink saved;
|
|
||||||
TeeSource tee{source, saved};
|
|
||||||
NullFileSystemObjectSink ether;
|
|
||||||
parseDump(ether, tee);
|
|
||||||
|
|
||||||
uint32_t magic = readInt(source);
|
/* Note: nario version 1 lacks an explicit header. The first
|
||||||
if (magic != exportMagic)
|
integer denotes whether a store path follows or not. So look
|
||||||
throw Error("Nix archive cannot be imported; wrong format");
|
for 0 or 1. */
|
||||||
|
switch (version) {
|
||||||
|
|
||||||
auto path = store.parseStorePath(readString(source));
|
case 0:
|
||||||
|
/* Empty version 1 nario, nothing to do. */
|
||||||
|
break;
|
||||||
|
|
||||||
// Activity act(*logger, lvlInfo, "importing path '%s'", info.path);
|
case 1:
|
||||||
|
/* Non-empty version 1 nario. */
|
||||||
|
while (true) {
|
||||||
|
/* Extract the NAR from the source. */
|
||||||
|
StringSink saved;
|
||||||
|
TeeSource tee{source, saved};
|
||||||
|
NullFileSystemObjectSink ether;
|
||||||
|
parseDump(ether, tee);
|
||||||
|
|
||||||
auto references = CommonProto::Serialise<StorePathSet>::read(store, CommonProto::ReadConn{.from = source});
|
uint32_t magic = readInt(source);
|
||||||
auto deriver = readString(source);
|
if (magic != exportMagicV1)
|
||||||
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);
|
throw Error("nario cannot be imported; wrong format");
|
||||||
|
|
||||||
ValidPathInfo info{path, narHash};
|
auto path = store.parseStorePath(readString(source));
|
||||||
if (deriver != "")
|
|
||||||
info.deriver = store.parseStorePath(deriver);
|
|
||||||
info.references = references;
|
|
||||||
info.narSize = saved.s.size();
|
|
||||||
|
|
||||||
// Ignore optional legacy signature.
|
auto references = CommonProto::Serialise<StorePathSet>::read(store, CommonProto::ReadConn{.from = source});
|
||||||
if (readInt(source) == 1)
|
auto deriver = readString(source);
|
||||||
readString(source);
|
auto narHash = hashString(HashAlgorithm::SHA256, saved.s);
|
||||||
|
|
||||||
// Can't use underlying source, which would have been exhausted
|
ValidPathInfo info{path, narHash};
|
||||||
auto source = StringSource(saved.s);
|
if (deriver != "")
|
||||||
store.addToStore(info, source, NoRepair, checkSigs);
|
info.deriver = store.parseStorePath(deriver);
|
||||||
|
info.references = references;
|
||||||
|
info.narSize = saved.s.size();
|
||||||
|
|
||||||
res.push_back(info.path);
|
// Ignore optional legacy signature.
|
||||||
|
if (readInt(source) == 1)
|
||||||
|
readString(source);
|
||||||
|
|
||||||
|
// Can't use underlying source, which would have been exhausted.
|
||||||
|
auto source2 = StringSource(saved.s);
|
||||||
|
store.addToStore(info, source2, NoRepair, checkSigs);
|
||||||
|
|
||||||
|
res.push_back(info.path);
|
||||||
|
|
||||||
|
auto n = readNum<uint64_t>(source);
|
||||||
|
if (n == 0)
|
||||||
|
break;
|
||||||
|
if (n != 1)
|
||||||
|
throw Error("input doesn't look like a nario");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw Error("input doesn't look like a nario");
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,11 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
/**
|
|
||||||
* Magic header of exportPath() output (obsolete).
|
|
||||||
*/
|
|
||||||
const uint32_t exportMagic = 0x4558494e;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export multiple paths in the format expected by `nix-store
|
* Export multiple paths in the format expected by `nix-store
|
||||||
* --import`. The paths will be sorted topologically.
|
* --import`. The paths will be sorted topologically.
|
||||||
*/
|
*/
|
||||||
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink);
|
void exportPaths(Store & store, const StorePathSet & paths, Sink & sink, unsigned int version);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a sequence of NAR dumps created by `exportPaths()` into the
|
* Import a sequence of NAR dumps created by `exportPaths()` into the
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,7 @@ nix_sources = [ config_priv_h ] + files(
|
||||||
'make-content-addressed.cc',
|
'make-content-addressed.cc',
|
||||||
'man-pages.cc',
|
'man-pages.cc',
|
||||||
'nar.cc',
|
'nar.cc',
|
||||||
|
'nario.cc',
|
||||||
'optimise-store.cc',
|
'optimise-store.cc',
|
||||||
'path-from-hash-part.cc',
|
'path-from-hash-part.cc',
|
||||||
'path-info.cc',
|
'path-info.cc',
|
||||||
|
|
|
||||||
28
src/nix/nario-export.md
Normal file
28
src/nix/nario-export.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* Export the closure of building `nixpkgs#hello`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix nario export --format 1 -r nixpkgs#hello > dump
|
||||||
|
```
|
||||||
|
|
||||||
|
It can be imported in another store:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix nario import < dump
|
||||||
|
```
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
This command prints on standard output a serialization of the specified store paths in `nario` format. This serialization can be imported into another store using `nix nario import`.
|
||||||
|
|
||||||
|
References of a path are not exported by default; use `-r` to export a complete closure.
|
||||||
|
Paths are exported in topographically sorted order (i.e. if path `X` refers to `Y`, then `Y` appears before `X`).
|
||||||
|
|
||||||
|
You must specify the desired `nario` version. Currently the following versions are supported:
|
||||||
|
|
||||||
|
* `1`: This version is compatible with the legacy `nix-store --export` and `nix-store --import` commands.
|
||||||
|
|
||||||
|
)""
|
||||||
15
src/nix/nario-import.md
Normal file
15
src/nix/nario-import.md
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* Import store paths from the file named `dump`:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix nario import < dump
|
||||||
|
```
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
This command reads from standard input a serialization of store paths produced by `nix nario export` and adds them to the Nix store.
|
||||||
|
|
||||||
|
)""
|
||||||
18
src/nix/nario-list.md
Normal file
18
src/nix/nario-list.md
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
R""(
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
* List the contents of a nario file:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix nario list < dump
|
||||||
|
/nix/store/4y1jj6cwvslmfh1bzkhbvhx77az6yf00-xgcc-14.2.1.20250322-libgcc: 201856 bytes
|
||||||
|
/nix/store/d8hnbm5hvbg2vza50garppb63y724i94-libunistring-1.3: 2070240 bytes
|
||||||
|
…
|
||||||
|
```
|
||||||
|
|
||||||
|
# Description
|
||||||
|
|
||||||
|
This command lists the contents of a nario file read from standard input.
|
||||||
|
|
||||||
|
)""
|
||||||
189
src/nix/nario.cc
Normal file
189
src/nix/nario.cc
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
#include "nix/cmd/command.hh"
|
||||||
|
#include "nix/main/shared.hh"
|
||||||
|
#include "nix/store/store-api.hh"
|
||||||
|
#include "nix/store/export-import.hh"
|
||||||
|
#include "nix/util/callback.hh"
|
||||||
|
#include "nix/util/fs-sink.hh"
|
||||||
|
#include "nix/util/archive.hh"
|
||||||
|
|
||||||
|
using namespace nix;
|
||||||
|
|
||||||
|
struct CmdNario : NixMultiCommand
|
||||||
|
{
|
||||||
|
CmdNario()
|
||||||
|
: NixMultiCommand("nario", RegisterCommand::getCommandsFor({"nario"}))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "operations for manipulating nario files";
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category() override
|
||||||
|
{
|
||||||
|
return catUtility;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdNario = registerCommand<CmdNario>("nario");
|
||||||
|
|
||||||
|
struct CmdNarioExport : StorePathsCommand
|
||||||
|
{
|
||||||
|
unsigned int version = 0;
|
||||||
|
|
||||||
|
CmdNarioExport()
|
||||||
|
{
|
||||||
|
addFlag({
|
||||||
|
.longName = "format",
|
||||||
|
.description = "Version of the nario format to use. Must be `1`.",
|
||||||
|
.labels = {"nario-format"},
|
||||||
|
.handler = {&version},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "serialize store paths to standard output in nario format";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "nario-export.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store, StorePaths && storePaths) override
|
||||||
|
{
|
||||||
|
if (!version)
|
||||||
|
throw UsageError("`nix nario export` requires `--format` argument");
|
||||||
|
|
||||||
|
FdSink sink(getStandardOutput());
|
||||||
|
exportPaths(*store, StorePathSet(storePaths.begin(), storePaths.end()), sink, version);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdNarioExport = registerCommand2<CmdNarioExport>({"nario", "export"});
|
||||||
|
|
||||||
|
struct CmdNarioImport : StoreCommand
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "import store paths from a nario file on standard input";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "nario-import.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(ref<Store> store) override
|
||||||
|
{
|
||||||
|
FdSource source(getStandardInput());
|
||||||
|
importPaths(*store, source, NoCheckSigs); // FIXME
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdNarioImport = registerCommand2<CmdNarioImport>({"nario", "import"});
|
||||||
|
|
||||||
|
struct CmdNarioList : Command
|
||||||
|
{
|
||||||
|
std::string description() override
|
||||||
|
{
|
||||||
|
return "list the contents of a nario file";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string doc() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
#include "nario-list.md"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
struct Config : StoreConfig
|
||||||
|
{
|
||||||
|
Config(const Params & params)
|
||||||
|
: StoreConfig(params)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<Store> openStore() const override
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ListingStore : Store
|
||||||
|
{
|
||||||
|
ListingStore(ref<const Config> config)
|
||||||
|
: Store{*config}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void queryPathInfoUncached(
|
||||||
|
const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept override
|
||||||
|
{
|
||||||
|
callback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TrustedFlag> isTrustedClient() override
|
||||||
|
{
|
||||||
|
return Trusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs) override
|
||||||
|
{
|
||||||
|
logger->cout(fmt("%s: %d bytes", printStorePath(info.path), info.narSize));
|
||||||
|
// Discard the NAR.
|
||||||
|
NullFileSystemObjectSink parseSink;
|
||||||
|
parseDump(parseSink, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePath addToStoreFromDump(
|
||||||
|
Source & dump,
|
||||||
|
std::string_view name,
|
||||||
|
FileSerialisationMethod dumpMethod,
|
||||||
|
ContentAddressMethod hashMethod,
|
||||||
|
HashAlgorithm hashAlgo,
|
||||||
|
const StorePathSet & references,
|
||||||
|
RepairFlag repair) override
|
||||||
|
{
|
||||||
|
unsupported("addToStoreFromDump");
|
||||||
|
}
|
||||||
|
|
||||||
|
void narFromPath(const StorePath & path, Sink & sink) override
|
||||||
|
{
|
||||||
|
unsupported("narFromPath");
|
||||||
|
}
|
||||||
|
|
||||||
|
void queryRealisationUncached(
|
||||||
|
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
||||||
|
{
|
||||||
|
callback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
|
||||||
|
{
|
||||||
|
return makeEmptySourceAccessor();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FdSource source(getStandardInput());
|
||||||
|
auto config = make_ref<Config>(StoreConfig::Params());
|
||||||
|
ListingStore lister(config);
|
||||||
|
importPaths(lister, source, NoCheckSigs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static auto rCmdNarioList = registerCommand2<CmdNarioList>({"nario", "list"});
|
||||||
|
|
@ -775,7 +775,7 @@ static void opExport(Strings opFlags, Strings opArgs)
|
||||||
paths.insert(store->followLinksToStorePath(i));
|
paths.insert(store->followLinksToStorePath(i));
|
||||||
|
|
||||||
FdSink sink(getStandardOutput());
|
FdSink sink(getStandardOutput());
|
||||||
exportPaths(*store, paths, sink);
|
exportPaths(*store, paths, sink, 1);
|
||||||
sink.flush();
|
sink.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -234,7 +234,7 @@ StoreWrapper::exportPaths(int fd, ...)
|
||||||
StorePathSet paths;
|
StorePathSet paths;
|
||||||
for (int n = 2; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
|
for (int n = 2; n < items; ++n) paths.insert(THIS->store->parseStorePath(SvPV_nolen(ST(n))));
|
||||||
FdSink sink(fd);
|
FdSink sink(fd);
|
||||||
exportPaths(*THIS->store, paths, sink);
|
exportPaths(*THIS->store, paths, sink, 1);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
croak("%s", e.what());
|
croak("%s", e.what());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,14 @@ clearStore
|
||||||
outPath=$(nix-build dependencies.nix --no-out-link)
|
outPath=$(nix-build dependencies.nix --no-out-link)
|
||||||
|
|
||||||
nix-store --export $outPath > $TEST_ROOT/exp
|
nix-store --export $outPath > $TEST_ROOT/exp
|
||||||
|
nix nario export --format 1 "$outPath" > $TEST_ROOT/exp2
|
||||||
|
cmp "$TEST_ROOT/exp" "$TEST_ROOT/exp2"
|
||||||
|
|
||||||
nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
|
nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
|
||||||
|
|
||||||
|
nix nario export --format 1 -r "$outPath" > $TEST_ROOT/exp_all2
|
||||||
|
cmp "$TEST_ROOT/exp_all" "$TEST_ROOT/exp_all2"
|
||||||
|
|
||||||
if nix-store --export $outPath >/dev/full ; then
|
if nix-store --export $outPath >/dev/full ; then
|
||||||
echo "exporting to a bad file descriptor should fail"
|
echo "exporting to a bad file descriptor should fail"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -38,3 +43,13 @@ clearStore
|
||||||
# Regression test: the derivers in exp_all2 are empty, which shouldn't
|
# Regression test: the derivers in exp_all2 are empty, which shouldn't
|
||||||
# cause a failure.
|
# cause a failure.
|
||||||
nix-store --import < $TEST_ROOT/exp_all2
|
nix-store --import < $TEST_ROOT/exp_all2
|
||||||
|
|
||||||
|
|
||||||
|
# Test `nix nario import` on files created by `nix-store --export`.
|
||||||
|
clearStore
|
||||||
|
nix nario import < $TEST_ROOT/exp_all
|
||||||
|
nix path-info "$outPath"
|
||||||
|
|
||||||
|
|
||||||
|
# Test `nix nario list`.
|
||||||
|
nix nario list < $TEST_ROOT/exp_all | grepQuiet "dependencies-input-0: .* bytes"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue