Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f68bed7f67d9
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 26f895a26d37
Choose a head ref
  • 1 commit
  • 7 files changed
  • 1 contributor

Commits on Jan 29, 2020

  1. Clean up the lock file handling flags

    Added a flag --no-update-lock-file to barf if the lock file needs any
    changes. This is useful for CI systems if you're building a
    checkout. Fixes #2947.
    
    Renamed --no-save-lock-file to --no-write-lock-file. It is now a fatal
    error if the lock file needs changes but --no-write-lock-file is not
    given.
    edolstra committed Jan 29, 2020
    Copy the full SHA
    26f895a View commit details
Showing with 78 additions and 75 deletions.
  1. +2 −2 flake.lock
  2. +21 −26 src/libexpr/flake/flake.cc
  3. +33 −15 src/libexpr/flake/flake.hh
  4. +0 −10 src/nix/command.hh
  5. +2 −2 src/nix/flake.cc
  6. +12 −18 src/nix/installables.cc
  7. +8 −2 tests/flakes.sh
4 changes: 2 additions & 2 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 21 additions & 26 deletions src/libexpr/flake/flake.cc
Original file line number Diff line number Diff line change
@@ -251,17 +251,6 @@ static std::pair<fetchers::Tree, FlakeRef> getNonFlake(
return std::make_pair(std::move(sourceInfo), resolvedRef);
}

bool allowedToUseRegistries(LockFileMode handle, bool isTopRef)
{
if (handle == AllPure) return false;
else if (handle == TopRefUsesRegistries) return isTopRef;
else if (handle == UpdateLockFile) return true;
else if (handle == UseUpdatedLockFile) return true;
else if (handle == RecreateLockFile) return true;
else if (handle == UseNewLockFile) return true;
else assert(false);
}

