forked from lix-project/lix
Add flag --override-input to override specific lock file entries
E.g. $ nix flake update ~/Misc/eelco-configurations/hagbard \ --override-input 'dwarffs/nixpkgs' ../my-nixpkgs overrides the 'nixpkgs' input of the 'dwarffs' input of the top-level flake. Fixes #2837.
This commit is contained in:
parent
e53c89a643
commit
f68bed7f67
|
@ -4,7 +4,6 @@
|
||||||
#include "eval-inline.hh"
|
#include "eval-inline.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
#include "fetchers/fetchers.hh"
|
#include "fetchers/fetchers.hh"
|
||||||
#include "fetchers/regex.hh"
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
@ -62,22 +61,6 @@ static void expectType(EvalState & state, ValueType type,
|
||||||
showType(type), showType(value.type), pos);
|
showType(type), showType(value.type), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
static InputPath parseInputPath(std::string_view s, const Pos & pos)
|
|
||||||
{
|
|
||||||
InputPath path;
|
|
||||||
|
|
||||||
for (auto & elem : tokenizeString<std::vector<std::string>>(s, "/")) {
|
|
||||||
if (!std::regex_match(elem, fetchers::flakeIdRegex))
|
|
||||||
throw Error("invalid flake input path element '%s' at %s", elem, pos);
|
|
||||||
path.push_back(elem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.empty())
|
|
||||||
throw Error("flake input path is empty at %s", pos);
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
static std::map<FlakeId, FlakeInput> parseFlakeInputs(
|
||||||
EvalState & state, Value * value, const Pos & pos);
|
EvalState & state, Value * value, const Pos & pos);
|
||||||
|
|
||||||
|
@ -107,7 +90,11 @@ static FlakeInput parseFlakeInput(EvalState & state,
|
||||||
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
|
input.overrides = parseFlakeInputs(state, attr.value, *attr.pos);
|
||||||
} else if (attr.name == sFollows) {
|
} else if (attr.name == sFollows) {
|
||||||
expectType(state, tString, *attr.value, *attr.pos);
|
expectType(state, tString, *attr.value, *attr.pos);
|
||||||
input.follows = parseInputPath(attr.value->string.s, *attr.pos);
|
try {
|
||||||
|
input.follows = parseInputPath(attr.value->string.s);
|
||||||
|
} catch (Error & e) {
|
||||||
|
e.addPrefix("in flake attribute at '%s':\n");
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
|
throw Error("flake input '%s' has an unsupported attribute '%s', at %s",
|
||||||
inputName, attr.name, *attr.pos);
|
inputName, attr.name, *attr.pos);
|
||||||
|
@ -324,7 +311,8 @@ static std::string diffLockFiles(const LockedInputs & oldLocks, const LockedInpu
|
||||||
LockedFlake lockFlake(
|
LockedFlake lockFlake(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const FlakeRef & topRef,
|
const FlakeRef & topRef,
|
||||||
LockFileMode lockFileMode)
|
LockFileMode lockFileMode,
|
||||||
|
const LockFlags & lockFlags)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature("flakes");
|
settings.requireExperimentalFeature("flakes");
|
||||||
|
|
||||||
|
@ -350,6 +338,9 @@ LockedFlake lockFlake(
|
||||||
// FIXME: check whether all overrides are used.
|
// FIXME: check whether all overrides are used.
|
||||||
std::map<InputPath, FlakeInput> overrides;
|
std::map<InputPath, FlakeInput> overrides;
|
||||||
|
|
||||||
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
|
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
|
||||||
|
|
||||||
/* Compute the new lock file. This is dones as a fixpoint
|
/* Compute the new lock file. This is dones as a fixpoint
|
||||||
iteration: we repeat until the new lock file no longer changes
|
iteration: we repeat until the new lock file no longer changes
|
||||||
and there are no unresolved "follows" inputs. */
|
and there are no unresolved "follows" inputs. */
|
||||||
|
@ -664,8 +655,9 @@ void callFlake(EvalState & state,
|
||||||
// This function is exposed to be used in nix files.
|
// This function is exposed to be used in nix files.
|
||||||
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
static void prim_getFlake(EvalState & state, const Pos & pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
|
LockFlags lockFlags;
|
||||||
callFlake(state, lockFlake(state, parseFlakeRef(state.forceStringNoCtx(*args[0], pos)),
|
callFlake(state, lockFlake(state, parseFlakeRef(state.forceStringNoCtx(*args[0], pos)),
|
||||||
evalSettings.pureEval ? AllPure : UseUpdatedLockFile), v);
|
evalSettings.pureEval ? AllPure : UseUpdatedLockFile, lockFlags), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
|
static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
|
||||||
|
|
|
@ -59,7 +59,16 @@ struct LockedFlake
|
||||||
Fingerprint getFingerprint() const;
|
Fingerprint getFingerprint() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
LockedFlake lockFlake(EvalState &, const FlakeRef &, LockFileMode);
|
struct LockFlags
|
||||||
|
{
|
||||||
|
std::map<InputPath, FlakeRef> inputOverrides;
|
||||||
|
};
|
||||||
|
|
||||||
|
LockedFlake lockFlake(
|
||||||
|
EvalState &,
|
||||||
|
const FlakeRef &,
|
||||||
|
LockFileMode,
|
||||||
|
const LockFlags &);
|
||||||
|
|
||||||
void callFlake(EvalState & state,
|
void callFlake(EvalState & state,
|
||||||
const Flake & flake,
|
const Flake & flake,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "lockfile.hh"
|
#include "lockfile.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "fetchers/regex.hh"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
@ -104,4 +105,20 @@ void LockFile::write(const Path & path) const
|
||||||
writeFile(path, fmt("%s\n", *this));
|
writeFile(path, fmt("%s\n", *this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputPath parseInputPath(std::string_view s)
|
||||||
|
{
|
||||||
|
InputPath path;
|
||||||
|
|
||||||
|
for (auto & elem : tokenizeString<std::vector<std::string>>(s, "/")) {
|
||||||
|
if (!std::regex_match(elem, fetchers::flakeIdRegex))
|
||||||
|
throw Error("invalid flake input path element '%s'", elem);
|
||||||
|
path.push_back(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
throw Error("flake input path is empty");
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,5 +81,7 @@ struct LockFile : LockedInputs
|
||||||
|
|
||||||
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile);
|
std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile);
|
||||||
|
|
||||||
|
InputPath parseInputPath(std::string_view s);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "args.hh"
|
#include "args.hh"
|
||||||
#include "common-eval-args.hh"
|
#include "common-eval-args.hh"
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
|
#include "flake/lockfile.hh"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -42,11 +43,11 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs
|
||||||
struct MixFlakeOptions : virtual Args
|
struct MixFlakeOptions : virtual Args
|
||||||
{
|
{
|
||||||
bool recreateLockFile = false;
|
bool recreateLockFile = false;
|
||||||
|
|
||||||
bool saveLockFile = true;
|
bool saveLockFile = true;
|
||||||
|
|
||||||
bool useRegistries = true;
|
bool useRegistries = true;
|
||||||
|
|
||||||
|
flake::LockFlags lockFlags;
|
||||||
|
|
||||||
MixFlakeOptions();
|
MixFlakeOptions();
|
||||||
|
|
||||||
flake::LockFileMode getLockFileMode();
|
flake::LockFileMode getLockFileMode();
|
||||||
|
|
|
@ -43,7 +43,7 @@ public:
|
||||||
|
|
||||||
LockedFlake lockFlake()
|
LockedFlake lockFlake()
|
||||||
{
|
{
|
||||||
return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode());
|
return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode(), lockFlags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,17 @@ MixFlakeOptions::MixFlakeOptions()
|
||||||
.longName("no-registries")
|
.longName("no-registries")
|
||||||
.description("don't use flake registries")
|
.description("don't use flake registries")
|
||||||
.set(&useRegistries, false);
|
.set(&useRegistries, false);
|
||||||
|
|
||||||
|
mkFlag()
|
||||||
|
.longName("override-input")
|
||||||
|
.description("override a specific flake input (e.g. 'dwarffs/nixpkgs')")
|
||||||
|
.arity(2)
|
||||||
|
.labels({"input-path", "flake-url"})
|
||||||
|
.handler([&](std::vector<std::string> ss) {
|
||||||
|
lockFlags.inputOverrides.insert_or_assign(
|
||||||
|
flake::parseInputPath(ss[0]),
|
||||||
|
parseFlakeRef(ss[1], absPath(".")));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
flake::LockFileMode MixFlakeOptions::getLockFileMode()
|
flake::LockFileMode MixFlakeOptions::getLockFileMode()
|
||||||
|
@ -321,7 +332,7 @@ std::tuple<std::string, FlakeRef, flake::EvalCache::Derivation> InstallableFlake
|
||||||
{
|
{
|
||||||
auto state = cmd.getEvalState();
|
auto state = cmd.getEvalState();
|
||||||
|
|
||||||
auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode());
|
auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags);
|
||||||
|
|
||||||
Value * vOutputs = nullptr;
|
Value * vOutputs = nullptr;
|
||||||
|
|
||||||
|
@ -375,7 +386,7 @@ std::vector<flake::EvalCache::Derivation> InstallableFlake::toDerivations()
|
||||||
|
|
||||||
Value * InstallableFlake::toValue(EvalState & state)
|
Value * InstallableFlake::toValue(EvalState & state)
|
||||||
{
|
{
|
||||||
auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode());
|
auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags);
|
||||||
|
|
||||||
auto vOutputs = getFlakeOutputs(state, lockedFlake);
|
auto vOutputs = getFlakeOutputs(state, lockedFlake);
|
||||||
|
|
||||||
|
|
|
@ -129,6 +129,12 @@ json=$(nix flake info flake1 --json | jq .)
|
||||||
[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]]
|
[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]]
|
||||||
[[ -d $(echo "$json" | jq -r .path) ]]
|
[[ -d $(echo "$json" | jq -r .path) ]]
|
||||||
[[ $(echo "$json" | jq -r .lastModified) = $(git -C $flake1Dir log -n1 --format=%ct) ]]
|
[[ $(echo "$json" | jq -r .lastModified) = $(git -C $flake1Dir log -n1 --format=%ct) ]]
|
||||||
|
hash1=$(echo "$json" | jq -r .revision)
|
||||||
|
|
||||||
|
echo -n '# foo' >> $flake1Dir/flake.nix
|
||||||
|
git -C $flake1Dir commit -a -m 'Foo'
|
||||||
|
hash2=$(nix flake info flake1 --json --refresh | jq -r .revision)
|
||||||
|
[[ $hash1 != $hash2 ]]
|
||||||
|
|
||||||
# Test 'nix build' on a flake.
|
# Test 'nix build' on a flake.
|
||||||
nix build -o $TEST_ROOT/result flake1#foo
|
nix build -o $TEST_ROOT/result flake1#foo
|
||||||
|
@ -587,3 +593,14 @@ nix build -o $TEST_ROOT/result $url
|
||||||
|
|
||||||
# Building with an incorrect SRI hash should fail.
|
# Building with an incorrect SRI hash should fail.
|
||||||
nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ2Zz4DNHViCUrp6gTS7EE4+RMqFQtUfWF2UNUtJKS0=" 2>&1 | grep 'NAR hash mismatch'
|
nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ2Zz4DNHViCUrp6gTS7EE4+RMqFQtUfWF2UNUtJKS0=" 2>&1 | grep 'NAR hash mismatch'
|
||||||
|
|
||||||
|
# Test --override-input.
|
||||||
|
git -C $flake3Dir reset --hard
|
||||||
|
nix flake update $flake3Dir --override-input flake2/flake1 flake5
|
||||||
|
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake5 ]]
|
||||||
|
|
||||||
|
nix flake update $flake3Dir --override-input flake2/flake1 flake1
|
||||||
|
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash2 ]]
|
||||||
|
|
||||||
|
nix flake update $flake3Dir --override-input flake2/flake1 flake1/master/$hash1
|
||||||
|
[[ $(jq .inputs.flake2.inputs.flake1.url $flake3Dir/flake.lock) =~ flake1.*rev=$hash1 ]]
|
||||||
|
|
Loading…
Reference in a new issue