mirror of
https://github.com/NixOS/nix.git
synced 2024-11-21 22:32:26 +00:00
nix copy: Add --profile flag
This allows `nix copy` to atomically copy a store path and point a profile to it, without the risk that the store path might be GC'ed in between. This is useful for instance when deploying a new NixOS system profile from a remote store.
This commit is contained in:
parent
26c3fc11ea
commit
76f75e7691
@ -179,30 +179,34 @@ BuiltPathsCommand::BuiltPathsCommand(bool recursive)
|
|||||||
|
|
||||||
void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
|
void BuiltPathsCommand::run(ref<Store> store, Installables && installables)
|
||||||
{
|
{
|
||||||
BuiltPaths paths;
|
BuiltPaths rootPaths, allPaths;
|
||||||
|
|
||||||
if (all) {
|
if (all) {
|
||||||
if (installables.size())
|
if (installables.size())
|
||||||
throw UsageError("'--all' does not expect arguments");
|
throw UsageError("'--all' does not expect arguments");
|
||||||
// XXX: Only uses opaque paths, ignores all the realisations
|
// XXX: Only uses opaque paths, ignores all the realisations
|
||||||
for (auto & p : store->queryAllValidPaths())
|
for (auto & p : store->queryAllValidPaths())
|
||||||
paths.emplace_back(BuiltPath::Opaque{p});
|
rootPaths.emplace_back(BuiltPath::Opaque{p});
|
||||||
|
allPaths = rootPaths;
|
||||||
} else {
|
} else {
|
||||||
paths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
|
rootPaths = Installable::toBuiltPaths(getEvalStore(), store, realiseMode, operateOn, installables);
|
||||||
|
allPaths = rootPaths;
|
||||||
|
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
// XXX: This only computes the store path closure, ignoring
|
// XXX: This only computes the store path closure, ignoring
|
||||||
// intermediate realisations
|
// intermediate realisations
|
||||||
StorePathSet pathsRoots, pathsClosure;
|
StorePathSet pathsRoots, pathsClosure;
|
||||||
for (auto & root : paths) {
|
for (auto & root : rootPaths) {
|
||||||
auto rootFromThis = root.outPaths();
|
auto rootFromThis = root.outPaths();
|
||||||
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
pathsRoots.insert(rootFromThis.begin(), rootFromThis.end());
|
||||||
}
|
}
|
||||||
store->computeFSClosure(pathsRoots, pathsClosure);
|
store->computeFSClosure(pathsRoots, pathsClosure);
|
||||||
for (auto & path : pathsClosure)
|
for (auto & path : pathsClosure)
|
||||||
paths.emplace_back(BuiltPath::Opaque{path});
|
allPaths.emplace_back(BuiltPath::Opaque{path});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run(store, std::move(paths));
|
run(store, std::move(allPaths), std::move(rootPaths));
|
||||||
}
|
}
|
||||||
|
|
||||||
StorePathsCommand::StorePathsCommand(bool recursive)
|
StorePathsCommand::StorePathsCommand(bool recursive)
|
||||||
@ -210,10 +214,10 @@ StorePathsCommand::StorePathsCommand(bool recursive)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorePathsCommand::run(ref<Store> store, BuiltPaths && paths)
|
void StorePathsCommand::run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths)
|
||||||
{
|
{
|
||||||
StorePathSet storePaths;
|
StorePathSet storePaths;
|
||||||
for (auto & builtPath : paths)
|
for (auto & builtPath : allPaths)
|
||||||
for (auto & p : builtPath.outPaths())
|
for (auto & p : builtPath.outPaths())
|
||||||
storePaths.insert(p);
|
storePaths.insert(p);
|
||||||
|
|
||||||
@ -242,17 +246,21 @@ MixProfile::MixProfile()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixProfile::updateProfile(const StorePath & storePath)
|
void MixProfile::updateProfile(
|
||||||
|
const StorePath & storePath,
|
||||||
|
ref<Store> store_)
|
||||||
{
|
{
|
||||||
if (!profile) return;
|
if (!profile) return;
|
||||||
auto store = getStore().dynamic_pointer_cast<LocalFSStore>();
|
auto store = store_.dynamic_pointer_cast<LocalFSStore>();
|
||||||
if (!store) throw Error("'--profile' is not supported for this Nix store");
|
if (!store) throw Error("'--profile' is not supported for this Nix store");
|
||||||
auto profile2 = absPath(*profile);
|
auto profile2 = absPath(*profile);
|
||||||
switchLink(profile2,
|
switchLink(profile2,
|
||||||
createGeneration(*store, profile2, storePath));
|
createGeneration(*store, profile2, storePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixProfile::updateProfile(const BuiltPaths & buildables)
|
void MixProfile::updateProfile(
|
||||||
|
const BuiltPaths & buildables,
|
||||||
|
ref<Store> store)
|
||||||
{
|
{
|
||||||
if (!profile) return;
|
if (!profile) return;
|
||||||
|
|
||||||
@ -274,7 +282,7 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
|||||||
if (result.size() != 1)
|
if (result.size() != 1)
|
||||||
throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
throw UsageError("'--profile' requires that the arguments produce a single store path, but there are %d", result.size());
|
||||||
|
|
||||||
updateProfile(result[0]);
|
updateProfile(result[0], store);
|
||||||
}
|
}
|
||||||
|
|
||||||
MixDefaultProfile::MixDefaultProfile()
|
MixDefaultProfile::MixDefaultProfile()
|
||||||
@ -308,7 +316,8 @@ MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixEnvironment::setEnviron() {
|
void MixEnvironment::setEnviron()
|
||||||
|
{
|
||||||
if (ignoreEnvironment) {
|
if (ignoreEnvironment) {
|
||||||
if (!unset.empty())
|
if (!unset.empty())
|
||||||
throw UsageError("--unset does not make sense with --ignore-environment");
|
throw UsageError("--unset does not make sense with --ignore-environment");
|
||||||
|
@ -238,7 +238,7 @@ public:
|
|||||||
|
|
||||||
BuiltPathsCommand(bool recursive = false);
|
BuiltPathsCommand(bool recursive = false);
|
||||||
|
|
||||||
virtual void run(ref<Store> store, BuiltPaths && paths) = 0;
|
virtual void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) = 0;
|
||||||
|
|
||||||
void run(ref<Store> store, Installables && installables) override;
|
void run(ref<Store> store, Installables && installables) override;
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ struct StorePathsCommand : public BuiltPathsCommand
|
|||||||
|
|
||||||
virtual void run(ref<Store> store, StorePaths && storePaths) = 0;
|
virtual void run(ref<Store> store, StorePaths && storePaths) = 0;
|
||||||
|
|
||||||
void run(ref<Store> store, BuiltPaths && paths) override;
|
void run(ref<Store> store, BuiltPaths && allPaths, BuiltPaths && rootPaths) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -301,11 +301,15 @@ struct MixProfile : virtual StoreCommand
|
|||||||
MixProfile();
|
MixProfile();
|
||||||
|
|
||||||
/* If 'profile' is set, make it point at 'storePath'. */
|
/* If 'profile' is set, make it point at 'storePath'. */
|
||||||
void updateProfile(const StorePath & storePath);
|
void updateProfile(
|
||||||
|
const StorePath & storePath,
|
||||||
|
ref<Store> store);
|
||||||
|
|
||||||
/* If 'profile' is set, make it point at the store path produced
|
/* If 'profile' is set, make it point at the store path produced
|
||||||
by 'buildables'. */
|
by 'buildables'. */
|
||||||
void updateProfile(const BuiltPaths & buildables);
|
void updateProfile(
|
||||||
|
const BuiltPaths & buildables,
|
||||||
|
ref<Store> store);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MixDefaultProfile : MixProfile
|
struct MixDefaultProfile : MixProfile
|
||||||
|
@ -161,7 +161,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
|
|||||||
BuiltPaths buildables2;
|
BuiltPaths buildables2;
|
||||||
for (auto & b : buildables)
|
for (auto & b : buildables)
|
||||||
buildables2.push_back(b.path);
|
buildables2.push_back(b.path);
|
||||||
updateProfile(buildables2);
|
updateProfile(buildables2, store);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand
|
struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand, MixProfile
|
||||||
{
|
{
|
||||||
CheckSigsFlag checkSigs = CheckSigs;
|
CheckSigsFlag checkSigs = CheckSigs;
|
||||||
|
|
||||||
@ -43,19 +43,21 @@ struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand
|
|||||||
|
|
||||||
Category category() override { return catSecondary; }
|
Category category() override { return catSecondary; }
|
||||||
|
|
||||||
void run(ref<Store> srcStore, BuiltPaths && paths) override
|
void run(ref<Store> srcStore, BuiltPaths && allPaths, BuiltPaths && rootPaths) override
|
||||||
{
|
{
|
||||||
auto dstStore = getDstStore();
|
auto dstStore = getDstStore();
|
||||||
|
|
||||||
RealisedPath::Set stuffToCopy;
|
RealisedPath::Set stuffToCopy;
|
||||||
|
|
||||||
for (auto & builtPath : paths) {
|
for (auto & builtPath : allPaths) {
|
||||||
auto theseRealisations = builtPath.toRealisedPaths(*srcStore);
|
auto theseRealisations = builtPath.toRealisedPaths(*srcStore);
|
||||||
stuffToCopy.insert(theseRealisations.begin(), theseRealisations.end());
|
stuffToCopy.insert(theseRealisations.begin(), theseRealisations.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
copyPaths(
|
copyPaths(
|
||||||
*srcStore, *dstStore, stuffToCopy, NoRepair, checkSigs, substitute);
|
*srcStore, *dstStore, stuffToCopy, NoRepair, checkSigs, substitute);
|
||||||
|
|
||||||
|
updateProfile(rootPaths, dstStore);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,6 +55,15 @@ R""(
|
|||||||
# nix copy --to /tmp/nix nixpkgs#hello --no-check-sigs
|
# nix copy --to /tmp/nix nixpkgs#hello --no-check-sigs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Update the NixOS system profile to point to a closure copied from a
|
||||||
|
remote machine:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix copy --from ssh://server \
|
||||||
|
--profile /nix/var/nix/profiles/system \
|
||||||
|
/nix/store/r14v3km89zm3prwsa521fab5kgzvfbw4-nixos-system-foobar-24.05.20240925.759537f
|
||||||
|
```
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
`nix copy` copies store path closures between two Nix stores. The
|
`nix copy` copies store path closures between two Nix stores. The
|
||||||
|
@ -502,7 +502,7 @@ struct Common : InstallableCommand, MixProfile
|
|||||||
|
|
||||||
auto strPath = store->printStorePath(shellOutPath);
|
auto strPath = store->printStorePath(shellOutPath);
|
||||||
|
|
||||||
updateProfile(shellOutPath);
|
updateProfile(shellOutPath, store);
|
||||||
|
|
||||||
debug("reading environment file '%s'", strPath);
|
debug("reading environment file '%s'", strPath);
|
||||||
|
|
||||||
|
@ -424,7 +424,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateProfile(manifest.build(store));
|
updateProfile(manifest.build(store), store);
|
||||||
} catch (BuildEnvFileConflictError & conflictError) {
|
} catch (BuildEnvFileConflictError & conflictError) {
|
||||||
// FIXME use C++20 std::ranges once macOS has it
|
// FIXME use C++20 std::ranges once macOS has it
|
||||||
// See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102
|
// See https://github.com/NixOS/nix/compare/3efa476c5439f8f6c1968a6ba20a31d1239c2f04..1fe5d172ece51a619e879c4b86f603d9495cc102
|
||||||
@ -669,7 +669,7 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem
|
|||||||
removedCount,
|
removedCount,
|
||||||
newManifest.elements.size());
|
newManifest.elements.size());
|
||||||
|
|
||||||
updateProfile(newManifest.build(store));
|
updateProfile(newManifest.build(store), store);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -779,7 +779,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
|||||||
builtPaths.find(&*installable)->second.first);
|
builtPaths.find(&*installable)->second.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProfile(manifest.build(store));
|
updateProfile(manifest.build(store), store);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
|
|||||||
|
|
||||||
Category category() override { return catSecondary; }
|
Category category() override { return catSecondary; }
|
||||||
|
|
||||||
void run(ref<Store> store, BuiltPaths && paths) override
|
void run(ref<Store> store, BuiltPaths && paths, BuiltPaths && rootPaths) override
|
||||||
{
|
{
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
RealisedPath::Set realisations;
|
RealisedPath::Set realisations;
|
||||||
|
@ -18,7 +18,9 @@ HASH=$(nix hash path $outPath)
|
|||||||
clearStore
|
clearStore
|
||||||
clearCacheCache
|
clearCacheCache
|
||||||
|
|
||||||
nix copy --from $cacheURI $outPath --no-check-sigs
|
nix copy --from $cacheURI $outPath --no-check-sigs --profile $TEST_ROOT/profile
|
||||||
|
|
||||||
|
[[ -e $TEST_ROOT/profile ]]
|
||||||
|
|
||||||
if ls $cacheDir/nar/*.zst &> /dev/null; then
|
if ls $cacheDir/nar/*.zst &> /dev/null; then
|
||||||
echo "files do exist"
|
echo "files do exist"
|
||||||
|
Loading…
Reference in New Issue
Block a user