static void flattenLockFile(
const LockedInputs & inputs,
const InputPath & prefix,
@@ -311,20 +300,17 @@ static std::string diffLockFiles(const LockedInputs & oldLocks, const LockedInpu
LockedFlake lockFlake(
EvalState & state,
const FlakeRef & topRef,
LockFileMode lockFileMode,
const LockFlags & lockFlags)
{
settings.requireExperimentalFeature("flakes");

RefMap refMap;

auto flake = getFlake(state, topRef,
allowedToUseRegistries(lockFileMode, true), refMap);
auto flake = getFlake(state, topRef, lockFlags.useRegistries, refMap);

LockFile oldLockFile;

if (lockFileMode != RecreateLockFile && lockFileMode != UseNewLockFile) {
// If recreateLockFile, start with an empty lockfile
if (!lockFlags.recreateLockFile) {
// FIXME: symlink attack
oldLockFile = LockFile::read(
flake.sourceInfo->actualPath + "/" + flake.resolvedRef.subdir + "/flake.lock");
@@ -436,12 +422,13 @@ LockedFlake lockFlake(
} else {
/* We need to update/create a new lock file
entry. So fetch the flake/non-flake. */
if (lockFileMode == AllPure || lockFileMode == TopRefUsesRegistries)

if (!lockFlags.allowMutable && !input.ref.isImmutable())
throw Error("cannot update flake input '%s' in pure mode", inputPathS);

if (input.isFlake) {
auto inputFlake = getFlake(state, input.ref,
allowedToUseRegistries(lockFileMode, false), refMap);
lockFlags.useRegistries, refMap);

newLocks.inputs.insert_or_assign(id,
LockedInput(inputFlake.resolvedRef, inputFlake.originalRef, inputFlake.sourceInfo->narHash));
@@ -461,7 +448,7 @@ LockedFlake lockFlake(

else {
auto [sourceInfo, resolvedRef] = getNonFlake(state, input.ref,
allowedToUseRegistries(lockFileMode, false), refMap);
lockFlags.useRegistries, refMap);
newLocks.inputs.insert_or_assign(id,
LockedInput(resolvedRef, input.ref, sourceInfo.narHash));
}
@@ -494,12 +481,15 @@ LockedFlake lockFlake(
if (!(oldLockFile == LockFile()))
printInfo("inputs of flake '%s' changed:\n%s", topRef, chomp(diffLockFiles(oldLockFile, newLockFile)));

if (lockFileMode == UpdateLockFile || lockFileMode == RecreateLockFile) {
if (lockFlags.writeLockFile) {
if (auto sourcePath = topRef.input->getSourcePath()) {
if (!newLockFile.isImmutable()) {
if (settings.warnDirty)
warn("will not write lock file of flake '%s' because it has a mutable input", topRef);
} else {
if (!lockFlags.updateLockFile)
throw Error("flake '%s' requires lock file changes but they're not allowed due to '--no-update-lock-file'", topRef);

auto path = *sourcePath + (topRef.subdir == "" ? "" : "/" + topRef.subdir) + "/flake.lock";

if (pathExists(path))
@@ -522,9 +512,9 @@ LockedFlake lockFlake(
#endif
}
} else
warn("cannot write lock file of remote flake '%s'", topRef);
} else if (lockFileMode != AllPure && lockFileMode != TopRefUsesRegistries)
warn("using updated lock file without writing it to file");
throw Error("cannot write modified lock file of flake '%s' (use '--no-write-lock-file' to ignore)", topRef);
} else
warn("not writing modified lock file of flake '%s'", topRef);
}

return LockedFlake { .flake = std::move(flake), .lockFile = std::move(newLockFile) };
@@ -655,9 +645,14 @@ void callFlake(EvalState & state,
// This function is exposed to be used in nix files.
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)),
evalSettings.pureEval ? AllPure : UseUpdatedLockFile, lockFlags), v);
callFlake(state,
lockFlake(state, parseFlakeRef(state.forceStringNoCtx(*args[0], pos)),
LockFlags {
.updateLockFile = false,
.useRegistries = !evalSettings.pureEval,
.allowMutable = !evalSettings.pureEval,
}),
v);
}

static RegisterPrimOp r2("getFlake", 1, prim_getFlake);
48 changes: 33 additions & 15 deletions src/libexpr/flake/flake.hh
Original file line number Diff line number Diff line change
@@ -13,15 +13,6 @@ namespace fetchers { struct Tree; }

namespace flake {

enum LockFileMode : unsigned int
{ AllPure // Everything is handled 100% purely
, TopRefUsesRegistries // The top FlakeRef uses the registries, apart from that, everything happens 100% purely
, UpdateLockFile // Update the existing lockfile and write it to file
, UseUpdatedLockFile // `UpdateLockFile` without writing to file
, RecreateLockFile // Recreate the lockfile from scratch and write it to file
, UseNewLockFile // `RecreateLockFile` without writing to file
};

struct FlakeInput;

typedef std::map<FlakeId, FlakeInput> FlakeInputs;
@@ -61,21 +52,48 @@ struct LockedFlake

struct LockFlags
{
/* Whether to ignore the existing lock file, creating a new one
from scratch. */
bool recreateLockFile = false;

/* Whether to update the lock file at all. If set to false, if any
change to the lock file is needed (e.g. when an input has been
added to flake.nix), you get a fatal error. */
bool updateLockFile = true;

/* Whether to write the lock file to disk. If set to true, if the
any changes to the lock file are needed and the flake is not
writable (i.e. is not a local Git working tree or similar), you
get a fatal error. If set to false, Nix will use the modified
lock file in memory only, without writing it to disk. */
bool writeLockFile = true;

/* Whether to use the registries to lookup indirect flake
references like 'nixpkgs'. */
bool useRegistries = true;

/* Whether mutable flake references (i.e. those without a Git
revision or similar) without a corresponding lock are
allowed. Mutable flake references with a lock are always
allowed. */
bool allowMutable = true;

std::map<InputPath, FlakeRef> inputOverrides;
};

LockedFlake lockFlake(
EvalState &,
const FlakeRef &,
LockFileMode,
const LockFlags &);
EvalState & state,
const FlakeRef & flakeRef,
const LockFlags & lockFlags);

void callFlake(EvalState & state,
void callFlake(
EvalState & state,
const Flake & flake,
const LockedInputs & inputs,
Value & v);

void callFlake(EvalState & state,
void callFlake(
EvalState & state,
const LockedFlake & resFlake,
Value & v);

10 changes: 0 additions & 10 deletions src/nix/command.hh
Original file line number Diff line number Diff line change
@@ -16,10 +16,6 @@ class EvalState;
struct Pos;
class Store;

namespace flake {
enum LockFileMode : unsigned int;
}

/* A command that requires a Nix store. */
struct StoreCommand : virtual Command
{
@@ -42,15 +38,9 @@ struct EvalCommand : virtual StoreCommand, MixEvalArgs

struct MixFlakeOptions : virtual Args
{
bool recreateLockFile = false;
bool saveLockFile = true;
bool useRegistries = true;

flake::LockFlags lockFlags;

MixFlakeOptions();

flake::LockFileMode getLockFileMode();
};

struct SourceExprCommand : virtual Args, EvalCommand, MixFlakeOptions
4 changes: 2 additions & 2 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
@@ -38,12 +38,12 @@ class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions
Flake getFlake()
{
auto evalState = getEvalState();
return flake::getFlake(*evalState, getFlakeRef(), useRegistries);
return flake::getFlake(*evalState, getFlakeRef(), lockFlags.useRegistries);
}

LockedFlake lockFlake()
{
return flake::lockFlake(*getEvalState(), getFlakeRef(), getLockFileMode(), lockFlags);
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
}
};

30 changes: 12 additions & 18 deletions src/nix/installables.cc
Original file line number Diff line number Diff line change
@@ -22,17 +22,22 @@ MixFlakeOptions::MixFlakeOptions()
mkFlag()
.longName("recreate-lock-file")
.description("recreate lock file from scratch")
.set(&recreateLockFile, true);
.set(&lockFlags.recreateLockFile, true);

mkFlag()
.longName("no-save-lock-file")
.description("do not save the newly generated lock file")
.set(&saveLockFile, false);
.longName("no-update-lock-file")
.description("do not allow any updates to the lock file")
.set(&lockFlags.updateLockFile, false);

mkFlag()
.longName("no-write-lock-file")
.description("do not write the newly generated lock file")
.set(&lockFlags.writeLockFile, false);

mkFlag()
.longName("no-registries")
.description("don't use flake registries")
.set(&useRegistries, false);
.set(&lockFlags.useRegistries, false);

mkFlag()
.longName("override-input")
@@ -46,17 +51,6 @@ MixFlakeOptions::MixFlakeOptions()
});
}

flake::LockFileMode MixFlakeOptions::getLockFileMode()
{
using namespace flake;
return
useRegistries
? recreateLockFile
? (saveLockFile ? RecreateLockFile : UseNewLockFile)
: (saveLockFile ? UpdateLockFile : UseUpdatedLockFile)
: AllPure;
}

SourceExprCommand::SourceExprCommand()
{
mkFlag()
@@ -332,7 +326,7 @@ std::tuple<std::string, FlakeRef, flake::EvalCache::Derivation> InstallableFlake
{
auto state = cmd.getEvalState();

auto lockedFlake = lockFlake(*state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags);
auto lockedFlake = lockFlake(*state, flakeRef, cmd.lockFlags);

Value * vOutputs = nullptr;

@@ -386,7 +380,7 @@ std::vector<flake::EvalCache::Derivation> InstallableFlake::toDerivations()

Value * InstallableFlake::toValue(EvalState & state)
{
auto lockedFlake = lockFlake(state, flakeRef, cmd.getLockFileMode(), cmd.lockFlags);
auto lockedFlake = lockFlake(state, flakeRef, cmd.lockFlags);

auto vOutputs = getFlakeOutputs(state, lockedFlake);

10 changes: 8 additions & 2 deletions tests/flakes.sh
Original file line number Diff line number Diff line change
@@ -156,9 +156,15 @@ nix path-info $flake1Dir/result
(! nix eval --expr "builtins.getFlake \"$flake2Dir\"")

# But should succeed in impure mode.
nix build -o $TEST_ROOT/result flake2#bar --impure
(! nix build -o $TEST_ROOT/result flake2#bar --impure)
nix build -o $TEST_ROOT/result flake2#bar --impure --no-write-lock-file

# Test automatic lock file generation.
# Building a local flake with an unlocked dependency should fail with --no-update-lock-file.
nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes'

# But it should succeed without that flag.
nix build -o $TEST_ROOT/result $flake2Dir#bar --no-write-lock-file
nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes'
nix build -o $TEST_ROOT/result $flake2Dir#bar
[[ -e $flake2Dir/flake.lock ]]
git -C $flake2Dir add flake.lock