Always fails to fetch non-flake file inputs
MathiasSven opened this issue · 1 comments
Consider this flake:
{
inputs.bar = {
url = "https://gist.githubusercontent.com/MathiasSven/44045168334c4601c4cae21d60d71491/raw/b6d47272b67d0d13bf495ba03b3a987bdf962986/gistfile1.txt";
flake = false;
};
outputs = { bar, ... }: { inherit bar; foo = import bar; };
}
Here the gist is just a placeholder for a file, any file would suffice for the example.
Every time you enter a flake.nix
with a non-flake file input such as this one, the LSP will ask you:
Some flake inputs are not available. Fetch them now?
You can enable autoArchive in lsp configuration.
(1)Fetch, (2)Ignore missing ones:
If you try to fetch it, it will give you this warning:
[coc.nvim] Failed to archiving flake: command succeeded but some paths are still missing: [("bar", Path("/nix/store/n1gjjsb5bwdy96gzp1fj78jhh2dxh5j1-source"))]
# Notice it says "/nix/store/n1gjjsb5bwdy96gzp1fj78jhh2dxh5j1-source"
And there is no way to just permanently ignore that input specifically, so the message/error will always appear.
Partial expiation of the issue, if anyone can fill in the gap I would appreciate it as I wasn't able to figure out why:
The problem here is that nil
uses a (derivation { ... ; outputHash = HASH }).outPath
in order to determine where to check for the input source path, however in this case this doesn't work, observe:
nix eval .#bar.narHash
"sha256-MrqkB8frUDtvEopnDXs8/t0nZxgqYtJkTZeAQ8B7RVM="
# This is the hash that is present in the flake.lock file, and the one nil fills inside the outputHash to get the path
nil eval .#bar.outPath
"/nix/store/dr9hbdf6mdbvbq83napciyfir2xk1m24-source"
# This is where the file is actually stored, not where nil thought it would be, ie: "/nix/store/n1gjjsb5bwdy96gzp1fj78jhh2dxh5j1-source" as shown above
There seems to be some indirection with how either the path or the hash get computed for a file input in flakes. Changing the strategy does not yield the correct path:
nix eval --expr '(derivation {
name = "source";
system = "dummy";
builder = "dummy";
outputHashMode = "recursive";
outputHashAlgo = null;
outputHash = "sha256-MrqkB8frUDtvEopnDXs8/t0nZxgqYtJkTZeAQ8B7RVM="; }).outPath'
# "/nix/store/n1gjjsb5bwdy96gzp1fj78jhh2dxh5j1-source"
# This is how nil gets to the incorrect path
nix eval --expr '(derivation {
name = "source";
system = "dummy";
builder = "dummy";
outputHashMode = "flat";
outputHashAlgo = null;
outputHash = "sha256-MrqkB8frUDtvEopnDXs8/t0nZxgqYtJkTZeAQ8B7RVM="; }).outPath'
# "/nix/store/lsxl020awachf1zjl1k0si4vzvnmhhkh-source"
# Using "flat" does not give the right path either
I think the main problem stems from this issue:
url="https://gist.githubusercontent.com/MathiasSven/44045168334c4601c4cae21d60d71491/raw/b6d47272b67d0d13bf495ba03b3a987bdf962986/gistfile1.txt"
curl $url > source.str
nix hash path source.str
# sha256-MrqkB8frUDtvEopnDXs8/t0nZxgqYtJkTZeAQ8B7RVM=
## The true hash that you see in flake.lock
nix hash file source.str
# sha256-KwOAjXZIRfei7ZuJhSao5Ui5rxJqHRYUQ7M8LJvxOF4=
nix eval --expr '(derivation {
name = "source";
system = "dummy";
builder = "dummy";
outputHashMode = "flat";
outputHashAlgo = null;
outputHash = "sha256-KwOAjXZIRfei7ZuJhSao5Ui5rxJqHRYUQ7M8LJvxOF4="; }).outPath'
# "/nix/store/dr9hbdf6mdbvbq83napciyfir2xk1m24-source"
The correct path! So the hash that the flake.lock
stores is the result of doing a nix hash file
on the content, but the location in which the file is stored is done from the hash you would get out of doing nix hash path
. nix-prefetch-url
also imitates this behaviour:
nix-prefetch-url $url --print-path --name source
# 0piqy6djqg5k8ca1c7ba2apvjj75m0k8b2cvxnigfia8fs6q00rb
# /nix/store/dr9hbdf6mdbvbq83napciyfir2xk1m24-source
nix hash to-sri --type sha256 0piqy6djqg5k8ca1c7ba2apvjj75m0k8b2cvxnigfia8fs6q00rb
# sha256-KwOAjXZIRfei7ZuJhSao5Ui5rxJqHRYUQ7M8LJvxOF4=
## The hash that leads to the correct path, but not the one shown in the flake.lock
Anyway, I have little experience with rust, and I am not sure if the .as_ref().unwrap()
bellow will result in exceptions in some cases, but this is the patch I have added to my installation of nil
, from the little testing I have done, there seems to be no issues so far.
diff --git a/crates/nix-interop/src/flake_lock.rs b/crates/nix-interop/src/flake_lock.rs
index ccadd4d..48e032f 100644
--- a/crates/nix-interop/src/flake_lock.rs
+++ b/crates/nix-interop/src/flake_lock.rs
@@ -38,7 +38,7 @@ pub async fn resolve_flake_locked_inputs(
// Ignore the root node which is unlocked. This happens in cycle flake inputs.
// TODO: Should come up a way to correctly handle it in database.
- inputs.retain(|&(_, node)| !std::ptr::eq(node, root_node));
+ inputs.retain(|&(_, node)| !std::ptr::eq(node, root_node) && !node.locked.as_ref().unwrap()._type.eq("file"));
let hashes = inputs
.iter()
@@ -184,6 +184,7 @@ enum FlakeInput {
#[serde(rename_all = "camelCase")]
struct LockedFlakeRef {
nar_hash: String,
+ _type: String,
// ...
}
So the hash that the
flake.lock
stores is the result of doing anix hash file
on the content, but the location in which the file is stored is done from the hash you would get out of doingnix hash path
.nix-prefetch-url
also imitates this behaviour:
This is unfortunately. So the store path is a plain content hash instead of a tree hash (nar hash). But since only narHash
is stored in flake.lock
, there's no way to recover the plain hash and store path before its existence. I don't think there's even a way for Nix itself to get the store path given only the flake.lock without a full database scan: the hash
(narHash) column of table ValidPath
is not even indexed. I'll check the source code of Nix for what's going on.